Merge "Use app context to avoid memory leaks"
diff --git a/Android.mk b/Android.mk
index 4fea0d4..561d220 100644
--- a/Android.mk
+++ b/Android.mk
@@ -73,6 +73,7 @@
core/java/android/app/IAlarmListener.aidl \
core/java/android/app/IAlarmManager.aidl \
core/java/android/app/IAppTask.aidl \
+ core/java/android/app/IApplicationThread.aidl \
core/java/android/app/ITaskStackListener.aidl \
core/java/android/app/IBackupAgent.aidl \
core/java/android/app/IEphemeralResolver.aidl \
@@ -502,7 +503,10 @@
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVA_LIBRARIES := core-oj core-libart conscrypt okhttp core-junit bouncycastle ext
-LOCAL_STATIC_JAVA_LIBRARIES := framework-protos
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ framework-protos \
+ android.hardware.thermal@1.0-java-constants \
LOCAL_MODULE := framework
@@ -574,6 +578,7 @@
frameworks/base/core/java/android/accounts/AuthenticatorDescription.aidl \
frameworks/base/core/java/android/accounts/Account.aidl \
frameworks/base/core/java/android/app/admin/SystemUpdatePolicy.aidl \
+ frameworks/base/core/java/android/app/admin/PasswordMetrics.aidl \
frameworks/base/core/java/android/print/PrintDocumentInfo.aidl \
frameworks/base/core/java/android/print/PageRange.aidl \
frameworks/base/core/java/android/print/PrintAttributes.aidl \
@@ -597,6 +602,7 @@
frameworks/base/core/java/android/os/WorkSource.aidl \
frameworks/base/core/java/android/os/DropBoxManager.aidl \
frameworks/base/core/java/android/os/Bundle.aidl \
+ frameworks/base/core/java/android/os/Debug.aidl \
frameworks/base/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl \
frameworks/base/core/java/android/net/Network.aidl \
frameworks/base/core/java/android/net/RouteInfo.aidl \
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index f5bd945..c28db57 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,7 +1,16 @@
[Hook Scripts]
checkstyle_hook = ../../development/tools/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
-fw core/java/android/animation/
+ core/java/android/hardware/usb/
+ core/java/android/print/
+ core/java/android/printservice/
core/java/android/text/
- core/java/android/view/
core/java/android/transition/
+ core/java/android/view/
core/java/android/widget/
+ core/tests/coretests/src/android/print/
+ packages/PrintRecommendationService/
+ packages/PrintSpooler/
+ services/print/
+ services/usb/
+
diff --git a/apct-tests/perftests/core/src/android/os/ParcelArrayPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelArrayPerfTest.java
new file mode 100644
index 0000000..a67aeca
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/ParcelArrayPerfTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class ParcelArrayPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameters(name = "size={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { {1}, {10}, {100}, {1000} });
+ }
+
+ private final int mSize;
+
+ private Parcel mWriteParcel;
+
+ private byte[] mByteArray;
+ private int[] mIntArray;
+ private long[] mLongArray;
+
+ private Parcel mByteParcel;
+ private Parcel mIntParcel;
+ private Parcel mLongParcel;
+
+ public ParcelArrayPerfTest(int size) {
+ mSize = size;
+ }
+
+ @Before
+ public void setUp() {
+ mWriteParcel = Parcel.obtain();
+
+ mByteArray = new byte[mSize];
+ mIntArray = new int[mSize];
+ mLongArray = new long[mSize];
+
+ mByteParcel = Parcel.obtain();
+ mByteParcel.writeByteArray(mByteArray);
+ mIntParcel = Parcel.obtain();
+ mIntParcel.writeIntArray(mIntArray);
+ mLongParcel = Parcel.obtain();
+ mLongParcel.writeLongArray(mLongArray);
+ }
+
+ @After
+ public void tearDown() {
+ mWriteParcel.recycle();
+ mWriteParcel = null;
+ }
+
+ @Test
+ public void timeWriteByteArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mWriteParcel.setDataPosition(0);
+ mWriteParcel.writeByteArray(mByteArray);
+ }
+ }
+
+ @Test
+ public void timeCreateByteArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mByteParcel.setDataPosition(0);
+ mByteParcel.createByteArray();
+ }
+ }
+
+ @Test
+ public void timeReadByteArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mByteParcel.setDataPosition(0);
+ mByteParcel.readByteArray(mByteArray);
+ }
+ }
+
+ @Test
+ public void timeWriteIntArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mWriteParcel.setDataPosition(0);
+ mWriteParcel.writeIntArray(mIntArray);
+ }
+ }
+
+ @Test
+ public void timeCreateIntArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mIntParcel.setDataPosition(0);
+ mIntParcel.createIntArray();
+ }
+ }
+
+ @Test
+ public void timeReadIntArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mIntParcel.setDataPosition(0);
+ mIntParcel.readIntArray(mIntArray);
+ }
+ }
+
+ @Test
+ public void timeWriteLongArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mWriteParcel.setDataPosition(0);
+ mWriteParcel.writeLongArray(mLongArray);
+ }
+ }
+
+ @Test
+ public void timeCreateLongArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mLongParcel.setDataPosition(0);
+ mLongParcel.createLongArray();
+ }
+ }
+
+ @Test
+ public void timeReadLongArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mLongParcel.setDataPosition(0);
+ mLongParcel.readLongArray(mLongArray);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
new file mode 100644
index 0000000..8cd45f7
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ParcelPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private Parcel mParcel;
+
+ @Before
+ public void setUp() {
+ mParcel = Parcel.obtain();
+ mParcel.setDataPosition(0);
+ mParcel.setDataCapacity(8);
+ }
+
+ @After
+ public void tearDown() {
+ mParcel.recycle();
+ mParcel = null;
+ }
+
+ @Test
+ public void timeSetDataPosition() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mParcel.setDataPosition(0);
+ }
+ }
+
+ @Test
+ public void timeWriteByte() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final byte val = 0xF;
+ while (state.keepRunning()) {
+ mParcel.setDataPosition(0);
+ mParcel.writeByte(val);
+ }
+ }
+
+ @Test
+ public void timeReadByte() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mParcel.setDataPosition(0);
+ mParcel.readByte();
+ }
+ }
+
+ @Test
+ public void timeWriteInt() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final int val = 0xF;
+ while (state.keepRunning()) {
+ mParcel.setDataPosition(0);
+ mParcel.writeInt(val);
+ }
+ }
+
+ @Test
+ public void timeReadInt() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mParcel.setDataPosition(0);
+ mParcel.readInt();
+ }
+ }
+
+ @Test
+ public void timeWriteLong() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final long val = 0xF;
+ while (state.keepRunning()) {
+ mParcel.setDataPosition(0);
+ mParcel.writeLong(val);
+ }
+ }
+
+ @Test
+ public void timeReadLong() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mParcel.setDataPosition(0);
+ mParcel.readLong();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/java/lang/perftests/SystemPerfTest.java b/apct-tests/perftests/core/src/java/lang/perftests/SystemPerfTest.java
index 80ce22e..1e9c49c 100644
--- a/apct-tests/perftests/core/src/java/lang/perftests/SystemPerfTest.java
+++ b/apct-tests/perftests/core/src/java/lang/perftests/SystemPerfTest.java
@@ -24,6 +24,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SystemPerfTest {
@@ -43,4 +45,19 @@
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {}
}
+
+ void spinBlock(long durationNs) {
+ long start = System.nanoTime();
+ while (System.nanoTime() - start < durationNs) {}
+ }
+
+ @Test
+ public void testBenchmarkPauseResumeOverhead() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ spinBlock(TimeUnit.MICROSECONDS.toNanos(5));
+ state.resumeTiming();
+ }
+ }
}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index bf04f6d..519d524 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -43,13 +43,13 @@
* }
*/
public final class BenchmarkState {
+
private static final String TAG = "BenchmarkState";
private static final int NOT_STARTED = 0; // The benchmark has not started yet.
private static final int WARMUP = 1; // The benchmark is warming up.
private static final int RUNNING = 2; // The benchmark is running.
- private static final int RUNNING_PAUSED = 3; // The benchmark is temporary paused.
- private static final int FINISHED = 4; // The benchmark has stopped.
+ private static final int FINISHED = 3; // The benchmark has stopped.
private int mState = NOT_STARTED; // Current benchmark state.
@@ -63,6 +63,7 @@
private static final int REPEAT_COUNT = 5;
private long mStartTimeNs = 0; // Previously captured System.nanoTime().
+ private boolean mPaused;
private long mPausedTimeNs = 0; // The System.nanoTime() when the pauseTiming() is called.
private long mPausedDurationNs = 0; // The duration of paused state in nano sec.
@@ -118,24 +119,24 @@
// Stops the benchmark timer.
// This method can be called only when the timer is running.
public void pauseTiming() {
- if (mState == RUNNING_PAUSED) {
+ if (mPaused) {
throw new IllegalStateException(
"Unable to pause the benchmark. The benchmark has already paused.");
}
mPausedTimeNs = System.nanoTime();
- mState = RUNNING_PAUSED;
+ mPaused = true;
}
// Starts the benchmark timer.
// This method can be called only when the timer is stopped.
public void resumeTiming() {
- if (mState == RUNNING) {
+ if (!mPaused) {
throw new IllegalStateException(
"Unable to resume the benchmark. The benchmark is already running.");
}
mPausedDurationNs += System.nanoTime() - mPausedTimeNs;
mPausedTimeNs = 0;
- mState = RUNNING;
+ mPaused = false;
}
private void beginWarmup() {
@@ -194,11 +195,12 @@
if (mIteration >= mMaxIterations) {
return startNextTestRun();
}
+ if (mPaused) {
+ throw new IllegalStateException(
+ "Benchmark step finished with paused state. " +
+ "Resume the benchmark before finishing each step.");
+ }
return true;
- case RUNNING_PAUSED:
- throw new IllegalStateException(
- "Benchmark step finished with paused state. " +
- "Resume the benchmark before finishing each step.");
case FINISHED:
throw new IllegalStateException("The benchmark has finished.");
default:
diff --git a/api/current.txt b/api/current.txt
index 225297a..4c98263 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4489,6 +4489,7 @@
method public void onTrimMemory(int);
method public void onViewCreated(android.view.View, android.os.Bundle);
method public void onViewStateRestored(android.os.Bundle);
+ method public void postponeEnterTransition();
method public void registerForContextMenu(android.view.View);
method public final void requestPermissions(java.lang.String[], int);
method public void setAllowEnterTransitionOverlap(boolean);
@@ -4514,6 +4515,7 @@
method public void startActivityForResult(android.content.Intent, int);
method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+ method public void startPostponedEnterTransition();
method public void unregisterForContextMenu(android.view.View);
}
@@ -4671,6 +4673,7 @@
method public abstract android.app.FragmentTransaction remove(android.app.Fragment);
method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment);
method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment, java.lang.String);
+ method public abstract android.app.FragmentTransaction setAllowOptimization(boolean);
method public abstract android.app.FragmentTransaction setBreadCrumbShortTitle(int);
method public abstract android.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence);
method public abstract android.app.FragmentTransaction setBreadCrumbTitle(int);
@@ -13899,6 +13902,7 @@
public static abstract class CameraCaptureSession.StateCallback {
ctor public CameraCaptureSession.StateCallback();
method public void onActive(android.hardware.camera2.CameraCaptureSession);
+ method public void onCaptureQueueEmpty(android.hardware.camera2.CameraCaptureSession);
method public void onClosed(android.hardware.camera2.CameraCaptureSession);
method public abstract void onConfigureFailed(android.hardware.camera2.CameraCaptureSession);
method public abstract void onConfigured(android.hardware.camera2.CameraCaptureSession);
@@ -14777,6 +14781,7 @@
method public java.lang.String getSerial();
method public boolean releaseInterface(android.hardware.usb.UsbInterface);
method public android.hardware.usb.UsbRequest requestWait();
+ method public android.hardware.usb.UsbRequest requestWait(int);
method public boolean setConfiguration(android.hardware.usb.UsbConfiguration);
method public boolean setInterface(android.hardware.usb.UsbInterface);
}
@@ -30463,9 +30468,11 @@
method protected void onDisconnected();
method protected abstract void onPrintJobQueued(android.printservice.PrintJob);
method protected abstract void onRequestCancelPrintJob(android.printservice.PrintJob);
+ field public static final java.lang.String EXTRA_CAN_SELECT_PRINTER = "android.printservice.extra.CAN_SELECT_PRINTER";
field public static final java.lang.String EXTRA_PRINTER_INFO = "android.intent.extra.print.EXTRA_PRINTER_INFO";
field public static final java.lang.String EXTRA_PRINT_DOCUMENT_INFO = "android.printservice.extra.PRINT_DOCUMENT_INFO";
field public static final java.lang.String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO";
+ field public static final java.lang.String EXTRA_SELECT_PRINTER = "android.printservice.extra.SELECT_PRINTER";
field public static final java.lang.String SERVICE_INTERFACE = "android.printservice.PrintService";
field public static final java.lang.String SERVICE_META_DATA = "android.printservice";
}
@@ -36980,6 +36987,7 @@
field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+ field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
@@ -44021,6 +44029,7 @@
field public static final int ROTATION_ANIMATION_CROSSFADE = 1; // 0x1
field public static final int ROTATION_ANIMATION_JUMPCUT = 2; // 0x2
field public static final int ROTATION_ANIMATION_ROTATE = 0; // 0x0
+ field public static final int ROTATION_ANIMATION_SEAMLESS = 3; // 0x3
field public static final int SCREEN_BRIGHTNESS_CHANGED = 2048; // 0x800
field public static final int SCREEN_ORIENTATION_CHANGED = 1024; // 0x400
field public static final int SOFT_INPUT_ADJUST_NOTHING = 48; // 0x30
diff --git a/api/system-current.txt b/api/system-current.txt
index de1bbd3..ce115d8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -103,7 +103,6 @@
field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
- field public static final java.lang.String GET_PACKAGE_IMPORTANCE = "android.permission.GET_PACKAGE_IMPORTANCE";
field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
field public static final java.lang.String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
@@ -4634,6 +4633,7 @@
method public void onTrimMemory(int);
method public void onViewCreated(android.view.View, android.os.Bundle);
method public void onViewStateRestored(android.os.Bundle);
+ method public void postponeEnterTransition();
method public void registerForContextMenu(android.view.View);
method public final void requestPermissions(java.lang.String[], int);
method public void setAllowEnterTransitionOverlap(boolean);
@@ -4659,6 +4659,7 @@
method public void startActivityForResult(android.content.Intent, int);
method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+ method public void startPostponedEnterTransition();
method public void unregisterForContextMenu(android.view.View);
}
@@ -4816,6 +4817,7 @@
method public abstract android.app.FragmentTransaction remove(android.app.Fragment);
method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment);
method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment, java.lang.String);
+ method public abstract android.app.FragmentTransaction setAllowOptimization(boolean);
method public abstract android.app.FragmentTransaction setBreadCrumbShortTitle(int);
method public abstract android.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence);
method public abstract android.app.FragmentTransaction setBreadCrumbTitle(int);
@@ -14355,6 +14357,7 @@
public static abstract class CameraCaptureSession.StateCallback {
ctor public CameraCaptureSession.StateCallback();
method public void onActive(android.hardware.camera2.CameraCaptureSession);
+ method public void onCaptureQueueEmpty(android.hardware.camera2.CameraCaptureSession);
method public void onClosed(android.hardware.camera2.CameraCaptureSession);
method public abstract void onConfigureFailed(android.hardware.camera2.CameraCaptureSession);
method public abstract void onConfigured(android.hardware.camera2.CameraCaptureSession);
@@ -15987,6 +15990,7 @@
method public java.lang.String getSerial();
method public boolean releaseInterface(android.hardware.usb.UsbInterface);
method public android.hardware.usb.UsbRequest requestWait();
+ method public android.hardware.usb.UsbRequest requestWait(int);
method public boolean resetDevice();
method public boolean setConfiguration(android.hardware.usb.UsbConfiguration);
method public boolean setInterface(android.hardware.usb.UsbInterface);
@@ -33063,9 +33067,11 @@
method protected void onDisconnected();
method protected abstract void onPrintJobQueued(android.printservice.PrintJob);
method protected abstract void onRequestCancelPrintJob(android.printservice.PrintJob);
+ field public static final java.lang.String EXTRA_CAN_SELECT_PRINTER = "android.printservice.extra.CAN_SELECT_PRINTER";
field public static final java.lang.String EXTRA_PRINTER_INFO = "android.intent.extra.print.EXTRA_PRINTER_INFO";
field public static final java.lang.String EXTRA_PRINT_DOCUMENT_INFO = "android.printservice.extra.PRINT_DOCUMENT_INFO";
field public static final java.lang.String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO";
+ field public static final java.lang.String EXTRA_SELECT_PRINTER = "android.printservice.extra.SELECT_PRINTER";
field public static final java.lang.String SERVICE_INTERFACE = "android.printservice.PrintService";
field public static final java.lang.String SERVICE_META_DATA = "android.printservice";
}
@@ -37554,8 +37560,6 @@
method public int getUser();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
- field public static final java.lang.String GROUP_KEY_OVERRIDE_KEY = "group_key_override";
- field public static final java.lang.String NEEDS_AUTOGROUPING_KEY = "autogroup_needed";
}
public final class Condition implements android.os.Parcelable {
@@ -40072,6 +40076,7 @@
field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+ field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
@@ -47205,6 +47210,7 @@
field public static final int ROTATION_ANIMATION_CROSSFADE = 1; // 0x1
field public static final int ROTATION_ANIMATION_JUMPCUT = 2; // 0x2
field public static final int ROTATION_ANIMATION_ROTATE = 0; // 0x0
+ field public static final int ROTATION_ANIMATION_SEAMLESS = 3; // 0x3
field public static final int SCREEN_BRIGHTNESS_CHANGED = 2048; // 0x800
field public static final int SCREEN_ORIENTATION_CHANGED = 1024; // 0x400
field public static final int SOFT_INPUT_ADJUST_NOTHING = 48; // 0x30
diff --git a/api/test-current.txt b/api/test-current.txt
index 2d0fa49..92e5776 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4492,6 +4492,7 @@
method public void onTrimMemory(int);
method public void onViewCreated(android.view.View, android.os.Bundle);
method public void onViewStateRestored(android.os.Bundle);
+ method public void postponeEnterTransition();
method public void registerForContextMenu(android.view.View);
method public final void requestPermissions(java.lang.String[], int);
method public void setAllowEnterTransitionOverlap(boolean);
@@ -4517,6 +4518,7 @@
method public void startActivityForResult(android.content.Intent, int);
method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+ method public void startPostponedEnterTransition();
method public void unregisterForContextMenu(android.view.View);
}
@@ -4674,6 +4676,7 @@
method public abstract android.app.FragmentTransaction remove(android.app.Fragment);
method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment);
method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment, java.lang.String);
+ method public abstract android.app.FragmentTransaction setAllowOptimization(boolean);
method public abstract android.app.FragmentTransaction setBreadCrumbShortTitle(int);
method public abstract android.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence);
method public abstract android.app.FragmentTransaction setBreadCrumbTitle(int);
@@ -13916,6 +13919,7 @@
public static abstract class CameraCaptureSession.StateCallback {
ctor public CameraCaptureSession.StateCallback();
method public void onActive(android.hardware.camera2.CameraCaptureSession);
+ method public void onCaptureQueueEmpty(android.hardware.camera2.CameraCaptureSession);
method public void onClosed(android.hardware.camera2.CameraCaptureSession);
method public abstract void onConfigureFailed(android.hardware.camera2.CameraCaptureSession);
method public abstract void onConfigured(android.hardware.camera2.CameraCaptureSession);
@@ -14794,6 +14798,7 @@
method public java.lang.String getSerial();
method public boolean releaseInterface(android.hardware.usb.UsbInterface);
method public android.hardware.usb.UsbRequest requestWait();
+ method public android.hardware.usb.UsbRequest requestWait(int);
method public boolean setConfiguration(android.hardware.usb.UsbConfiguration);
method public boolean setInterface(android.hardware.usb.UsbInterface);
}
@@ -30541,9 +30546,11 @@
method protected void onDisconnected();
method protected abstract void onPrintJobQueued(android.printservice.PrintJob);
method protected abstract void onRequestCancelPrintJob(android.printservice.PrintJob);
+ field public static final java.lang.String EXTRA_CAN_SELECT_PRINTER = "android.printservice.extra.CAN_SELECT_PRINTER";
field public static final java.lang.String EXTRA_PRINTER_INFO = "android.intent.extra.print.EXTRA_PRINTER_INFO";
field public static final java.lang.String EXTRA_PRINT_DOCUMENT_INFO = "android.printservice.extra.PRINT_DOCUMENT_INFO";
field public static final java.lang.String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO";
+ field public static final java.lang.String EXTRA_SELECT_PRINTER = "android.printservice.extra.SELECT_PRINTER";
field public static final java.lang.String SERVICE_INTERFACE = "android.printservice.PrintService";
field public static final java.lang.String SERVICE_META_DATA = "android.printservice";
}
@@ -37061,6 +37068,7 @@
field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+ field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
@@ -44257,6 +44265,7 @@
field public static final int ROTATION_ANIMATION_CROSSFADE = 1; // 0x1
field public static final int ROTATION_ANIMATION_JUMPCUT = 2; // 0x2
field public static final int ROTATION_ANIMATION_ROTATE = 0; // 0x0
+ field public static final int ROTATION_ANIMATION_SEAMLESS = 3; // 0x3
field public static final int SCREEN_BRIGHTNESS_CHANGED = 2048; // 0x800
field public static final int SCREEN_ORIENTATION_CHANGED = 1024; // 0x400
field public static final int SOFT_INPUT_ADJUST_NOTHING = 48; // 0x30
diff --git a/cmds/am/Android.mk b/cmds/am/Android.mk
index f8350dc..5586dd4 100644
--- a/cmds/am/Android.mk
+++ b/cmds/am/Android.mk
@@ -3,8 +3,11 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-proto-files-under, proto)
LOCAL_MODULE := am
+LOCAL_PROTOC_OPTIMIZE_TYPE := stream
include $(BUILD_JAVA_LIBRARY)
include $(CLEAR_VARS)
@@ -13,3 +16,14 @@
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_TAGS := optional
include $(BUILD_PREBUILT)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ $(call all-proto-files-under, proto)
+LOCAL_MODULE := libinstrumentation
+LOCAL_PROTOC_OPTIMIZE_TYPE := full
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(call intermediates-dir-for,STATIC_LIBRARIES,libinstrumentation,HOST,,,)/proto/$(LOCAL_PATH)/proto
+include $(BUILD_HOST_STATIC_LIBRARY)
+
diff --git a/cmds/am/proto/instrumentation_data.proto b/cmds/am/proto/instrumentation_data.proto
new file mode 100644
index 0000000..12a18a2
--- /dev/null
+++ b/cmds/am/proto/instrumentation_data.proto
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.am;
+
+option java_package = "com.android.commands.am";
+
+message ResultsBundleEntry {
+ optional string key = 1;
+
+ optional string value_string = 2;
+ optional sint32 value_int = 3;
+ optional float value_float = 4;
+ optional double value_double = 5;
+ optional sint64 value_long = 6;
+ optional ResultsBundle value_bundle = 7;
+}
+
+message ResultsBundle {
+ repeated ResultsBundleEntry entries = 1;
+}
+
+message TestStatus {
+ optional sint32 result_code = 3;
+ optional ResultsBundle results = 4;
+}
+
+enum SessionStatusCode {
+ /**
+ * The command ran successfully. This does not imply that the tests passed.
+ */
+ SESSION_FINISHED = 0;
+
+ /**
+ * There was an unrecoverable error running the tests.
+ */
+ SESSION_ABORTED = 1;
+}
+
+message SessionStatus {
+ optional SessionStatusCode status_code = 1;
+ optional string error_text = 2;
+ optional sint32 result_code = 3;
+ optional ResultsBundle results = 4;
+}
+
+message Session {
+ repeated TestStatus test_status = 1;
+ optional SessionStatus session_status = 2;
+}
+
+
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index e197bfc..91a4549 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1,20 +1,18 @@
/*
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package com.android.commands.am;
@@ -235,6 +233,7 @@
" -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a\n" +
" common form is [-e <testrunner_flag> <value>[,<value>...]].\n" +
" -p <FILE>: write profiling data to <FILE>\n" +
+ " -m: Write output as protobuf (machine readable)\n" +
" -w: wait for instrumentation to finish before returning. Required for\n" +
" test runners.\n" +
" --user <USER_ID> | current: Specify user instrumentation runs in;\n" +
@@ -543,208 +542,43 @@
receiver.waitForFinish();
}
- private void runInstrument() throws Exception {
- String profileFile = null;
- boolean wait = false;
- boolean rawMode = false;
- boolean no_window_animation = false;
- int userId = UserHandle.USER_CURRENT;
- Bundle args = new Bundle();
- String argKey = null, argValue = null;
- IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
- String abi = null;
+ public void runInstrument() throws Exception {
+ Instrument instrument = new Instrument(mAm, mPm);
String opt;
while ((opt=nextOption()) != null) {
if (opt.equals("-p")) {
- profileFile = nextArgRequired();
+ instrument.profileFile = nextArgRequired();
} else if (opt.equals("-w")) {
- wait = true;
+ instrument.wait = true;
} else if (opt.equals("-r")) {
- rawMode = true;
+ instrument.rawMode = true;
+ } else if (opt.equals("-m")) {
+ instrument.proto = true;
} else if (opt.equals("-e")) {
- argKey = nextArgRequired();
- argValue = nextArgRequired();
- args.putString(argKey, argValue);
+ final String argKey = nextArgRequired();
+ final String argValue = nextArgRequired();
+ instrument.args.putString(argKey, argValue);
} else if (opt.equals("--no_window_animation")
|| opt.equals("--no-window-animation")) {
- no_window_animation = true;
+ instrument.noWindowAnimation = true;
} else if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
+ instrument.userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
- abi = nextArgRequired();
+ instrument.abi = nextArgRequired();
} else {
System.err.println("Error: Unknown option: " + opt);
return;
}
}
- if (userId == UserHandle.USER_ALL) {
+ if (instrument.userId == UserHandle.USER_ALL) {
System.err.println("Error: Can't start instrumentation with user 'all'");
return;
}
- String cnArg = nextArgRequired();
+ instrument.componentNameArg = nextArgRequired();
- ComponentName cn;
- if (cnArg.contains("/")) {
- cn = ComponentName.unflattenFromString(cnArg);
- if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
- } else {
- List<InstrumentationInfo> infos = mPm.queryInstrumentation(null, 0).getList();
-
- final int numInfos = infos == null ? 0: infos.size();
- List<ComponentName> cns = new ArrayList<>();
- for (int i = 0; i < numInfos; i++) {
- InstrumentationInfo info = infos.get(i);
-
- ComponentName c = new ComponentName(info.packageName, info.name);
- if (cnArg.equals(info.packageName)) {
- cns.add(c);
- }
- }
-
- if (cns.size() == 0) {
- throw new IllegalArgumentException("No instrumentation found for: " + cnArg);
- } else if (cns.size() == 1) {
- cn = cns.get(0);
- } else {
- StringBuilder cnsStr = new StringBuilder();
- final int numCns = cns.size();
- for (int i = 0; i < numCns; i++) {
- cnsStr.append(cns.get(i).flattenToString());
- cnsStr.append(", ");
- }
-
- // Remove last ", "
- cnsStr.setLength(cnsStr.length() - 2);
-
- throw new IllegalArgumentException("Found multiple instrumentations: "
- + cnsStr.toString());
- }
- }
-
- InstrumentationWatcher watcher = null;
- UiAutomationConnection connection = null;
- if (wait) {
- watcher = new InstrumentationWatcher();
- watcher.setRawOutput(rawMode);
- connection = new UiAutomationConnection();
- }
-
- float[] oldAnims = null;
- if (no_window_animation) {
- oldAnims = wm.getAnimationScales();
- wm.setAnimationScale(0, 0.0f);
- wm.setAnimationScale(1, 0.0f);
- }
-
- if (abi != null) {
- final String[] supportedAbis = Build.SUPPORTED_ABIS;
- boolean matched = false;
- for (String supportedAbi : supportedAbis) {
- if (supportedAbi.equals(abi)) {
- matched = true;
- break;
- }
- }
-
- if (!matched) {
- throw new AndroidException(
- "INSTRUMENTATION_FAILED: Unsupported instruction set " + abi);
- }
- }
-
- if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)) {
- throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
- }
-
- if (watcher != null) {
- if (!watcher.waitForFinish()) {
- System.out.println("INSTRUMENTATION_ABORTED: System has crashed.");
- }
- }
-
- if (oldAnims != null) {
- wm.setAnimationScales(oldAnims);
- }
- }
-
- private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
- private boolean mFinished = false;
- private boolean mRawMode = false;
-
- /**
- * Set or reset "raw mode". In "raw mode", all bundles are dumped. In "pretty mode",
- * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
- * @param rawMode true for raw mode, false for pretty mode.
- */
- public void setRawOutput(boolean rawMode) {
- mRawMode = rawMode;
- }
-
- @Override
- public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
- synchronized (this) {
- // pretty printer mode?
- String pretty = null;
- if (!mRawMode && results != null) {
- pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
- }
- if (pretty != null) {
- System.out.print(pretty);
- } else {
- if (results != null) {
- for (String key : results.keySet()) {
- System.out.println(
- "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
- }
- }
- System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
- }
- notifyAll();
- }
- }
-
- @Override
- public void instrumentationFinished(ComponentName name, int resultCode,
- Bundle results) {
- synchronized (this) {
- // pretty printer mode?
- String pretty = null;
- if (!mRawMode && results != null) {
- pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
- }
- if (pretty != null) {
- System.out.println(pretty);
- } else {
- if (results != null) {
- for (String key : results.keySet()) {
- System.out.println(
- "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
- }
- }
- System.out.println("INSTRUMENTATION_CODE: " + resultCode);
- }
- mFinished = true;
- notifyAll();
- }
- }
-
- public boolean waitForFinish() {
- synchronized (this) {
- while (!mFinished) {
- try {
- if (!mAm.asBinder().pingBinder()) {
- return false;
- }
- wait(1000);
- } catch (InterruptedException e) {
- throw new IllegalStateException(e);
- }
- }
- }
- return true;
- }
+ instrument.run();
}
}
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
new file mode 100644
index 0000000..8eefd25
--- /dev/null
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.am;
+
+import android.app.IActivityManager;
+import android.app.IInstrumentationWatcher;
+import android.app.Instrumentation;
+import android.app.UiAutomationConnection;
+import android.content.ComponentName;
+import android.content.pm.IPackageManager;
+import android.content.pm.InstrumentationInfo;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.AndroidException;
+import android.util.proto.ProtoOutputStream;
+import android.view.IWindowManager;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Runs the am instrument command
+ */
+public class Instrument {
+ private final IActivityManager mAm;
+ private final IPackageManager mPm;
+ private final IWindowManager mWm;
+
+ // Command line arguments
+ public String profileFile = null;
+ public boolean wait = false;
+ public boolean rawMode = false;
+ public boolean proto = false;
+ public boolean noWindowAnimation = false;
+ public String abi = null;
+ public int userId = UserHandle.USER_CURRENT;
+ public Bundle args = new Bundle();
+ // Required
+ public String componentNameArg;
+
+ /**
+ * Construct the instrument command runner.
+ */
+ public Instrument(IActivityManager am, IPackageManager pm) {
+ mAm = am;
+ mPm = pm;
+ mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+ }
+
+ /**
+ * Base class for status reporting.
+ *
+ * All the methods on this interface are called within the synchronized block
+ * of the InstrumentationWatcher, so calls are in order. However, that means
+ * you must be careful not to do blocking operations because you don't know
+ * exactly the locking dependencies.
+ */
+ private interface StatusReporter {
+ /**
+ * Status update for tests.
+ */
+ public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
+ Bundle results);
+
+ /**
+ * The tests finished.
+ */
+ public void onInstrumentationFinishedLocked(ComponentName name, int resultCode,
+ Bundle results);
+
+ /**
+ * @param errorText a description of the error
+ * @param commandError True if the error is related to the commandline, as opposed
+ * to a test failing.
+ */
+ public void onError(String errorText, boolean commandError);
+ }
+
+ /**
+ * Printer for the 'classic' text based status reporting.
+ */
+ private class TextStatusReporter implements StatusReporter {
+ private boolean mRawMode;
+
+ /**
+ * Human-ish readable output.
+ *
+ * @param rawMode In "raw mode" (true), all bundles are dumped.
+ * In "pretty mode" (false), if a bundle includes
+ * Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
+ */
+ public TextStatusReporter(boolean rawMode) {
+ mRawMode = rawMode;
+ }
+
+ @Override
+ public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ // pretty printer mode?
+ String pretty = null;
+ if (!mRawMode && results != null) {
+ pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
+ }
+ if (pretty != null) {
+ System.out.print(pretty);
+ } else {
+ if (results != null) {
+ for (String key : results.keySet()) {
+ System.out.println(
+ "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
+ }
+ }
+ System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
+ }
+ }
+
+ @Override
+ public void onInstrumentationFinishedLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ // pretty printer mode?
+ String pretty = null;
+ if (!mRawMode && results != null) {
+ pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
+ }
+ if (pretty != null) {
+ System.out.println(pretty);
+ } else {
+ if (results != null) {
+ for (String key : results.keySet()) {
+ System.out.println(
+ "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
+ }
+ }
+ System.out.println("INSTRUMENTATION_CODE: " + resultCode);
+ }
+ }
+
+ @Override
+ public void onError(String errorText, boolean commandError) {
+ // The regular BaseCommand error printing will print the commandErrors.
+ if (!commandError) {
+ System.out.println(errorText);
+ }
+ }
+ }
+
+ /**
+ * Printer for the protobuf based status reporting.
+ */
+ private class ProtoStatusReporter implements StatusReporter {
+ @Override
+ public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ final ProtoOutputStream proto = new ProtoOutputStream();
+
+ final long token = proto.startRepeatedObject(InstrumentationData.Session.TEST_STATUS);
+
+ proto.writeSInt32(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
+ writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results);
+
+ proto.endRepeatedObject(token);
+ writeProtoToStdout(proto);
+ }
+
+ @Override
+ public void onInstrumentationFinishedLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ final ProtoOutputStream proto = new ProtoOutputStream();
+
+ final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
+
+ proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
+ InstrumentationData.SESSION_FINISHED);
+ proto.writeSInt32(InstrumentationData.SessionStatus.RESULT_CODE, resultCode);
+ writeBundle(proto, InstrumentationData.SessionStatus.RESULTS, results);
+
+ proto.endObject(token);
+ writeProtoToStdout(proto);
+ }
+
+ @Override
+ public void onError(String errorText, boolean commandError) {
+ final ProtoOutputStream proto = new ProtoOutputStream();
+
+ final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
+
+ proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
+ InstrumentationData.SESSION_ABORTED);
+ proto.writeString(InstrumentationData.SessionStatus.ERROR_TEXT, errorText);
+
+ proto.endObject(token);
+ writeProtoToStdout(proto);
+ }
+
+ private void writeBundle(ProtoOutputStream proto, long fieldId, Bundle bundle) {
+ final long bundleToken = proto.startObject(fieldId);
+
+ for (final String key: bundle.keySet()) {
+ final long entryToken = proto.startRepeatedObject(
+ InstrumentationData.ResultsBundle.ENTRIES);
+
+ proto.writeString(InstrumentationData.ResultsBundleEntry.KEY, key);
+
+ final Object val = bundle.get(key);
+ if (val instanceof String) {
+ proto.writeString(InstrumentationData.ResultsBundleEntry.VALUE_STRING,
+ (String)val);
+ } else if (val instanceof Byte) {
+ proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
+ ((Byte)val).intValue());
+ } else if (val instanceof Double) {
+ proto.writeDouble(InstrumentationData.ResultsBundleEntry.VALUE_DOUBLE,
+ ((Double)val).doubleValue());
+ } else if (val instanceof Float) {
+ proto.writeFloat(InstrumentationData.ResultsBundleEntry.VALUE_FLOAT,
+ ((Float)val).floatValue());
+ } else if (val instanceof Integer) {
+ proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
+ ((Integer)val).intValue());
+ } else if (val instanceof Long) {
+ proto.writeSInt64(InstrumentationData.ResultsBundleEntry.VALUE_LONG,
+ ((Long)val).longValue());
+ } else if (val instanceof Short) {
+ proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
+ ((Short)val).intValue());
+ } else if (val instanceof Bundle) {
+ writeBundle(proto, InstrumentationData.ResultsBundleEntry.VALUE_BUNDLE,
+ (Bundle)val);
+ }
+
+ proto.endRepeatedObject(entryToken);
+ }
+
+ proto.endObject(bundleToken);
+ }
+
+ private void writeProtoToStdout(ProtoOutputStream proto) {
+ try {
+ System.out.write(proto.getBytes());
+ System.out.flush();
+ } catch (IOException ex) {
+ System.err.println("Error writing finished response: ");
+ ex.printStackTrace(System.err);
+ }
+ }
+ }
+
+
+ /**
+ * Callbacks from the remote instrumentation instance.
+ */
+ private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
+ private final StatusReporter mReporter;
+
+ private boolean mFinished = false;
+
+ public InstrumentationWatcher(StatusReporter reporter) {
+ mReporter = reporter;
+ }
+
+ @Override
+ public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
+ synchronized (this) {
+ mReporter.onInstrumentationStatusLocked(name, resultCode, results);
+ notifyAll();
+ }
+ }
+
+ @Override
+ public void instrumentationFinished(ComponentName name, int resultCode, Bundle results) {
+ synchronized (this) {
+ mReporter.onInstrumentationFinishedLocked(name, resultCode, results);
+ mFinished = true;
+ notifyAll();
+ }
+ }
+
+ public boolean waitForFinish() {
+ synchronized (this) {
+ while (!mFinished) {
+ try {
+ if (!mAm.asBinder().pingBinder()) {
+ return false;
+ }
+ wait(1000);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Figure out which component they really meant.
+ */
+ private ComponentName parseComponentName(String cnArg) throws Exception {
+ if (cnArg.contains("/")) {
+ ComponentName cn = ComponentName.unflattenFromString(cnArg);
+ if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
+ return cn;
+ } else {
+ List<InstrumentationInfo> infos = mPm.queryInstrumentation(null, 0).getList();
+
+ final int numInfos = infos == null ? 0: infos.size();
+ ArrayList<ComponentName> cns = new ArrayList<>();
+ for (int i = 0; i < numInfos; i++) {
+ InstrumentationInfo info = infos.get(i);
+
+ ComponentName c = new ComponentName(info.packageName, info.name);
+ if (cnArg.equals(info.packageName)) {
+ cns.add(c);
+ }
+ }
+
+ if (cns.size() == 0) {
+ throw new IllegalArgumentException("No instrumentation found for: " + cnArg);
+ } else if (cns.size() == 1) {
+ return cns.get(0);
+ } else {
+ StringBuilder cnsStr = new StringBuilder();
+ final int numCns = cns.size();
+ for (int i = 0; i < numCns; i++) {
+ cnsStr.append(cns.get(i).flattenToString());
+ cnsStr.append(", ");
+ }
+
+ // Remove last ", "
+ cnsStr.setLength(cnsStr.length() - 2);
+
+ throw new IllegalArgumentException("Found multiple instrumentations: "
+ + cnsStr.toString());
+ }
+ }
+ }
+
+ /**
+ * Run the instrumentation.
+ */
+ public void run() throws Exception {
+ StatusReporter reporter = null;
+ float[] oldAnims = null;
+
+ try {
+ // Choose which output we will do.
+ if (proto) {
+ reporter = new ProtoStatusReporter();
+ } else if (wait) {
+ reporter = new TextStatusReporter(rawMode);
+ }
+
+ // Choose whether we have to wait for the results.
+ InstrumentationWatcher watcher = null;
+ UiAutomationConnection connection = null;
+ if (reporter != null) {
+ watcher = new InstrumentationWatcher(reporter);
+ connection = new UiAutomationConnection();
+ }
+
+ // Set the window animation if necessary
+ if (noWindowAnimation) {
+ oldAnims = mWm.getAnimationScales();
+ mWm.setAnimationScale(0, 0.0f);
+ mWm.setAnimationScale(1, 0.0f);
+ }
+
+ // Figure out which component we are tring to do.
+ final ComponentName cn = parseComponentName(componentNameArg);
+
+ // Choose an ABI if necessary
+ if (abi != null) {
+ final String[] supportedAbis = Build.SUPPORTED_ABIS;
+ boolean matched = false;
+ for (String supportedAbi : supportedAbis) {
+ if (supportedAbi.equals(abi)) {
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) {
+ throw new AndroidException(
+ "INSTRUMENTATION_FAILED: Unsupported instruction set " + abi);
+ }
+ }
+
+ // Start the instrumentation
+ if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId,
+ abi)) {
+ throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
+ }
+
+ // If we have been requested to wait, do so until the instrumentation is finished.
+ if (watcher != null) {
+ if (!watcher.waitForFinish()) {
+ reporter.onError("INSTRUMENTATION_ABORTED: System has crashed.", false);
+ return;
+ }
+ }
+ } catch (Exception ex) {
+ // Report failures
+ if (reporter != null) {
+ reporter.onError(ex.getMessage(), true);
+ }
+
+ // And re-throw the exception
+ throw ex;
+ } finally {
+ // Clean up
+ if (oldAnims != null) {
+ mWm.setAnimationScales(oldAnims);
+ }
+ }
+ }
+}
+
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index b5f1c2a..e18de8e 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -69,7 +69,6 @@
LOCAL_MODULE_STEM_64 := app_process64
LOCAL_SANITIZE := address
-LOCAL_CLANG := true
LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp
index f6afc85..154cb25 100644
--- a/cmds/idmap/inspect.cpp
+++ b/cmds/idmap/inspect.cpp
@@ -2,6 +2,7 @@
#include <androidfw/AssetManager.h>
#include <androidfw/ResourceTypes.h>
+#include <utils/ByteOrder.h>
#include <utils/String8.h>
#include <fcntl.h>
diff --git a/cmds/webview_zygote/Android.mk b/cmds/webview_zygote/Android.mk
new file mode 100644
index 0000000..66e762c
--- /dev/null
+++ b/cmds/webview_zygote/Android.mk
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := webview_zygote
+
+LOCAL_SRC_FILES := webview_zygote.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroid_runtime \
+ libbinder \
+ liblog \
+ libcutils \
+ libutils
+
+LOCAL_LDFLAGS_32 := -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
+LOCAL_LDFLAGS_64 := -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+
+LOCAL_INIT_RC := webview_zygote32.rc
+
+# Always include the 32-bit version of webview_zygote. If the target is 64-bit,
+# also include the 64-bit webview_zygote.
+ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true)
+ LOCAL_INIT_RC += webview_zygote64.rc
+endif
+
+LOCAL_MULTILIB := both
+
+LOCAL_MODULE_STEM_32 := webview_zygote32
+LOCAL_MODULE_STEM_64 := webview_zygote64
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/webview_zygote/webview_zygote.cpp b/cmds/webview_zygote/webview_zygote.cpp
new file mode 100644
index 0000000..88fee64
--- /dev/null
+++ b/cmds/webview_zygote/webview_zygote.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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 "WebViewZygote"
+
+#include <sys/prctl.h>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class WebViewRuntime : public AndroidRuntime {
+public:
+ WebViewRuntime(char* argBlockStart, size_t argBlockSize)
+ : AndroidRuntime(argBlockStart, argBlockSize) {}
+
+ ~WebViewRuntime() override {}
+
+ void onStarted() override {
+ // Nothing to do since this is a zygote server.
+ }
+
+ void onVmCreated(JNIEnv*) override {
+ // Nothing to do when the VM is created in the zygote.
+ }
+
+ void onZygoteInit() override {
+ // Called after a new process is forked.
+ sp<ProcessState> proc = ProcessState::self();
+ proc->startThreadPool();
+ }
+
+ void onExit(int code) override {
+ IPCThreadState::self()->stopProcess();
+ AndroidRuntime::onExit(code);
+ }
+};
+
+} // namespace android
+
+int main(int argc, char* const argv[]) {
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+ LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
+ return 12;
+ }
+
+ size_t argBlockSize = 0;
+ for (int i = 0; i < argc; ++i) {
+ argBlockSize += strlen(argv[i]) + 1;
+ }
+
+ android::WebViewRuntime runtime(argv[0], argBlockSize);
+ runtime.addOption("-Xzygote");
+
+ android::Vector<android::String8> args;
+ runtime.start("com.android.internal.os.WebViewZygoteInit", args, /*zygote=*/ true);
+}
diff --git a/cmds/webview_zygote/webview_zygote32.rc b/cmds/webview_zygote/webview_zygote32.rc
new file mode 100644
index 0000000..b7decc8
--- /dev/null
+++ b/cmds/webview_zygote/webview_zygote32.rc
@@ -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.
+#
+
+service webview_zygote32 /system/bin/webview_zygote32
+ user webview_zygote
+ socket webview_zygote stream 660 webview_zygote system
+
+on property:init.svc.zygote=stopped
+ stop webview_zygote32
diff --git a/cmds/webview_zygote/webview_zygote64.rc b/cmds/webview_zygote/webview_zygote64.rc
new file mode 100644
index 0000000..2935b28
--- /dev/null
+++ b/cmds/webview_zygote/webview_zygote64.rc
@@ -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.
+#
+
+service webview_zygote64 /system/bin/webview_zygote64
+ user webview_zygote
+ socket webview_zygote stream 660 webview_zygote system
+
+on property:init.svc.zygote=stopped
+ stop webview_zygote64
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 0d9be5f..4066f1c 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3045,6 +3045,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
public int getPackageImportance(String packageName) {
try {
int procState = ActivityManagerNative.getDefault().getPackageProcessState(packageName,
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index d91472b..a2f7aea 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -150,7 +150,7 @@
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
String callingPackage = data.readString();
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
@@ -173,7 +173,7 @@
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
String callingPackage = data.readString();
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
@@ -197,7 +197,7 @@
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
String callingPackage = data.readString();
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
@@ -223,7 +223,7 @@
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
String callingPackage = data.readString();
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
@@ -247,7 +247,7 @@
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
String callingPackage = data.readString();
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
@@ -270,7 +270,7 @@
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
IntentSender intent = IntentSender.CREATOR.createFromParcel(data);
Intent fillInIntent = null;
if (data.readInt() != 0) {
@@ -432,7 +432,7 @@
case RELEASE_SOME_ACTIVITIES_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
- IApplicationThread app = ApplicationThreadNative.asInterface(data.readStrongBinder());
+ IApplicationThread app = IApplicationThread.Stub.asInterface(data.readStrongBinder());
releaseSomeActivities(app);
reply.writeNoException();
return true;
@@ -452,7 +452,7 @@
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app =
- b != null ? ApplicationThreadNative.asInterface(b) : null;
+ b != null ? IApplicationThread.Stub.asInterface(b) : null;
String packageName = data.readString();
b = data.readStrongBinder();
IIntentReceiver rec
@@ -489,7 +489,7 @@
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app =
- b != null ? ApplicationThreadNative.asInterface(b) : null;
+ b != null ? IApplicationThread.Stub.asInterface(b) : null;
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
b = data.readStrongBinder();
@@ -516,7 +516,7 @@
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = b != null ? ApplicationThreadNative.asInterface(b) : null;
+ IApplicationThread app = b != null ? IApplicationThread.Stub.asInterface(b) : null;
Intent intent = Intent.CREATOR.createFromParcel(data);
int userId = data.readInt();
unbroadcastIntent(app, intent, userId);
@@ -541,7 +541,7 @@
case ATTACH_APPLICATION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
- IApplicationThread app = ApplicationThreadNative.asInterface(
+ IApplicationThread app = IApplicationThread.Stub.asInterface(
data.readStrongBinder());
if (app != null) {
attachApplication(app);
@@ -978,7 +978,7 @@
case GET_CONTENT_PROVIDER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
String name = data.readString();
int userId = data.readInt();
boolean stable = data.readInt() != 0;
@@ -1012,7 +1012,7 @@
case PUBLISH_CONTENT_PROVIDERS_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
ArrayList<ContentProviderHolder> providers =
data.createTypedArrayList(ContentProviderHolder.CREATOR);
publishContentProviders(app, providers);
@@ -1077,7 +1077,7 @@
case START_SERVICE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
Intent service = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
String callingPackage = data.readString();
@@ -1091,7 +1091,7 @@
case STOP_SERVICE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
Intent service = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
int userId = data.readInt();
@@ -1130,7 +1130,7 @@
case BIND_SERVICE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
IBinder token = data.readStrongBinder();
Intent service = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
@@ -1210,7 +1210,7 @@
case FINISH_INSTRUMENTATION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
int resultCode = data.readInt();
Bundle results = data.readBundle();
finishInstrumentation(app, resultCode, results);
@@ -1235,6 +1235,16 @@
return true;
}
+ case UPDATE_DISPLAY_OVERRIDE_CONFIGURATION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final Configuration config = Configuration.CREATOR.createFromParcel(data);
+ final int displayId = data.readInt();
+ final boolean updated = updateDisplayOverrideConfiguration(config, displayId);
+ reply.writeNoException();
+ reply.writeInt(updated ? 1 : 0);
+ return true;
+ }
+
case SET_REQUESTED_ORIENTATION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -1421,7 +1431,7 @@
case GRANT_URI_PERMISSION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
String targetPkg = data.readString();
Uri uri = Uri.CREATOR.createFromParcel(data);
int mode = data.readInt();
@@ -1434,7 +1444,7 @@
case REVOKE_URI_PERMISSION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
Uri uri = Uri.CREATOR.createFromParcel(data);
int mode = data.readInt();
int userId = data.readInt();
@@ -1497,7 +1507,7 @@
case SHOW_WAITING_FOR_DEBUGGER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
boolean waiting = data.readInt() != 0;
showWaitingForDebugger(app, waiting);
reply.writeNoException();
@@ -2061,7 +2071,7 @@
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
- IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IApplicationThread app = IApplicationThread.Stub.asInterface(b);
String callingPackage = data.readString();
Intent[] intents = data.createTypedArray(Intent.CREATOR);
String[] resolvedTypes = data.createStringArray();
@@ -2547,15 +2557,6 @@
return true;
}
- case DELETE_ACTIVITY_CONTAINER_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- IActivityContainer activityContainer =
- IActivityContainer.Stub.asInterface(data.readStrongBinder());
- deleteActivityContainer(activityContainer);
- reply.writeNoException();
- return true;
- }
-
case CREATE_STACK_ON_DISPLAY: {
data.enforceInterface(IActivityManager.descriptor);
int displayId = data.readInt();
@@ -2931,6 +2932,22 @@
reply.writeNoException();
return true;
}
+ case GET_DEFAULT_PICTURE_IN_PICTURE_BOUNDS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int displayId = data.readInt();
+ Rect r = getDefaultPictureInPictureBounds(displayId);
+ reply.writeNoException();
+ r.writeToParcel(reply, 0);
+ return true;
+ }
+ case GET_PICTURE_IN_PICTURE_MOVEMENT_BOUNDS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int displayId = data.readInt();
+ Rect r = getPictureInPictureMovementBounds(displayId);
+ reply.writeNoException();
+ r.writeToParcel(reply, 0);
+ return true;
+ }
case SET_VR_MODE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final IBinder token = data.readStrongBinder();
@@ -4594,8 +4611,7 @@
data.recycle();
return res;
}
- public boolean updateConfiguration(Configuration values) throws RemoteException
- {
+ public boolean updateConfiguration(Configuration values) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -4607,6 +4623,20 @@
reply.recycle();
return updated;
}
+ public boolean updateDisplayOverrideConfiguration(Configuration values, int displayId)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ values.writeToParcel(data, 0);
+ data.writeInt(displayId);
+ mRemote.transact(UPDATE_DISPLAY_OVERRIDE_CONFIGURATION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean updated = reply.readInt() == 1;
+ data.recycle();
+ reply.recycle();
+ return updated;
+ }
public void setRequestedOrientation(IBinder token, int requestedOrientation)
throws RemoteException {
Parcel data = Parcel.obtain();
@@ -6359,18 +6389,6 @@
return res;
}
- public void deleteActivityContainer(IActivityContainer activityContainer)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeStrongBinder(activityContainer.asBinder());
- mRemote.transact(DELETE_ACTIVITY_CONTAINER_TRANSACTION, data, reply, 0);
- reply.readException();
- data.recycle();
- reply.recycle();
- }
-
public boolean startBinderTracking() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -6989,6 +7007,36 @@
}
@Override
+ public Rect getDefaultPictureInPictureBounds(int displayId) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(displayId);
+ mRemote.transact(GET_DEFAULT_PICTURE_IN_PICTURE_BOUNDS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ Rect rect = Rect.CREATOR.createFromParcel(reply);
+ data.recycle();
+ reply.recycle();
+ return rect;
+ }
+
+ @Override
+ public Rect getPictureInPictureMovementBounds(int displayId) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(displayId);
+ mRemote.transact(GET_PICTURE_IN_PICTURE_MOVEMENT_BOUNDS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ Rect rect = Rect.CREATOR.createFromParcel(reply);
+ data.recycle();
+ reply.recycle();
+ return rect;
+ }
+
+ @Override
public boolean isAppForeground(int uid) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3a8b6c7..e9a200f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -647,7 +647,7 @@
private native void dumpGraphicsInfo(FileDescriptor fd);
- private class ApplicationThread extends ApplicationThreadNative {
+ private class ApplicationThread extends IApplicationThread.Stub {
private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s";
private int mLastProcessState = -1;
@@ -859,7 +859,7 @@
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
- CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings,
+ CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial) {
if (services != null) {
@@ -929,15 +929,17 @@
mH.sendMessage(mH.obtainMessage(H.GC_WHEN_IDLE));
}
- public void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args) {
+ public void dumpService(ParcelFileDescriptor pfd, IBinder servicetoken, String[] args) {
DumpComponentInfo data = new DumpComponentInfo();
try {
- data.fd = ParcelFileDescriptor.dup(fd);
+ data.fd = pfd.dup();
data.token = servicetoken;
data.args = args;
sendMessage(H.DUMP_SERVICE, data, 0, 0, true /*async*/);
} catch (IOException e) {
Slog.w(TAG, "dumpService failed", e);
+ } finally {
+ IoUtils.closeQuietly(pfd);
}
}
@@ -976,6 +978,10 @@
sendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0, 0, true /*async*/);
}
+ public void attachAgent(String agent) {
+ sendMessage(H.ATTACH_AGENT, agent);
+ }
+
public void setSchedulingGroup(int group) {
// Note: do this immediately, since going into the foreground
// should happen regardless of what pending work we have to do
@@ -996,43 +1002,48 @@
sendMessage(H.SCHEDULE_CRASH, msg);
}
- public void dumpActivity(FileDescriptor fd, IBinder activitytoken,
+ public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken,
String prefix, String[] args) {
DumpComponentInfo data = new DumpComponentInfo();
try {
- data.fd = ParcelFileDescriptor.dup(fd);
+ data.fd = pfd.dup();
data.token = activitytoken;
data.prefix = prefix;
data.args = args;
sendMessage(H.DUMP_ACTIVITY, data, 0, 0, true /*async*/);
} catch (IOException e) {
Slog.w(TAG, "dumpActivity failed", e);
+ } finally {
+ IoUtils.closeQuietly(pfd);
}
}
- public void dumpProvider(FileDescriptor fd, IBinder providertoken,
+ public void dumpProvider(ParcelFileDescriptor pfd, IBinder providertoken,
String[] args) {
DumpComponentInfo data = new DumpComponentInfo();
try {
- data.fd = ParcelFileDescriptor.dup(fd);
+ data.fd = pfd.dup();
data.token = providertoken;
data.args = args;
sendMessage(H.DUMP_PROVIDER, data, 0, 0, true /*async*/);
} catch (IOException e) {
Slog.w(TAG, "dumpProvider failed", e);
+ } finally {
+ IoUtils.closeQuietly(pfd);
}
}
@Override
- public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin,
+ public void dumpMemInfo(ParcelFileDescriptor pfd, Debug.MemoryInfo mem, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
boolean dumpUnreachable, String[] args) {
- FileOutputStream fout = new FileOutputStream(fd);
+ FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor());
PrintWriter pw = new FastPrintWriter(fout);
try {
dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, dumpUnreachable);
} finally {
pw.flush();
+ IoUtils.closeQuietly(pfd);
}
}
@@ -1175,44 +1186,49 @@
}
@Override
- public void dumpGfxInfo(FileDescriptor fd, String[] args) {
- dumpGraphicsInfo(fd);
- WindowManagerGlobal.getInstance().dumpGfxInfo(fd, args);
+ public void dumpGfxInfo(ParcelFileDescriptor pfd, String[] args) {
+ dumpGraphicsInfo(pfd.getFileDescriptor());
+ WindowManagerGlobal.getInstance().dumpGfxInfo(pfd.getFileDescriptor(), args);
+ IoUtils.closeQuietly(pfd);
}
- private void dumpDatabaseInfo(FileDescriptor fd, String[] args) {
- PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
+ private void dumpDatabaseInfo(ParcelFileDescriptor pfd, String[] args) {
+ PrintWriter pw = new FastPrintWriter(
+ new FileOutputStream(pfd.getFileDescriptor()));
PrintWriterPrinter printer = new PrintWriterPrinter(pw);
SQLiteDebug.dump(printer, args);
pw.flush();
}
@Override
- public void dumpDbInfo(final FileDescriptor fd, final String[] args) {
+ public void dumpDbInfo(final ParcelFileDescriptor pfd, final String[] args) {
if (mSystemThread) {
// Ensure this invocation is asynchronous to prevent writer waiting if buffer cannot
// be consumed. But it must duplicate the file descriptor first, since caller might
// be closing it.
final ParcelFileDescriptor dup;
try {
- dup = ParcelFileDescriptor.dup(fd);
+ dup = pfd.dup();
} catch (IOException e) {
- Log.w(TAG, "Could not dup FD " + fd.getInt$());
+ Log.w(TAG, "Could not dup FD " + pfd.getFileDescriptor().getInt$());
return;
+ } finally {
+ IoUtils.closeQuietly(pfd);
}
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
try {
- dumpDatabaseInfo(dup.getFileDescriptor(), args);
+ dumpDatabaseInfo(dup, args);
} finally {
IoUtils.closeQuietly(dup);
}
}
});
} else {
- dumpDatabaseInfo(fd, args);
+ dumpDatabaseInfo(pfd, args);
+ IoUtils.closeQuietly(pfd);
}
}
@@ -1251,9 +1267,9 @@
sendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
}
- public void scheduleOnNewActivityOptions(IBinder token, ActivityOptions options) {
+ public void scheduleOnNewActivityOptions(IBinder token, Bundle options) {
sendMessage(H.ON_NEW_ACTIVITY_OPTIONS,
- new Pair<IBinder, ActivityOptions>(token, options));
+ new Pair<IBinder, ActivityOptions>(token, ActivityOptions.fromBundle(options)));
}
public void setProcessState(int state) {
@@ -1319,10 +1335,12 @@
}
@Override
- public void stopBinderTrackingAndDump(FileDescriptor fd) {
+ public void stopBinderTrackingAndDump(ParcelFileDescriptor pfd) {
try {
- sendMessage(H.STOP_BINDER_TRACKING_AND_DUMP, ParcelFileDescriptor.dup(fd));
+ sendMessage(H.STOP_BINDER_TRACKING_AND_DUMP, pfd.dup());
} catch (IOException e) {
+ } finally {
+ IoUtils.closeQuietly(pfd);
}
}
@@ -1415,6 +1433,7 @@
public static final int MULTI_WINDOW_MODE_CHANGED = 152;
public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
+ public static final int ATTACH_AGENT = 155;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
@@ -1471,6 +1490,7 @@
case MULTI_WINDOW_MODE_CHANGED: return "MULTI_WINDOW_MODE_CHANGED";
case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED";
case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED";
+ case ATTACH_AGENT: return "ATTACH_AGENT";
}
}
return Integer.toString(code);
@@ -1725,6 +1745,8 @@
case LOCAL_VOICE_INTERACTION_STARTED:
handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
(IVoiceInteractor) ((SomeArgs) msg.obj).arg2);
+ case ATTACH_AGENT:
+ handleAttachAgent((String) msg.obj);
break;
}
Object obj = msg.obj;
@@ -2994,6 +3016,14 @@
}
}
+ static final void handleAttachAgent(String agent) {
+ try {
+ VMDebug.attachAgent(agent);
+ } catch (IOException e) {
+ Slog.e(TAG, "Attaching agent failed: " + agent);
+ }
+ }
+
private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
/**
@@ -3097,8 +3127,8 @@
String classname = data.appInfo.backupAgentName;
// full backup operation but no app-supplied agent? use the default implementation
- if (classname == null && (data.backupMode == IApplicationThread.BACKUP_MODE_FULL
- || data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL)) {
+ if (classname == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
+ || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) {
classname = "android.app.backup.FullBackupAgent";
}
@@ -3130,8 +3160,9 @@
// If this is during restore, fail silently; otherwise go
// ahead and let the user see the crash.
Slog.e(TAG, "Agent threw during creation: " + e);
- if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE
- && data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
+ if (data.backupMode != ApplicationThreadConstants.BACKUP_MODE_RESTORE
+ && data.backupMode !=
+ ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL) {
throw e;
}
// falling through with 'binder' still null
@@ -4904,10 +4935,10 @@
final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
boolean hasPkgInfo = false;
switch (cmd) {
- case IApplicationThread.PACKAGE_REMOVED:
- case IApplicationThread.PACKAGE_REMOVED_DONT_KILL:
+ case ApplicationThreadConstants.PACKAGE_REMOVED:
+ case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL:
{
- final boolean killApp = cmd == IApplicationThread.PACKAGE_REMOVED;
+ final boolean killApp = cmd == ApplicationThreadConstants.PACKAGE_REMOVED;
if (packages == null) {
break;
}
@@ -4932,7 +4963,7 @@
}
break;
}
- case IApplicationThread.PACKAGE_REPLACED:
+ case ApplicationThreadConstants.PACKAGE_REPLACED:
{
if (packages == null) {
break;
@@ -5232,10 +5263,10 @@
/* ignore */
}
- if (data.debugMode != IApplicationThread.DEBUG_OFF) {
+ if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
// XXX should have option to change the port.
Debug.changeDebugPort(8100);
- if (data.debugMode == IApplicationThread.DEBUG_WAIT) {
+ if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
Slog.w(TAG, "Application " + data.info.getPackageName()
+ " is waiting for the debugger on port 8100...");
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 6458d6f..ec21882 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -206,6 +206,7 @@
private ArrayList<Matrix> mSharedElementParentMatrices;
private boolean mSharedElementTransitionComplete;
private boolean mViewsTransitionComplete;
+ private ArrayList<View> mStrippedTransitioningViews = new ArrayList<>();
public ActivityTransitionCoordinator(Window window,
ArrayList<String> allSharedElementNames,
@@ -287,7 +288,7 @@
View view = mTransitioningViews.get(i);
if (!view.getGlobalVisibleRect(r)) {
mTransitioningViews.remove(i);
- showView(view, true);
+ mStrippedTransitioningViews.add(view);
}
}
}
@@ -360,6 +361,12 @@
}
}
}
+ if (mStrippedTransitioningViews != null) {
+ for (int i = mStrippedTransitioningViews.size() - 1; i >= 0; i--) {
+ View view = mStrippedTransitioningViews.get(i);
+ set.excludeTarget(view, true);
+ }
+ }
// By adding the transition after addTarget, we prevent addTarget from
// affecting transition.
set.addTransition(transition);
@@ -679,6 +686,7 @@
mWindow = null;
mSharedElements.clear();
mTransitioningViews = null;
+ mStrippedTransitioningViews = null;
mOriginalAlphas.clear();
mResultReceiver = null;
mPendingTransition = null;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 37faa2e..3b3e070 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1381,7 +1381,7 @@
static void handlePackageBroadcast(int cmd, String[] pkgList, boolean hasPkgInfo) {
boolean immediateGc = false;
- if (cmd == IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE) {
+ if (cmd == ApplicationThreadConstants.EXTERNAL_STORAGE_UNAVAILABLE) {
immediateGc = true;
}
if (pkgList != null && (pkgList.length > 0)) {
diff --git a/core/java/android/app/ApplicationThreadConstants.java b/core/java/android/app/ApplicationThreadConstants.java
new file mode 100644
index 0000000..1fa670f
--- /dev/null
+++ b/core/java/android/app/ApplicationThreadConstants.java
@@ -0,0 +1,39 @@
+/*
+ * 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.app;
+
+/**
+ * @hide
+ */
+public final class ApplicationThreadConstants {
+ public static final int BACKUP_MODE_INCREMENTAL = 0;
+ public static final int BACKUP_MODE_FULL = 1;
+ public static final int BACKUP_MODE_RESTORE = 2;
+ public static final int BACKUP_MODE_RESTORE_FULL = 3;
+
+ public static final int DEBUG_OFF = 0;
+ public static final int DEBUG_ON = 1;
+ public static final int DEBUG_WAIT = 2;
+
+ // the package has been removed, clean up internal references
+ public static final int PACKAGE_REMOVED = 0;
+ public static final int EXTERNAL_STORAGE_UNAVAILABLE = 1;
+ // the package is being modified in-place, don't kill it and retain references to it
+ public static final int PACKAGE_REMOVED_DONT_KILL = 2;
+ // a previously removed package was replaced with a new version [eg. upgrade, split added, ...]
+ public static final int PACKAGE_REPLACED = 3;
+}
\ No newline at end of file
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
deleted file mode 100644
index 12e527e..0000000
--- a/core/java/android/app/ApplicationThreadNative.java
+++ /dev/null
@@ -1,1544 +0,0 @@
-/*
- * Copyright (C) 2006 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.app;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.IIntentReceiver;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Parcelable;
-import android.os.PersistableBundle;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
-import android.os.TransactionTooLargeException;
-import android.util.Log;
-
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.content.ReferrerIntent;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/** {@hide} */
-public abstract class ApplicationThreadNative extends Binder
- implements IApplicationThread {
- /**
- * Cast a Binder object into an application thread interface, generating
- * a proxy if needed.
- */
- static public IApplicationThread asInterface(IBinder obj) {
- if (obj == null) {
- return null;
- }
- IApplicationThread in =
- (IApplicationThread)obj.queryLocalInterface(descriptor);
- if (in != null) {
- return in;
- }
-
- return new ApplicationThreadProxy(obj);
- }
-
- public ApplicationThreadNative() {
- attachInterface(this, descriptor);
- }
-
- @Override
- public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
- throws RemoteException {
- switch (code) {
- case SCHEDULE_PAUSE_ACTIVITY_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder b = data.readStrongBinder();
- boolean finished = data.readInt() != 0;
- boolean userLeaving = data.readInt() != 0;
- int configChanges = data.readInt();
- boolean dontReport = data.readInt() != 0;
- schedulePauseActivity(b, finished, userLeaving, configChanges, dontReport);
- return true;
- }
-
- case SCHEDULE_STOP_ACTIVITY_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder b = data.readStrongBinder();
- boolean show = data.readInt() != 0;
- int configChanges = data.readInt();
- scheduleStopActivity(b, show, configChanges);
- return true;
- }
-
- case SCHEDULE_WINDOW_VISIBILITY_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder b = data.readStrongBinder();
- boolean show = data.readInt() != 0;
- scheduleWindowVisibility(b, show);
- return true;
- }
-
- case SCHEDULE_SLEEPING_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder b = data.readStrongBinder();
- boolean sleeping = data.readInt() != 0;
- scheduleSleeping(b, sleeping);
- return true;
- }
-
- case SCHEDULE_RESUME_ACTIVITY_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder b = data.readStrongBinder();
- int procState = data.readInt();
- boolean isForward = data.readInt() != 0;
- Bundle resumeArgs = data.readBundle();
- scheduleResumeActivity(b, procState, isForward, resumeArgs);
- return true;
- }
-
- case SCHEDULE_SEND_RESULT_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder b = data.readStrongBinder();
- List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
- scheduleSendResult(b, ri);
- return true;
- }
-
- case SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- Intent intent = Intent.CREATOR.createFromParcel(data);
- IBinder b = data.readStrongBinder();
- int ident = data.readInt();
- ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
- Configuration curConfig = Configuration.CREATOR.createFromParcel(data);
- Configuration overrideConfig = null;
- if (data.readInt() != 0) {
- overrideConfig = Configuration.CREATOR.createFromParcel(data);
- }
- CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
- String referrer = data.readString();
- IVoiceInteractor voiceInteractor = IVoiceInteractor.Stub.asInterface(
- data.readStrongBinder());
- int procState = data.readInt();
- Bundle state = data.readBundle();
- PersistableBundle persistentState = data.readPersistableBundle();
- List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
- List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR);
- boolean notResumed = data.readInt() != 0;
- boolean isForward = data.readInt() != 0;
- ProfilerInfo profilerInfo = data.readInt() != 0
- ? ProfilerInfo.CREATOR.createFromParcel(data) : null;
- scheduleLaunchActivity(intent, b, ident, info, curConfig, overrideConfig, compatInfo,
- referrer, voiceInteractor, procState, state, persistentState, ri, pi,
- notResumed, isForward, profilerInfo);
- return true;
- }
-
- case SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder b = data.readStrongBinder();
- List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
- List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR);
- int configChanges = data.readInt();
- boolean notResumed = data.readInt() != 0;
- Configuration config = Configuration.CREATOR.createFromParcel(data);
- Configuration overrideConfig = null;
- if (data.readInt() != 0) {
- overrideConfig = Configuration.CREATOR.createFromParcel(data);
- }
- boolean preserveWindows = data.readInt() == 1;
- scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config, overrideConfig,
- preserveWindows);
- return true;
- }
-
- case SCHEDULE_NEW_INTENT_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR);
- IBinder b = data.readStrongBinder();
- final boolean andPause = data.readInt() == 1;
- scheduleNewIntent(pi, b, andPause);
- return true;
- }
-
- case SCHEDULE_FINISH_ACTIVITY_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder b = data.readStrongBinder();
- boolean finishing = data.readInt() != 0;
- int configChanges = data.readInt();
- scheduleDestroyActivity(b, finishing, configChanges);
- return true;
- }
-
- case SCHEDULE_RECEIVER_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- Intent intent = Intent.CREATOR.createFromParcel(data);
- ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
- CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
- int resultCode = data.readInt();
- String resultData = data.readString();
- Bundle resultExtras = data.readBundle();
- boolean sync = data.readInt() != 0;
- int sendingUser = data.readInt();
- int processState = data.readInt();
- scheduleReceiver(intent, info, compatInfo, resultCode, resultData,
- resultExtras, sync, sendingUser, processState);
- return true;
- }
-
- case SCHEDULE_CREATE_SERVICE_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder token = data.readStrongBinder();
- ServiceInfo info = ServiceInfo.CREATOR.createFromParcel(data);
- CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
- int processState = data.readInt();
- scheduleCreateService(token, info, compatInfo, processState);
- return true;
- }
-
- case SCHEDULE_BIND_SERVICE_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder token = data.readStrongBinder();
- Intent intent = Intent.CREATOR.createFromParcel(data);
- boolean rebind = data.readInt() != 0;
- int processState = data.readInt();
- scheduleBindService(token, intent, rebind, processState);
- return true;
- }
-
- case SCHEDULE_UNBIND_SERVICE_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder token = data.readStrongBinder();
- Intent intent = Intent.CREATOR.createFromParcel(data);
- scheduleUnbindService(token, intent);
- return true;
- }
-
- case SCHEDULE_SERVICE_ARGS_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder token = data.readStrongBinder();
- boolean taskRemoved = data.readInt() != 0;
- int startId = data.readInt();
- int fl = data.readInt();
- Intent args;
- if (data.readInt() != 0) {
- args = Intent.CREATOR.createFromParcel(data);
- } else {
- args = null;
- }
- scheduleServiceArgs(token, taskRemoved, startId, fl, args);
- return true;
- }
-
- case SCHEDULE_STOP_SERVICE_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder token = data.readStrongBinder();
- scheduleStopService(token);
- return true;
- }
-
- case BIND_APPLICATION_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- String packageName = data.readString();
- ApplicationInfo info =
- ApplicationInfo.CREATOR.createFromParcel(data);
- List<ProviderInfo> providers =
- data.createTypedArrayList(ProviderInfo.CREATOR);
- ComponentName testName = (data.readInt() != 0)
- ? new ComponentName(data) : null;
- ProfilerInfo profilerInfo = data.readInt() != 0
- ? ProfilerInfo.CREATOR.createFromParcel(data) : null;
- Bundle testArgs = data.readBundle();
- IBinder binder = data.readStrongBinder();
- IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder);
- binder = data.readStrongBinder();
- IUiAutomationConnection uiAutomationConnection =
- IUiAutomationConnection.Stub.asInterface(binder);
- int testMode = data.readInt();
- boolean enableBinderTracking = data.readInt() != 0;
- boolean trackAllocation = data.readInt() != 0;
- boolean restrictedBackupMode = (data.readInt() != 0);
- boolean persistent = (data.readInt() != 0);
- Configuration config = Configuration.CREATOR.createFromParcel(data);
- CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
- HashMap<String, IBinder> services = data.readHashMap(null);
- Bundle coreSettings = data.readBundle();
- String buildSerial = data.readString();
- bindApplication(packageName, info, providers, testName, profilerInfo, testArgs,
- testWatcher, uiAutomationConnection, testMode, enableBinderTracking,
- trackAllocation, restrictedBackupMode, persistent, config, compatInfo, services,
- coreSettings, buildSerial);
- return true;
- }
-
- case SCHEDULE_EXIT_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- scheduleExit();
- return true;
- }
-
- case SCHEDULE_SUICIDE_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- scheduleSuicide();
- return true;
- }
-
- case SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- Configuration config = Configuration.CREATOR.createFromParcel(data);
- scheduleConfigurationChanged(config);
- return true;
- }
-
- case UPDATE_TIME_ZONE_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- updateTimeZone();
- return true;
- }
-
- case CLEAR_DNS_CACHE_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- clearDnsCache();
- return true;
- }
-
- case SET_HTTP_PROXY_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- final String proxy = data.readString();
- final String port = data.readString();
- final String exclList = data.readString();
- final Uri pacFileUrl = Uri.CREATOR.createFromParcel(data);
- setHttpProxy(proxy, port, exclList, pacFileUrl);
- return true;
- }
-
- case PROCESS_IN_BACKGROUND_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- processInBackground();
- return true;
- }
-
- case DUMP_SERVICE_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- ParcelFileDescriptor fd = data.readFileDescriptor();
- final IBinder service = data.readStrongBinder();
- final String[] args = data.readStringArray();
- if (fd != null) {
- dumpService(fd.getFileDescriptor(), service, args);
- try {
- fd.close();
- } catch (IOException e) {
- }
- }
- return true;
- }
-
- case DUMP_PROVIDER_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- ParcelFileDescriptor fd = data.readFileDescriptor();
- final IBinder service = data.readStrongBinder();
- final String[] args = data.readStringArray();
- if (fd != null) {
- dumpProvider(fd.getFileDescriptor(), service, args);
- try {
- fd.close();
- } catch (IOException e) {
- }
- }
- return true;
- }
-
- case SCHEDULE_REGISTERED_RECEIVER_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- IIntentReceiver receiver = IIntentReceiver.Stub.asInterface(
- data.readStrongBinder());
- Intent intent = Intent.CREATOR.createFromParcel(data);
- int resultCode = data.readInt();
- String dataStr = data.readString();
- Bundle extras = data.readBundle();
- boolean ordered = data.readInt() != 0;
- boolean sticky = data.readInt() != 0;
- int sendingUser = data.readInt();
- int processState = data.readInt();
- scheduleRegisteredReceiver(receiver, intent,
- resultCode, dataStr, extras, ordered, sticky, sendingUser, processState);
- return true;
- }
-
- case SCHEDULE_LOW_MEMORY_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- scheduleLowMemory();
- return true;
- }
-
- case SCHEDULE_ACTIVITY_CONFIGURATION_CHANGED_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder b = data.readStrongBinder();
- Configuration overrideConfig = null;
- if (data.readInt() != 0) {
- overrideConfig = Configuration.CREATOR.createFromParcel(data);
- }
- final boolean reportToActivity = data.readInt() == 1;
- scheduleActivityConfigurationChanged(b, overrideConfig, reportToActivity);
- return true;
- }
-
- case SCHEDULE_LOCAL_VOICE_INTERACTION_STARTED_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder token = data.readStrongBinder();
- IVoiceInteractor voiceInteractor = IVoiceInteractor.Stub.asInterface(
- data.readStrongBinder());
- scheduleLocalVoiceInteractionStarted(token, voiceInteractor);
- return true;
- }
-
- case PROFILER_CONTROL_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- boolean start = data.readInt() != 0;
- int profileType = data.readInt();
- ProfilerInfo profilerInfo = data.readInt() != 0
- ? ProfilerInfo.CREATOR.createFromParcel(data) : null;
- profilerControl(start, profilerInfo, profileType);
- return true;
- }
-
- case SET_SCHEDULING_GROUP_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- int group = data.readInt();
- setSchedulingGroup(group);
- return true;
- }
-
- case SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data);
- CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
- int backupMode = data.readInt();
- scheduleCreateBackupAgent(appInfo, compatInfo, backupMode);
- return true;
- }
-
- case SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data);
- CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
- scheduleDestroyBackupAgent(appInfo, compatInfo);
- return true;
- }
-
- case DISPATCH_PACKAGE_BROADCAST_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- int cmd = data.readInt();
- String[] packages = data.readStringArray();
- dispatchPackageBroadcast(cmd, packages);
- return true;
- }
-
- case SCHEDULE_CRASH_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- String msg = data.readString();
- scheduleCrash(msg);
- return true;
- }
-
- case DUMP_HEAP_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- boolean managed = data.readInt() != 0;
- String path = data.readString();
- ParcelFileDescriptor fd = data.readInt() != 0
- ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null;
- dumpHeap(managed, path, fd);
- return true;
- }
-
- case DUMP_ACTIVITY_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- ParcelFileDescriptor fd = data.readFileDescriptor();
- final IBinder activity = data.readStrongBinder();
- final String prefix = data.readString();
- final String[] args = data.readStringArray();
- if (fd != null) {
- dumpActivity(fd.getFileDescriptor(), activity, prefix, args);
- try {
- fd.close();
- } catch (IOException e) {
- }
- }
- return true;
- }
-
- case SET_CORE_SETTINGS_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- Bundle settings = data.readBundle();
- setCoreSettings(settings);
- return true;
- }
-
- case UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- String pkg = data.readString();
- CompatibilityInfo compat = CompatibilityInfo.CREATOR.createFromParcel(data);
- updatePackageCompatibilityInfo(pkg, compat);
- return true;
- }
-
- case SCHEDULE_TRIM_MEMORY_TRANSACTION: {
- data.enforceInterface(IApplicationThread.descriptor);
- int level = data.readInt();
- scheduleTrimMemory(level);
- return true;
- }
-
- case DUMP_MEM_INFO_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- ParcelFileDescriptor fd = data.readFileDescriptor();
- Debug.MemoryInfo mi = Debug.MemoryInfo.CREATOR.createFromParcel(data);
- boolean checkin = data.readInt() != 0;
- boolean dumpInfo = data.readInt() != 0;
- boolean dumpDalvik = data.readInt() != 0;
- boolean dumpSummaryOnly = data.readInt() != 0;
- boolean dumpUnreachable = data.readInt() != 0;
- String[] args = data.readStringArray();
- if (fd != null) {
- try {
- dumpMemInfo(fd.getFileDescriptor(), mi, checkin, dumpInfo,
- dumpDalvik, dumpSummaryOnly, dumpUnreachable, args);
- } finally {
- try {
- fd.close();
- } catch (IOException e) {
- // swallowed, not propagated back to the caller
- }
- }
- }
- reply.writeNoException();
- return true;
- }
-
- case DUMP_GFX_INFO_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- ParcelFileDescriptor fd = data.readFileDescriptor();
- String[] args = data.readStringArray();
- if (fd != null) {
- try {
- dumpGfxInfo(fd.getFileDescriptor(), args);
- } finally {
- try {
- fd.close();
- } catch (IOException e) {
- // swallowed, not propagated back to the caller
- }
- }
- }
- reply.writeNoException();
- return true;
- }
-
- case DUMP_DB_INFO_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- ParcelFileDescriptor fd = data.readFileDescriptor();
- String[] args = data.readStringArray();
- if (fd != null) {
- try {
- dumpDbInfo(fd.getFileDescriptor(), args);
- } finally {
- try {
- fd.close();
- } catch (IOException e) {
- // swallowed, not propagated back to the caller
- }
- }
- }
- reply.writeNoException();
- return true;
- }
-
- case UNSTABLE_PROVIDER_DIED_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder provider = data.readStrongBinder();
- unstableProviderDied(provider);
- reply.writeNoException();
- return true;
- }
-
- case REQUEST_ASSIST_CONTEXT_EXTRAS_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder activityToken = data.readStrongBinder();
- IBinder requestToken = data.readStrongBinder();
- int requestType = data.readInt();
- int sessionId = data.readInt();
- requestAssistContextExtras(activityToken, requestToken, requestType, sessionId);
- reply.writeNoException();
- return true;
- }
-
- case SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder token = data.readStrongBinder();
- boolean timeout = data.readInt() == 1;
- scheduleTranslucentConversionComplete(token, timeout);
- reply.writeNoException();
- return true;
- }
-
- case SCHEDULE_ON_NEW_ACTIVITY_OPTIONS_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder token = data.readStrongBinder();
- ActivityOptions options = new ActivityOptions(data.readBundle());
- scheduleOnNewActivityOptions(token, options);
- reply.writeNoException();
- return true;
- }
-
- case SET_PROCESS_STATE_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- int state = data.readInt();
- setProcessState(state);
- reply.writeNoException();
- return true;
- }
-
- case SCHEDULE_INSTALL_PROVIDER_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- ProviderInfo provider = ProviderInfo.CREATOR.createFromParcel(data);
- scheduleInstallProvider(provider);
- reply.writeNoException();
- return true;
- }
-
- case UPDATE_TIME_PREFS_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- byte is24Hour = data.readByte();
- updateTimePrefs(is24Hour == (byte) 1);
- reply.writeNoException();
- return true;
- }
-
- case CANCEL_VISIBLE_BEHIND_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder token = data.readStrongBinder();
- scheduleCancelVisibleBehind(token);
- reply.writeNoException();
- return true;
- }
-
- case BACKGROUND_VISIBLE_BEHIND_CHANGED_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder token = data.readStrongBinder();
- boolean enabled = data.readInt() > 0;
- scheduleBackgroundVisibleBehindChanged(token, enabled);
- reply.writeNoException();
- return true;
- }
-
- case ENTER_ANIMATION_COMPLETE_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder token = data.readStrongBinder();
- scheduleEnterAnimationComplete(token);
- reply.writeNoException();
- return true;
- }
-
- case NOTIFY_CLEARTEXT_NETWORK_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- final byte[] firstPacket = data.createByteArray();
- notifyCleartextNetwork(firstPacket);
- reply.writeNoException();
- return true;
- }
-
- case START_BINDER_TRACKING_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- startBinderTracking();
- return true;
- }
-
- case STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- ParcelFileDescriptor fd = data.readFileDescriptor();
- if (fd != null) {
- stopBinderTrackingAndDump(fd.getFileDescriptor());
- try {
- fd.close();
- } catch (IOException e) {
- }
- }
- return true;
- }
-
- case SCHEDULE_MULTI_WINDOW_CHANGED_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- final IBinder b = data.readStrongBinder();
- final boolean inMultiWindow = data.readInt() != 0;
- scheduleMultiWindowModeChanged(b, inMultiWindow);
- return true;
- }
-
- case SCHEDULE_PICTURE_IN_PICTURE_CHANGED_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- final IBinder b = data.readStrongBinder();
- final boolean inPip = data.readInt() != 0;
- schedulePictureInPictureModeChanged(b, inPip);
- return true;
- }
- case HANDLE_TRUST_STORAGE_UPDATE_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- handleTrustStorageUpdate();
- return true;
- }
-
- }
-
- return super.onTransact(code, data, reply, flags);
- }
-
- public IBinder asBinder()
- {
- return this;
- }
-}
-
-class ApplicationThreadProxy implements IApplicationThread {
- private final IBinder mRemote;
-
- public ApplicationThreadProxy(IBinder remote) {
- mRemote = remote;
- }
-
- public final IBinder asBinder() {
- return mRemote;
- }
-
- public final void schedulePauseActivity(IBinder token, boolean finished,
- boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeInt(finished ? 1 : 0);
- data.writeInt(userLeaving ? 1 :0);
- data.writeInt(configChanges);
- data.writeInt(dontReport ? 1 : 0);
- mRemote.transact(SCHEDULE_PAUSE_ACTIVITY_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleStopActivity(IBinder token, boolean showWindow,
- int configChanges) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeInt(showWindow ? 1 : 0);
- data.writeInt(configChanges);
- mRemote.transact(SCHEDULE_STOP_ACTIVITY_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleWindowVisibility(IBinder token,
- boolean showWindow) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeInt(showWindow ? 1 : 0);
- mRemote.transact(SCHEDULE_WINDOW_VISIBILITY_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleSleeping(IBinder token,
- boolean sleeping) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeInt(sleeping ? 1 : 0);
- mRemote.transact(SCHEDULE_SLEEPING_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleResumeActivity(IBinder token, int procState, boolean isForward,
- Bundle resumeArgs)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeInt(procState);
- data.writeInt(isForward ? 1 : 0);
- data.writeBundle(resumeArgs);
- mRemote.transact(SCHEDULE_RESUME_ACTIVITY_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleSendResult(IBinder token, List<ResultInfo> results)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeTypedList(results);
- mRemote.transact(SCHEDULE_SEND_RESULT_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
- ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
- CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
- int procState, Bundle state, PersistableBundle persistentState,
- List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- intent.writeToParcel(data, 0);
- data.writeStrongBinder(token);
- data.writeInt(ident);
- info.writeToParcel(data, 0);
- curConfig.writeToParcel(data, 0);
- if (overrideConfig != null) {
- data.writeInt(1);
- overrideConfig.writeToParcel(data, 0);
- } else {
- data.writeInt(0);
- }
- compatInfo.writeToParcel(data, 0);
- data.writeString(referrer);
- data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
- data.writeInt(procState);
- data.writeBundle(state);
- data.writePersistableBundle(persistentState);
- data.writeTypedList(pendingResults);
- data.writeTypedList(pendingNewIntents);
- data.writeInt(notResumed ? 1 : 0);
- data.writeInt(isForward ? 1 : 0);
- if (profilerInfo != null) {
- data.writeInt(1);
- profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
- } else {
- data.writeInt(0);
- }
- mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleRelaunchActivity(IBinder token,
- List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig, boolean preserveWindow) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeTypedList(pendingResults);
- data.writeTypedList(pendingNewIntents);
- data.writeInt(configChanges);
- data.writeInt(notResumed ? 1 : 0);
- config.writeToParcel(data, 0);
- if (overrideConfig != null) {
- data.writeInt(1);
- overrideConfig.writeToParcel(data, 0);
- } else {
- data.writeInt(0);
- }
- data.writeInt(preserveWindow ? 1 : 0);
- mRemote.transact(SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token, boolean andPause)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeTypedList(intents);
- data.writeStrongBinder(token);
- data.writeInt(andPause ? 1 : 0);
- mRemote.transact(SCHEDULE_NEW_INTENT_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleDestroyActivity(IBinder token, boolean finishing,
- int configChanges) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeInt(finishing ? 1 : 0);
- data.writeInt(configChanges);
- mRemote.transact(SCHEDULE_FINISH_ACTIVITY_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleReceiver(Intent intent, ActivityInfo info,
- CompatibilityInfo compatInfo, int resultCode, String resultData,
- Bundle map, boolean sync, int sendingUser, int processState) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- intent.writeToParcel(data, 0);
- info.writeToParcel(data, 0);
- compatInfo.writeToParcel(data, 0);
- data.writeInt(resultCode);
- data.writeString(resultData);
- data.writeBundle(map);
- data.writeInt(sync ? 1 : 0);
- data.writeInt(sendingUser);
- data.writeInt(processState);
- mRemote.transact(SCHEDULE_RECEIVER_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleCreateBackupAgent(ApplicationInfo app,
- CompatibilityInfo compatInfo, int backupMode) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- app.writeToParcel(data, 0);
- compatInfo.writeToParcel(data, 0);
- data.writeInt(backupMode);
- mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleDestroyBackupAgent(ApplicationInfo app,
- CompatibilityInfo compatInfo) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- app.writeToParcel(data, 0);
- compatInfo.writeToParcel(data, 0);
- mRemote.transact(SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleCreateService(IBinder token, ServiceInfo info,
- CompatibilityInfo compatInfo, int processState) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- info.writeToParcel(data, 0);
- compatInfo.writeToParcel(data, 0);
- data.writeInt(processState);
- try {
- mRemote.transact(SCHEDULE_CREATE_SERVICE_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- } catch (TransactionTooLargeException e) {
- Log.e("CREATE_SERVICE", "Binder failure starting service; service=" + info);
- throw e;
- }
- data.recycle();
- }
-
- public final void scheduleBindService(IBinder token, Intent intent, boolean rebind,
- int processState) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- intent.writeToParcel(data, 0);
- data.writeInt(rebind ? 1 : 0);
- data.writeInt(processState);
- mRemote.transact(SCHEDULE_BIND_SERVICE_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleUnbindService(IBinder token, Intent intent)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- intent.writeToParcel(data, 0);
- mRemote.transact(SCHEDULE_UNBIND_SERVICE_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
- int flags, Intent args) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeInt(taskRemoved ? 1 : 0);
- data.writeInt(startId);
- data.writeInt(flags);
- if (args != null) {
- data.writeInt(1);
- args.writeToParcel(data, 0);
- } else {
- data.writeInt(0);
- }
- mRemote.transact(SCHEDULE_SERVICE_ARGS_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleStopService(IBinder token)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- mRemote.transact(SCHEDULE_STOP_SERVICE_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public final void bindApplication(String packageName, ApplicationInfo info,
- List<ProviderInfo> providers, ComponentName testName, ProfilerInfo profilerInfo,
- Bundle testArgs, IInstrumentationWatcher testWatcher,
- IUiAutomationConnection uiAutomationConnection, int debugMode,
- boolean enableBinderTracking, boolean trackAllocation, boolean restrictedBackupMode,
- boolean persistent, Configuration config, CompatibilityInfo compatInfo,
- Map<String, IBinder> services, Bundle coreSettings, String buildSerial)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeString(packageName);
- info.writeToParcel(data, 0);
- data.writeTypedList(providers);
- if (testName == null) {
- data.writeInt(0);
- } else {
- data.writeInt(1);
- testName.writeToParcel(data, 0);
- }
- if (profilerInfo != null) {
- data.writeInt(1);
- profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
- } else {
- data.writeInt(0);
- }
- data.writeBundle(testArgs);
- data.writeStrongInterface(testWatcher);
- data.writeStrongInterface(uiAutomationConnection);
- data.writeInt(debugMode);
- data.writeInt(enableBinderTracking ? 1 : 0);
- data.writeInt(trackAllocation ? 1 : 0);
- data.writeInt(restrictedBackupMode ? 1 : 0);
- data.writeInt(persistent ? 1 : 0);
- config.writeToParcel(data, 0);
- compatInfo.writeToParcel(data, 0);
- data.writeMap(services);
- data.writeBundle(coreSettings);
- data.writeString(buildSerial);
- mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleExit() throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- mRemote.transact(SCHEDULE_EXIT_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleSuicide() throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- mRemote.transact(SCHEDULE_SUICIDE_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleConfigurationChanged(Configuration config)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- config.writeToParcel(data, 0);
- mRemote.transact(SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public final void scheduleLocalVoiceInteractionStarted(IBinder token,
- IVoiceInteractor voiceInteractor) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
- mRemote.transact(SCHEDULE_LOCAL_VOICE_INTERACTION_STARTED_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void updateTimeZone() throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- mRemote.transact(UPDATE_TIME_ZONE_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void clearDnsCache() throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- mRemote.transact(CLEAR_DNS_CACHE_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void setHttpProxy(String proxy, String port, String exclList,
- Uri pacFileUrl) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeString(proxy);
- data.writeString(port);
- data.writeString(exclList);
- pacFileUrl.writeToParcel(data, 0);
- mRemote.transact(SET_HTTP_PROXY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void processInBackground() throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- mRemote.transact(PROCESS_IN_BACKGROUND_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void dumpService(FileDescriptor fd, IBinder token, String[] args)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeFileDescriptor(fd);
- data.writeStrongBinder(token);
- data.writeStringArray(args);
- mRemote.transact(DUMP_SERVICE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void dumpProvider(FileDescriptor fd, IBinder token, String[] args)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeFileDescriptor(fd);
- data.writeStrongBinder(token);
- data.writeStringArray(args);
- mRemote.transact(DUMP_PROVIDER_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
- int resultCode, String dataStr, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser, int processState) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(receiver.asBinder());
- intent.writeToParcel(data, 0);
- data.writeInt(resultCode);
- data.writeString(dataStr);
- data.writeBundle(extras);
- data.writeInt(ordered ? 1 : 0);
- data.writeInt(sticky ? 1 : 0);
- data.writeInt(sendingUser);
- data.writeInt(processState);
- mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public final void scheduleLowMemory() throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- mRemote.transact(SCHEDULE_LOW_MEMORY_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public final void scheduleActivityConfigurationChanged(IBinder token,
- Configuration overrideConfig, boolean reportToActivity) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- if (overrideConfig != null) {
- data.writeInt(1);
- overrideConfig.writeToParcel(data, 0);
- } else {
- data.writeInt(0);
- }
- data.writeInt(reportToActivity ? 1 : 0);
- mRemote.transact(SCHEDULE_ACTIVITY_CONFIGURATION_CHANGED_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void profilerControl(boolean start, ProfilerInfo profilerInfo, int profileType)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeInt(start ? 1 : 0);
- data.writeInt(profileType);
- if (profilerInfo != null) {
- data.writeInt(1);
- profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
- } else {
- data.writeInt(0);
- }
- mRemote.transact(PROFILER_CONTROL_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void setSchedulingGroup(int group) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeInt(group);
- mRemote.transact(SET_SCHEDULING_GROUP_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void dispatchPackageBroadcast(int cmd, String[] packages) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeInt(cmd);
- data.writeStringArray(packages);
- mRemote.transact(DISPATCH_PACKAGE_BROADCAST_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void scheduleCrash(String msg) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeString(msg);
- mRemote.transact(SCHEDULE_CRASH_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void dumpHeap(boolean managed, String path,
- ParcelFileDescriptor fd) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeInt(managed ? 1 : 0);
- data.writeString(path);
- if (fd != null) {
- data.writeInt(1);
- fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
- } else {
- data.writeInt(0);
- }
- mRemote.transact(DUMP_HEAP_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void dumpActivity(FileDescriptor fd, IBinder token, String prefix, String[] args)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeFileDescriptor(fd);
- data.writeStrongBinder(token);
- data.writeString(prefix);
- data.writeStringArray(args);
- mRemote.transact(DUMP_ACTIVITY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void setCoreSettings(Bundle coreSettings) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeBundle(coreSettings);
- mRemote.transact(SET_CORE_SETTINGS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- }
-
- public void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeString(pkg);
- info.writeToParcel(data, 0);
- mRemote.transact(UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- }
-
- public void scheduleTrimMemory(int level) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeInt(level);
- mRemote.transact(SCHEDULE_TRIM_MEMORY_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin,
- boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
- boolean dumpUnreachable, String[] args) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeFileDescriptor(fd);
- mem.writeToParcel(data, 0);
- data.writeInt(checkin ? 1 : 0);
- data.writeInt(dumpInfo ? 1 : 0);
- data.writeInt(dumpDalvik ? 1 : 0);
- data.writeInt(dumpSummaryOnly ? 1 : 0);
- data.writeInt(dumpUnreachable ? 1 : 0);
- data.writeStringArray(args);
- mRemote.transact(DUMP_MEM_INFO_TRANSACTION, data, reply, 0);
- reply.readException();
- data.recycle();
- reply.recycle();
- }
-
- public void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeFileDescriptor(fd);
- data.writeStringArray(args);
- mRemote.transact(DUMP_GFX_INFO_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- public void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeFileDescriptor(fd);
- data.writeStringArray(args);
- mRemote.transact(DUMP_DB_INFO_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void unstableProviderDied(IBinder provider) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(provider);
- mRemote.transact(UNSTABLE_PROVIDER_DIED_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void requestAssistContextExtras(IBinder activityToken, IBinder requestToken,
- int requestType, int sessionId) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(activityToken);
- data.writeStrongBinder(requestToken);
- data.writeInt(requestType);
- data.writeInt(sessionId);
- mRemote.transact(REQUEST_ASSIST_CONTEXT_EXTRAS_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeInt(timeout ? 1 : 0);
- mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void scheduleOnNewActivityOptions(IBinder token, ActivityOptions options)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeBundle(options == null ? null : options.toBundle());
- mRemote.transact(SCHEDULE_ON_NEW_ACTIVITY_OPTIONS_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void setProcessState(int state) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeInt(state);
- mRemote.transact(SET_PROCESS_STATE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void scheduleInstallProvider(ProviderInfo provider) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- provider.writeToParcel(data, 0);
- mRemote.transact(SCHEDULE_INSTALL_PROVIDER_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void updateTimePrefs(boolean is24Hour) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeByte(is24Hour ? (byte) 1 : (byte) 0);
- mRemote.transact(UPDATE_TIME_PREFS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void scheduleCancelVisibleBehind(IBinder token) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- mRemote.transact(CANCEL_VISIBLE_BEHIND_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void scheduleBackgroundVisibleBehindChanged(IBinder token, boolean enabled)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeInt(enabled ? 1 : 0);
- mRemote.transact(BACKGROUND_VISIBLE_BEHIND_CHANGED_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void scheduleEnterAnimationComplete(IBinder token) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- mRemote.transact(ENTER_ANIMATION_COMPLETE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void notifyCleartextNetwork(byte[] firstPacket) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeByteArray(firstPacket);
- mRemote.transact(NOTIFY_CLEARTEXT_NETWORK_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void startBinderTracking() throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- mRemote.transact(START_BINDER_TRACKING_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void stopBinderTrackingAndDump(FileDescriptor fd) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeFileDescriptor(fd);
- mRemote.transact(STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public final void scheduleMultiWindowModeChanged(
- IBinder token, boolean isInMultiWindowMode) throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeInt(isInMultiWindowMode ? 1 : 0);
- mRemote.transact(SCHEDULE_MULTI_WINDOW_CHANGED_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public final void schedulePictureInPictureModeChanged(IBinder token, boolean isInPipMode)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- data.writeInt(isInPipMode ? 1 : 0);
- mRemote.transact(SCHEDULE_PICTURE_IN_PICTURE_CHANGED_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
- @Override
- public void handleTrustStorageUpdate() throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- mRemote.transact(HANDLE_TRUST_STORAGE_UPDATE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-}
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index a4b1a1f..cf794c5 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -16,21 +16,13 @@
package android.app;
-import android.graphics.Rect;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.transition.Transition;
-import android.transition.TransitionManager;
-import android.transition.TransitionSet;
-import android.util.ArrayMap;
import android.util.Log;
import android.util.LogWriter;
-import android.util.SparseArray;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import com.android.internal.util.FastPrintWriter;
@@ -38,7 +30,6 @@
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
-import java.util.List;
final class BackStackState implements Parcelable {
final int[] mOps;
@@ -52,6 +43,7 @@
final CharSequence mBreadCrumbShortTitleText;
final ArrayList<String> mSharedElementSourceNames;
final ArrayList<String> mSharedElementTargetNames;
+ final boolean mAllowOptimization;
public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
final int numOps = bse.mOps.size();
@@ -81,6 +73,7 @@
mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
mSharedElementSourceNames = bse.mSharedElementSourceNames;
mSharedElementTargetNames = bse.mSharedElementTargetNames;
+ mAllowOptimization = bse.mAllowOptimization;
}
public BackStackState(Parcel in) {
@@ -95,6 +88,7 @@
mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mSharedElementSourceNames = in.createStringArrayList();
mSharedElementTargetNames = in.createStringArrayList();
+ mAllowOptimization = in.readInt() != 0;
}
public BackStackRecord instantiate(FragmentManagerImpl fm) {
@@ -137,6 +131,7 @@
bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
bse.mSharedElementSourceNames = mSharedElementSourceNames;
bse.mSharedElementTargetNames = mSharedElementTargetNames;
+ bse.mAllowOptimization = mAllowOptimization;
bse.bumpBackStackNesting(1);
return bse;
}
@@ -157,6 +152,7 @@
TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
dest.writeStringList(mSharedElementSourceNames);
dest.writeStringList(mSharedElementTargetNames);
+ dest.writeInt(mAllowOptimization ? 1 : 0);
}
public static final Parcelable.Creator<BackStackState> CREATOR
@@ -175,7 +171,7 @@
* @hide Entry of an operation on the fragment back stack.
*/
final class BackStackRecord extends FragmentTransaction implements
- FragmentManager.BackStackEntry, Runnable {
+ FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
static final String TAG = FragmentManagerImpl.TAG;
final FragmentManagerImpl mManager;
@@ -210,6 +206,7 @@
String mName;
boolean mCommitted;
int mIndex = -1;
+ boolean mAllowOptimization;
int mBreadCrumbTitleRes;
CharSequence mBreadCrumbTitleText;
@@ -352,6 +349,7 @@
public BackStackRecord(FragmentManagerImpl manager) {
mManager = manager;
+ mAllowOptimization = Build.isAtLeastO();
}
public int getId() {
@@ -633,6 +631,12 @@
mManager.execSingleAction(this, true);
}
+ @Override
+ public FragmentTransaction setAllowOptimization(boolean allowOptimization) {
+ mAllowOptimization = allowOptimization;
+ return this;
+ }
+
int commitInternal(boolean allowStateLoss) {
if (mCommitted) {
throw new IllegalStateException("commit already called");
@@ -654,94 +658,177 @@
return mIndex;
}
- public void run() {
+ /**
+ * Implementation of {@link android.app.FragmentManagerImpl.OpGenerator}.
+ * This operation is added to the list of pending actions during {@link #commit()}, and
+ * will be executed on the UI thread to run this FragmentTransaction.
+ *
+ * @param records Modified to add this BackStackRecord
+ * @param isRecordPop Modified to add a false (this isn't a pop)
+ * @return true always because the records and isRecordPop will always be changed
+ */
+ @Override
+ public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Run: " + this);
}
+ records.add(this);
+ isRecordPop.add(false);
if (mAddToBackStack) {
- if (mIndex < 0) {
- throw new IllegalStateException("addToBackStack() called after commit()");
- }
+ mManager.addBackStackState(this);
}
+ return true;
+ }
- expandReplaceOps();
- bumpBackStackNesting(1);
-
- if (mManager.mCurState >= Fragment.CREATED) {
- SparseArray<FragmentContainerTransition> transitioningFragments = new SparseArray<>();
- calculateFragments(transitioningFragments);
- beginTransition(transitioningFragments);
- }
-
+ boolean interactsWith(int containerId) {
final int numOps = mOps.size();
for (int opNum = 0; opNum < numOps; opNum++) {
final Op op = mOps.get(opNum);
- Fragment f = op.fragment;
+ if (op.fragment.mContainerId == containerId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean interactsWith(ArrayList<BackStackRecord> records, int startIndex, int endIndex) {
+ if (endIndex == startIndex) {
+ return false;
+ }
+ final int numOps = mOps.size();
+ int lastContainer = -1;
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ final Op op = mOps.get(opNum);
+ final int container = op.fragment.mContainerId;
+ if (container != 0 && container != lastContainer) {
+ lastContainer = container;
+ for (int i = startIndex; i < endIndex; i++) {
+ BackStackRecord record = records.get(i);
+ final int numThoseOps = record.mOps.size();
+ for (int thoseOpIndex = 0; thoseOpIndex < numThoseOps; thoseOpIndex++) {
+ final Op thatOp = record.mOps.get(thoseOpIndex);
+ if (thatOp.fragment.mContainerId == container) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Executes the operations contained within this transaction. The Fragment states will only
+ * be modified if optimizations are not allowed.
+ */
+ void executeOps() {
+ final int numOps = mOps.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ final Op op = mOps.get(opNum);
+ final Fragment f = op.fragment;
+ f.setNextTransition(mTransition, mTransitionStyle);
switch (op.cmd) {
case OP_ADD:
- f.mNextAnim = op.enterAnim;
+ f.setNextAnim(op.enterAnim);
mManager.addFragment(f, false);
break;
case OP_REMOVE:
- f.mNextAnim = op.exitAnim;
- mManager.removeFragment(f, mTransition, mTransitionStyle);
+ f.setNextAnim(op.exitAnim);
+ mManager.removeFragment(f);
break;
case OP_HIDE:
- f.mNextAnim = op.exitAnim;
- mManager.hideFragment(f, mTransition, mTransitionStyle);
+ f.setNextAnim(op.exitAnim);
+ mManager.hideFragment(f);
break;
case OP_SHOW:
- f.mNextAnim = op.enterAnim;
- mManager.showFragment(f, mTransition, mTransitionStyle);
+ f.setNextAnim(op.enterAnim);
+ mManager.showFragment(f);
break;
case OP_DETACH:
- f.mNextAnim = op.exitAnim;
- mManager.detachFragment(f, mTransition, mTransitionStyle);
+ f.setNextAnim(op.exitAnim);
+ mManager.detachFragment(f);
break;
case OP_ATTACH:
- f.mNextAnim = op.enterAnim;
- mManager.attachFragment(f, mTransition, mTransitionStyle);
+ f.setNextAnim(op.enterAnim);
+ mManager.attachFragment(f);
break;
default:
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
+ if (!mAllowOptimization && op.cmd != OP_ADD) {
+ mManager.moveFragmentToExpectedState(f);
+ }
}
-
- mManager.moveToState(mManager.mCurState, mTransition,
- mTransitionStyle, true);
-
- if (mAddToBackStack) {
- mManager.addBackStackState(this);
+ if (!mAllowOptimization) {
+ // Added fragments are added at the end to comply with prior behavior.
+ mManager.moveToState(mManager.mCurState);
}
}
- private void expandReplaceOps() {
- final int numOps = mOps.size();
-
- boolean hasReplace = false;
- // Before we do anything, check to see if any replace operations exist:
- for (int opNum = 0; opNum < numOps; opNum++) {
+ /**
+ * Reverses the execution of the operations within this transaction. The Fragment states will
+ * only be modified if optimizations are not allowed.
+ */
+ void executePopOps() {
+ for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) {
final Op op = mOps.get(opNum);
- if (op.cmd == OP_REPLACE) {
- hasReplace = true;
- break;
+ Fragment f = op.fragment;
+ f.setNextTransition(FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
+ switch (op.cmd) {
+ case OP_ADD:
+ f.setNextAnim(op.popExitAnim);
+ mManager.removeFragment(f);
+ break;
+ case OP_REMOVE:
+ f.setNextAnim(op.popEnterAnim);
+ mManager.addFragment(f, false);
+ break;
+ case OP_HIDE:
+ f.setNextAnim(op.popEnterAnim);
+ mManager.showFragment(f);
+ break;
+ case OP_SHOW:
+ f.setNextAnim(op.popExitAnim);
+ mManager.hideFragment(f);
+ break;
+ case OP_DETACH:
+ f.setNextAnim(op.popEnterAnim);
+ mManager.attachFragment(f);
+ break;
+ case OP_ATTACH:
+ f.setNextAnim(op.popExitAnim);
+ mManager.detachFragment(f);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
+ }
+ if (!mAllowOptimization && op.cmd != OP_ADD) {
+ mManager.moveFragmentToExpectedState(f);
}
}
-
- if (!hasReplace) {
- return; // nothing to expand
+ if (!mAllowOptimization) {
+ mManager.moveToState(mManager.mCurState);
}
+ }
- ArrayList<Fragment> added = (mManager.mAdded == null) ? new ArrayList<Fragment>() :
- new ArrayList<>(mManager.mAdded);
+ /**
+ * Removes all OP_REPLACE ops and replaces them with the proper add and remove
+ * operations that are equivalent to the replace. This must be called prior to
+ * {@link #executeOps()} or any other call that operations on mOps.
+ *
+ * @param added Initialized to the fragments that are in the mManager.mAdded, this
+ * will be modified to contain the fragments that will be in mAdded
+ * after the execution ({@link #executeOps()}.
+ */
+ void expandReplaceOps(ArrayList<Fragment> added) {
for (int opNum = 0; opNum < mOps.size(); opNum++) {
final Op op = mOps.get(opNum);
switch (op.cmd) {
case OP_ADD:
case OP_ATTACH:
added.add(op.fragment);
- break;
+ break;
case OP_REMOVE:
case OP_DETACH:
added.remove(op.fragment);
@@ -782,920 +869,29 @@
}
}
- private static void setFirstOut(SparseArray<FragmentContainerTransition> transitioningFragments,
- Fragment fragment, boolean isPop) {
- if (fragment != null) {
- int containerId = fragment.mContainerId;
- if (containerId != 0 && !fragment.isHidden()) {
- FragmentContainerTransition fragments = transitioningFragments.get(containerId);
- if (fragment.isAdded() && fragment.getView() != null && (fragments == null ||
- fragments.firstOut == null)) {
- if (fragments == null) {
- fragments = new FragmentContainerTransition();
- transitioningFragments.put(containerId, fragments);
- }
- fragments.firstOut = fragment;
- fragments.firstOutIsPop = isPop;
- }
- if (fragments != null && fragments.lastIn == fragment) {
- fragments.lastIn = null;
- }
- }
- }
- }
-
- private void setLastIn(SparseArray<FragmentContainerTransition> transitioningFragments,
- Fragment fragment, boolean isPop) {
- if (fragment != null) {
- int containerId = fragment.mContainerId;
- if (containerId != 0) {
- FragmentContainerTransition fragments = transitioningFragments.get(containerId);
- if (!fragment.isAdded()) {
- if (fragments == null) {
- fragments = new FragmentContainerTransition();
- transitioningFragments.put(containerId, fragments);
- }
- fragments.lastIn = fragment;
- fragments.lastInIsPop = isPop;
- }
- if (fragments != null && fragments.firstOut == fragment) {
- fragments.firstOut = null;
- }
- }
- /**
- * Ensure that fragments that are entering are at least at the CREATED state
- * so that they may load Transitions using TransitionInflater.
- */
- if (fragment.mState < Fragment.CREATED && mManager.mCurState >= Fragment.CREATED &&
- mManager.mHost.getContext().getApplicationInfo().targetSdkVersion >=
- Build.VERSION_CODES.N) {
- mManager.makeActive(fragment);
- mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
- }
- }
- }
-
- /**
- * Finds the first removed fragment and last added fragments when going forward.
- * If none of the fragments have transitions, then both lists will be empty.
- *
- * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
- * and last fragments to be added. This will be modified by
- * this method.
- */
- private void calculateFragments(
- SparseArray<FragmentContainerTransition> transitioningFragments) {
- if (!mManager.mContainer.onHasView()) {
- return; // nothing to see, so no transitions
- }
- final int numOps = mOps.size();
- for (int opNum = 0; opNum < numOps; opNum++) {
+ boolean isPostponed() {
+ for (int opNum = 0; opNum < mOps.size(); opNum++) {
final Op op = mOps.get(opNum);
- switch (op.cmd) {
- case OP_ADD:
- case OP_SHOW:
- case OP_ATTACH:
- setLastIn(transitioningFragments, op.fragment, false);
- break;
- case OP_REMOVE:
- case OP_HIDE:
- case OP_DETACH:
- setFirstOut(transitioningFragments, op.fragment, false);
- break;
- }
- }
- }
-
- /**
- * Finds the first removed fragment and last added fragments when popping the back stack.
- * If none of the fragments have transitions, then both lists will be empty.
- *
- * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
- * and last fragments to be added. This will be modified by
- * this method.
- */
- public void calculateBackFragments(
- SparseArray<FragmentContainerTransition> transitioningFragments) {
- if (!mManager.mContainer.onHasView()) {
- return; // nothing to see, so no transitions
- }
- final int numOps = mOps.size();
- for (int opNum = numOps - 1; opNum >= 0; opNum--) {
- final Op op = mOps.get(opNum);
- switch (op.cmd) {
- case OP_ADD:
- case OP_SHOW:
- case OP_ATTACH:
- setFirstOut(transitioningFragments, op.fragment, true);
- break;
- case OP_REMOVE:
- case OP_HIDE:
- case OP_DETACH:
- setLastIn(transitioningFragments, op.fragment, true);
- break;
- }
- }
- }
-
- /**
- * When custom fragment transitions are used, this sets up the state for each transition
- * and begins the transition. A different transition is started for each fragment container
- * and consists of up to 3 different transitions: the exit transition, a shared element
- * transition and an enter transition.
- *
- * <p>The exit transition operates against the leaf nodes of the first fragment
- * with a view that was removed. If no such fragment was removed, then no exit
- * transition is executed. The exit transition comes from the outgoing fragment.</p>
- *
- * <p>The enter transition operates against the last fragment that was added. If
- * that fragment does not have a view or no fragment was added, then no enter
- * transition is executed. The enter transition comes from the incoming fragment.</p>
- *
- * <p>The shared element transition operates against all views and comes either
- * from the outgoing fragment or the incoming fragment, depending on whether this
- * is going forward or popping the back stack. When going forward, the incoming
- * fragment's enter shared element transition is used, but when going back, the
- * outgoing fragment's return shared element transition is used. Shared element
- * transitions only operate if there is both an incoming and outgoing fragment.</p>
- *
- * @param containers The first in and last out fragments that are transitioning.
- * @return The TransitionState used to complete the operation of the transition
- * in {@link #setNameOverrides(android.app.BackStackRecord.TransitionState, java.util.ArrayList,
- * java.util.ArrayList)}.
- */
- private TransitionState beginTransition(SparseArray<FragmentContainerTransition> containers) {
- TransitionState state = new TransitionState();
-
- // Adding a non-existent target view makes sure that the transitions don't target
- // any views by default. They'll only target the views we tell add. If we don't
- // add any, then no views will be targeted.
- state.nonExistentView = new View(mManager.mHost.getContext());
-
- final int numContainers = containers.size();
- for (int i = 0; i < numContainers; i++) {
- int containerId = containers.keyAt(i);
- FragmentContainerTransition containerTransition = containers.valueAt(i);
- configureTransitions(containerId, state, containerTransition);
- }
- return state;
- }
-
- private static Transition cloneTransition(Transition transition) {
- if (transition != null) {
- transition = transition.clone();
- }
- return transition;
- }
-
- private static Transition getEnterTransition(Fragment inFragment, boolean isBack) {
- if (inFragment == null) {
- return null;
- }
- return cloneTransition(isBack ? inFragment.getReenterTransition() :
- inFragment.getEnterTransition());
- }
-
- private static Transition getExitTransition(Fragment outFragment, boolean isBack) {
- if (outFragment == null) {
- return null;
- }
- return cloneTransition(isBack ? outFragment.getReturnTransition() :
- outFragment.getExitTransition());
- }
-
- private static TransitionSet getSharedElementTransition(Fragment inFragment,
- Fragment outFragment, boolean isBack) {
- if (inFragment == null || outFragment == null) {
- return null;
- }
- Transition transition = cloneTransition(isBack
- ? outFragment.getSharedElementReturnTransition()
- : inFragment.getSharedElementEnterTransition());
- if (transition == null) {
- return null;
- }
- TransitionSet transitionSet = new TransitionSet();
- transitionSet.addTransition(transition);
- return transitionSet;
- }
-
- private static ArrayList<View> captureExitingViews(Transition exitTransition,
- Fragment outFragment, ArrayMap<String, View> namedViews, View nonExistentView) {
- ArrayList<View> viewList = null;
- if (exitTransition != null) {
- viewList = new ArrayList<View>();
- View root = outFragment.getView();
- root.captureTransitioningViews(viewList);
- if (namedViews != null) {
- viewList.removeAll(namedViews.values());
- }
- if (!viewList.isEmpty()) {
- viewList.add(nonExistentView);
- addTargets(exitTransition, viewList);
- }
- }
- return viewList;
- }
-
- private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment,
- boolean isBack) {
- ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
- if (mSharedElementSourceNames != null) {
- outFragment.getView().findNamedViews(namedViews);
- if (isBack) {
- namedViews.retainAll(mSharedElementTargetNames);
- } else {
- namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames,
- namedViews);
- }
- }
-
- if (isBack) {
- outFragment.mEnterTransitionCallback.onMapSharedElements(
- mSharedElementTargetNames, namedViews);
- setBackNameOverrides(state, namedViews, false);
- } else {
- outFragment.mExitTransitionCallback.onMapSharedElements(
- mSharedElementTargetNames, namedViews);
- setNameOverrides(state, namedViews, false);
- }
-
- return namedViews;
- }
-
- /**
- * Prepares the enter transition by adding a non-existent view to the transition's target list
- * and setting it epicenter callback. By adding a non-existent view to the target list,
- * we can prevent any view from being targeted at the beginning of the transition.
- * We will add to the views before the end state of the transition is captured so that the
- * views will appear. At the start of the transition, we clear the list of targets so that
- * we can restore the state of the transition and use it again.
- *
- * <p>The shared element transition maps its shared elements immediately prior to
- * capturing the final state of the Transition.</p>
- */
- private ArrayList<View> addTransitionTargets(final TransitionState state,
- final Transition enterTransition, final TransitionSet sharedElementTransition,
- final Transition exitTransition, final Transition overallTransition,
- final View container, final Fragment inFragment, final Fragment outFragment,
- final ArrayList<View> hiddenFragmentViews, final boolean isBack,
- final ArrayList<View> sharedElementTargets) {
- if (enterTransition == null && sharedElementTransition == null &&
- overallTransition == null) {
- return null;
- }
- final ArrayList<View> enteringViews = new ArrayList<View>();
- container.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- container.getViewTreeObserver().removeOnPreDrawListener(this);
-
- // Don't include any newly-hidden fragments in the transition.
- if (inFragment != null) {
- excludeHiddenFragments(hiddenFragmentViews, inFragment.mContainerId,
- overallTransition);
- }
-
- ArrayMap<String, View> namedViews = null;
- if (sharedElementTransition != null) {
- namedViews = mapSharedElementsIn(state, isBack, inFragment);
- removeTargets(sharedElementTransition, sharedElementTargets);
- // keep the nonExistentView as excluded so the list doesn't get emptied
- sharedElementTargets.remove(state.nonExistentView);
- excludeViews(exitTransition, sharedElementTransition,
- sharedElementTargets, false);
- excludeViews(enterTransition, sharedElementTransition,
- sharedElementTargets, false);
-
- setSharedElementTargets(sharedElementTransition,
- state.nonExistentView, namedViews, sharedElementTargets);
-
- setEpicenterIn(namedViews, state);
-
- callSharedElementEnd(state, inFragment, outFragment, isBack,
- namedViews);
- }
-
- if (enterTransition != null) {
- enterTransition.removeTarget(state.nonExistentView);
- View view = inFragment.getView();
- if (view != null) {
- view.captureTransitioningViews(enteringViews);
- if (namedViews != null) {
- enteringViews.removeAll(namedViews.values());
- }
- enteringViews.add(state.nonExistentView);
- // We added this earlier to prevent any views being targeted.
- addTargets(enterTransition, enteringViews);
- }
- setSharedElementEpicenter(enterTransition, state);
- }
-
- excludeViews(exitTransition, enterTransition, enteringViews, true);
- excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
- true);
- excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
- true);
- return true;
- }
- });
- return enteringViews;
- }
-
- private void callSharedElementEnd(TransitionState state, Fragment inFragment,
- Fragment outFragment, boolean isBack, ArrayMap<String, View> namedViews) {
- SharedElementCallback sharedElementCallback = isBack ?
- outFragment.mEnterTransitionCallback :
- inFragment.mEnterTransitionCallback;
- ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
- ArrayList<View> views = new ArrayList<View>(namedViews.values());
- sharedElementCallback.onSharedElementEnd(names, views, null);
- }
-
- private void setEpicenterIn(ArrayMap<String, View> namedViews, TransitionState state) {
- if (mSharedElementTargetNames != null && !namedViews.isEmpty()) {
- // now we know the epicenter of the entering transition.
- View epicenter = namedViews
- .get(mSharedElementTargetNames.get(0));
- if (epicenter != null) {
- state.enteringEpicenterView = epicenter;
- }
- }
- }
-
- private ArrayMap<String, View> mapSharedElementsIn(TransitionState state,
- boolean isBack, Fragment inFragment) {
- // Now map the shared elements in the incoming fragment
- ArrayMap<String, View> namedViews = mapEnteringSharedElements(state, inFragment, isBack);
-
- // remap shared elements and set the name mapping used
- // in the shared element transition.
- if (isBack) {
- inFragment.mExitTransitionCallback.onMapSharedElements(
- mSharedElementTargetNames, namedViews);
- setBackNameOverrides(state, namedViews, true);
- } else {
- inFragment.mEnterTransitionCallback.onMapSharedElements(
- mSharedElementTargetNames, namedViews);
- setNameOverrides(state, namedViews, true);
- }
- return namedViews;
- }
-
- private static Transition mergeTransitions(Transition enterTransition,
- Transition exitTransition, Transition sharedElementTransition, Fragment inFragment,
- boolean isBack) {
- boolean overlap = true;
- if (enterTransition != null && exitTransition != null && inFragment != null) {
- overlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
- inFragment.getAllowEnterTransitionOverlap();
- }
-
- // Wrap the transitions. Explicit targets like in enter and exit will cause the
- // views to be targeted regardless of excluded views. If that happens, then the
- // excluded fragments views (hidden fragments) will still be in the transition.
-
- Transition transition;
- if (overlap) {
- // Regular transition -- do it all together
- TransitionSet transitionSet = new TransitionSet();
- if (enterTransition != null) {
- transitionSet.addTransition(enterTransition);
- }
- if (exitTransition != null) {
- transitionSet.addTransition(exitTransition);
- }
- if (sharedElementTransition != null) {
- transitionSet.addTransition(sharedElementTransition);
- }
- transition = transitionSet;
- } else {
- // First do exit, then enter, but allow shared element transition to happen
- // during both.
- Transition staggered = null;
- if (exitTransition != null && enterTransition != null) {
- staggered = new TransitionSet()
- .addTransition(exitTransition)
- .addTransition(enterTransition)
- .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
- } else if (exitTransition != null) {
- staggered = exitTransition;
- } else if (enterTransition != null) {
- staggered = enterTransition;
- }
- if (sharedElementTransition != null) {
- TransitionSet together = new TransitionSet();
- if (staggered != null) {
- together.addTransition(staggered);
- }
- together.addTransition(sharedElementTransition);
- transition = together;
- } else {
- transition = staggered;
- }
- }
- return transition;
- }
-
- /**
- * Configures custom transitions for a specific fragment container.
- *
- * @param containerId The container ID of the fragments to configure the transition for.
- * @param state The Transition State keeping track of the executing transitions.
- * @param transitioningFragments The first out and last in fragments for the fragment container.
- */
- private void configureTransitions(int containerId, TransitionState state,
- FragmentContainerTransition transitioningFragments) {
- ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.onFindViewById(containerId);
- if (sceneRoot != null) {
- final Fragment inFragment = transitioningFragments.lastIn;
- final Fragment outFragment = transitioningFragments.firstOut;
-
- Transition enterTransition =
- getEnterTransition(inFragment, transitioningFragments.lastInIsPop);
- TransitionSet sharedElementTransition = getSharedElementTransition(inFragment,
- outFragment, transitioningFragments.lastInIsPop);
- Transition exitTransition =
- getExitTransition(outFragment, transitioningFragments.firstOutIsPop);
-
- if (enterTransition == null && sharedElementTransition == null &&
- exitTransition == null) {
- return; // no transitions!
- }
- if (enterTransition != null) {
- enterTransition.addTarget(state.nonExistentView);
- }
- ArrayMap<String, View> namedViews = null;
- ArrayList<View> sharedElementTargets = new ArrayList<View>();
- if (sharedElementTransition != null) {
- namedViews = remapSharedElements(state, outFragment,
- transitioningFragments.firstOutIsPop);
- setSharedElementTargets(sharedElementTransition,
- state.nonExistentView, namedViews, sharedElementTargets);
-
- // Notify the start of the transition.
- SharedElementCallback callback = transitioningFragments.lastInIsPop ?
- outFragment.mEnterTransitionCallback :
- inFragment.mEnterTransitionCallback;
- ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
- ArrayList<View> views = new ArrayList<View>(namedViews.values());
- callback.onSharedElementStart(names, views, null);
- }
-
- ArrayList<View> exitingViews = captureExitingViews(exitTransition, outFragment,
- namedViews, state.nonExistentView);
- if (exitingViews == null || exitingViews.isEmpty()) {
- exitTransition = null;
- }
- excludeViews(enterTransition, exitTransition, exitingViews, true);
- excludeViews(enterTransition, sharedElementTransition, sharedElementTargets, true);
- excludeViews(exitTransition, sharedElementTransition, sharedElementTargets, true);
-
- // Set the epicenter of the exit transition
- if (mSharedElementTargetNames != null && namedViews != null) {
- View epicenterView = namedViews.get(mSharedElementTargetNames.get(0));
- if (epicenterView != null) {
- if (exitTransition != null) {
- setEpicenter(exitTransition, epicenterView);
- }
- if (sharedElementTransition != null) {
- setEpicenter(sharedElementTransition, epicenterView);
- }
- }
- }
-
- Transition transition = mergeTransitions(enterTransition, exitTransition,
- sharedElementTransition, inFragment, transitioningFragments.lastInIsPop);
-
- if (transition != null) {
- ArrayList<View> hiddenFragments = new ArrayList<View>();
- ArrayList<View> enteringViews = addTransitionTargets(state, enterTransition,
- sharedElementTransition, exitTransition, transition, sceneRoot, inFragment,
- outFragment, hiddenFragments, transitioningFragments.lastInIsPop,
- sharedElementTargets);
-
- transition.setNameOverrides(state.nameOverrides);
- // We want to exclude hidden views later, so we need a non-null list in the
- // transition now.
- transition.excludeTarget(state.nonExistentView, true);
- // Now exclude all currently hidden fragments.
- excludeHiddenFragments(hiddenFragments, containerId, transition);
- TransitionManager.beginDelayedTransition(sceneRoot, transition);
- // Remove the view targeting after the transition starts
- removeTargetedViewsFromTransitions(sceneRoot, state.nonExistentView,
- enterTransition, enteringViews, exitTransition, exitingViews,
- sharedElementTransition, sharedElementTargets, transition,
- hiddenFragments);
- }
- }
- }
-
- /**
- * Finds all children of the shared elements and sets the wrapping TransitionSet
- * targets to point to those. It also limits transitions that have no targets to the
- * specific shared elements. This allows developers to target child views of the
- * shared elements specifically, but this doesn't happen by default.
- */
- private static void setSharedElementTargets(TransitionSet transition,
- View nonExistentView, ArrayMap<String, View> namedViews,
- ArrayList<View> sharedElementTargets) {
- sharedElementTargets.clear();
- sharedElementTargets.addAll(namedViews.values());
-
- final List<View> views = transition.getTargets();
- views.clear();
- final int count = sharedElementTargets.size();
- for (int i = 0; i < count; i++) {
- final View view = sharedElementTargets.get(i);
- bfsAddViewChildren(views, view);
- }
- sharedElementTargets.add(nonExistentView);
- addTargets(transition, sharedElementTargets);
- }
-
- /**
- * Uses a breadth-first scheme to add startView and all of its children to views.
- * It won't add a child if it is already in views.
- */
- private static void bfsAddViewChildren(final List<View> views, final View startView) {
- final int startIndex = views.size();
- if (containedBeforeIndex(views, startView, startIndex)) {
- return; // This child is already in the list, so all its children are also.
- }
- views.add(startView);
- for (int index = startIndex; index < views.size(); index++) {
- final View view = views.get(index);
- if (view instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) view;
- final int childCount = viewGroup.getChildCount();
- for (int childIndex = 0; childIndex < childCount; childIndex++) {
- final View child = viewGroup.getChildAt(childIndex);
- if (!containedBeforeIndex(views, child, startIndex)) {
- views.add(child);
- }
- }
- }
- }
- }
-
- /**
- * Does a linear search through views for view, limited to maxIndex.
- */
- private static boolean containedBeforeIndex(final List<View> views, final View view,
- final int maxIndex) {
- for (int i = 0; i < maxIndex; i++) {
- if (views.get(i) == view) {
+ if (isFragmentPostponed(op)) {
return true;
}
}
return false;
}
- private static void excludeViews(Transition transition, Transition fromTransition,
- ArrayList<View> views, boolean exclude) {
- if (transition != null) {
- final int viewCount = fromTransition == null ? 0 : views.size();
- for (int i = 0; i < viewCount; i++) {
- transition.excludeTarget(views.get(i), exclude);
- }
- }
- }
-
- /**
- * After the transition has started, remove all targets that we added to the transitions
- * so that the transitions are left in a clean state.
- */
- private void removeTargetedViewsFromTransitions(
- final ViewGroup sceneRoot, final View nonExistingView,
- final Transition enterTransition, final ArrayList<View> enteringViews,
- final Transition exitTransition, final ArrayList<View> exitingViews,
- final Transition sharedElementTransition, final ArrayList<View> sharedElementTargets,
- final Transition overallTransition, final ArrayList<View> hiddenViews) {
- if (overallTransition != null) {
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
- if (enterTransition != null) {
- removeTargets(enterTransition, enteringViews);
- excludeViews(enterTransition, exitTransition, exitingViews, false);
- excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
- false);
- }
- if (exitTransition != null) {
- removeTargets(exitTransition, exitingViews);
- excludeViews(exitTransition, enterTransition, enteringViews, false);
- excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
- false);
- }
- if (sharedElementTransition != null) {
- removeTargets(sharedElementTransition, sharedElementTargets);
- }
- int numViews = hiddenViews.size();
- for (int i = 0; i < numViews; i++) {
- overallTransition.excludeTarget(hiddenViews.get(i), false);
- }
- overallTransition.excludeTarget(nonExistingView, false);
- return true;
- }
- });
- }
- }
-
- /**
- * This method removes the views from transitions that target ONLY those views.
- * The views list should match those added in addTargets and should contain
- * one view that is not in the view hierarchy (state.nonExistentView).
- */
- public static void removeTargets(Transition transition, ArrayList<View> views) {
- if (transition instanceof TransitionSet) {
- TransitionSet set = (TransitionSet) transition;
- int numTransitions = set.getTransitionCount();
- for (int i = 0; i < numTransitions; i++) {
- Transition child = set.getTransitionAt(i);
- removeTargets(child, views);
- }
- } else if (!hasSimpleTarget(transition)) {
- List<View> targets = transition.getTargets();
- if (targets != null && targets.size() == views.size() &&
- targets.containsAll(views)) {
- // We have an exact match. We must have added these earlier in addTargets
- for (int i = views.size() - 1; i >= 0; i--) {
- transition.removeTarget(views.get(i));
- }
- }
- }
- }
-
- /**
- * This method adds views as targets to the transition, but only if the transition
- * doesn't already have a target. It is best for views to contain one View object
- * that does not exist in the view hierarchy (state.nonExistentView) so that
- * when they are removed later, a list match will suffice to remove the targets.
- * Otherwise, if you happened to have targeted the exact views for the transition,
- * the removeTargets call will remove them unexpectedly.
- */
- public static void addTargets(Transition transition, ArrayList<View> views) {
- if (transition instanceof TransitionSet) {
- TransitionSet set = (TransitionSet) transition;
- int numTransitions = set.getTransitionCount();
- for (int i = 0; i < numTransitions; i++) {
- Transition child = set.getTransitionAt(i);
- addTargets(child, views);
- }
- } else if (!hasSimpleTarget(transition)) {
- List<View> targets = transition.getTargets();
- if (isNullOrEmpty(targets)) {
- // We can just add the target views
- int numViews = views.size();
- for (int i = 0; i < numViews; i++) {
- transition.addTarget(views.get(i));
- }
- }
- }
- }
-
- private static boolean hasSimpleTarget(Transition transition) {
- return !isNullOrEmpty(transition.getTargetIds()) ||
- !isNullOrEmpty(transition.getTargetNames()) ||
- !isNullOrEmpty(transition.getTargetTypes());
- }
-
- private static boolean isNullOrEmpty(List list) {
- return list == null || list.isEmpty();
- }
-
- /**
- * Remaps a name-to-View map, substituting different names for keys.
- *
- * @param inMap A list of keys found in the map, in the order in toGoInMap
- * @param toGoInMap A list of keys to use for the new map, in the order of inMap
- * @param namedViews The current mapping
- * @return a new Map after it has been mapped with the new names as keys.
- */
- private static ArrayMap<String, View> remapNames(ArrayList<String> inMap,
- ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews) {
- ArrayMap<String, View> remappedViews = new ArrayMap<String, View>();
- if (!namedViews.isEmpty()) {
- int numKeys = inMap.size();
- for (int i = 0; i < numKeys; i++) {
- View view = namedViews.get(inMap.get(i));
-
- if (view != null) {
- remappedViews.put(toGoInMap.get(i), view);
- }
- }
- }
- return remappedViews;
- }
-
- /**
- * Maps shared elements to views in the entering fragment.
- *
- * @param state The transition State as returned from {@link #beginTransition(
- * android.util.SparseArray, android.util.SparseArray, boolean)}.
- * @param inFragment The last fragment to be added.
- * @param isBack true if this is popping the back stack or false if this is a
- * forward operation.
- */
- private ArrayMap<String, View> mapEnteringSharedElements(TransitionState state,
- Fragment inFragment, boolean isBack) {
- ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
- View root = inFragment.getView();
- if (root != null) {
- if (mSharedElementSourceNames != null) {
- root.findNamedViews(namedViews);
- if (isBack) {
- namedViews = remapNames(mSharedElementSourceNames,
- mSharedElementTargetNames, namedViews);
- } else {
- namedViews.retainAll(mSharedElementTargetNames);
- }
- }
- }
- return namedViews;
- }
-
- private void excludeHiddenFragments(final ArrayList<View> hiddenFragmentViews, int containerId,
- Transition transition) {
- if (mManager.mAdded != null) {
- for (int i = 0; i < mManager.mAdded.size(); i++) {
- Fragment fragment = mManager.mAdded.get(i);
- if (fragment.mView != null && fragment.mContainer != null &&
- fragment.mContainerId == containerId) {
- if (fragment.mHidden) {
- if (!hiddenFragmentViews.contains(fragment.mView)) {
- transition.excludeTarget(fragment.mView, true);
- hiddenFragmentViews.add(fragment.mView);
- }
- } else {
- transition.excludeTarget(fragment.mView, false);
- hiddenFragmentViews.remove(fragment.mView);
- }
- }
- }
- }
- }
-
- private static void setEpicenter(Transition transition, View view) {
- final Rect epicenter = new Rect();
- view.getBoundsOnScreen(epicenter);
-
- transition.setEpicenterCallback(new Transition.EpicenterCallback() {
- @Override
- public Rect onGetEpicenter(Transition transition) {
- return epicenter;
- }
- });
- }
-
- private void setSharedElementEpicenter(Transition transition, final TransitionState state) {
- transition.setEpicenterCallback(new Transition.EpicenterCallback() {
- private Rect mEpicenter;
-
- @Override
- public Rect onGetEpicenter(Transition transition) {
- if (mEpicenter == null && state.enteringEpicenterView != null) {
- mEpicenter = new Rect();
- state.enteringEpicenterView.getBoundsOnScreen(mEpicenter);
- }
- return mEpicenter;
- }
- });
- }
-
- public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,
- SparseArray<FragmentContainerTransition> transitioningFragments) {
- if (FragmentManagerImpl.DEBUG) {
- Log.v(TAG, "popFromBackStack: " + this);
- LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
- PrintWriter pw = new FastPrintWriter(logw, false, 1024);
- dump(" ", null, pw, null);
- pw.flush();
- }
-
- if (mManager.mCurState >= Fragment.CREATED) {
- if (state == null) {
- if (transitioningFragments.size() != 0) {
- state = beginTransition(transitioningFragments);
- }
- } else if (!doStateMove) {
- setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
- }
- }
-
- bumpBackStackNesting(-1);
-
- final int numOps = mOps.size();
- for (int opNum = numOps - 1; opNum >= 0; opNum--) {
+ void setOnStartPostponedListener(Fragment.OnStartEnterTransitionListener listener) {
+ for (int opNum = 0; opNum < mOps.size(); opNum++) {
final Op op = mOps.get(opNum);
- Fragment f = op.fragment;
- switch (op.cmd) {
- case OP_ADD:
- f.mNextAnim = op.popExitAnim;
- mManager.removeFragment(f,
- FragmentManagerImpl.reverseTransit(mTransition),
- mTransitionStyle);
- break;
- case OP_REMOVE:
- f.mNextAnim = op.popEnterAnim;
- mManager.addFragment(f, false);
- break;
- case OP_HIDE:
- f.mNextAnim = op.popEnterAnim;
- mManager.showFragment(f,
- FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- break;
- case OP_SHOW:
- f.mNextAnim = op.popExitAnim;
- mManager.hideFragment(f,
- FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- break;
- case OP_DETACH:
- f.mNextAnim = op.popEnterAnim;
- mManager.attachFragment(f,
- FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- break;
- case OP_ATTACH:
- f.mNextAnim = op.popExitAnim;
- mManager.detachFragment(f,
- FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
- break;
- default:
- throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
- }
- }
-
- if (doStateMove) {
- mManager.moveToState(mManager.mCurState,
- FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
- state = null;
- }
-
- if (mIndex >= 0) {
- mManager.freeBackStackIndex(mIndex);
- mIndex = -1;
- }
- return state;
- }
-
- private static void setNameOverride(ArrayMap<String, String> overrides,
- String source, String target) {
- if (source != null && target != null && !source.equals(target)) {
- for (int index = 0; index < overrides.size(); index++) {
- if (source.equals(overrides.valueAt(index))) {
- overrides.setValueAt(index, target);
- return;
- }
- }
- overrides.put(source, target);
- }
- }
-
- private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames,
- ArrayList<String> targetNames) {
- if (sourceNames != null && targetNames != null) {
- for (int i = 0; i < sourceNames.size(); i++) {
- String source = sourceNames.get(i);
- String target = targetNames.get(i);
- setNameOverride(state.nameOverrides, source, target);
+ if (isFragmentPostponed(op)) {
+ op.fragment.setOnStartEnterTransitionListener(listener);
}
}
}
- private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
- boolean isEnd) {
- int targetCount = mSharedElementTargetNames == null ? 0 : mSharedElementTargetNames.size();
- int sourceCount = mSharedElementSourceNames == null ? 0 : mSharedElementSourceNames.size();
- final int count = Math.min(targetCount, sourceCount);
- for (int i = 0; i < count; i++) {
- String source = mSharedElementSourceNames.get(i);
- String originalTarget = mSharedElementTargetNames.get(i);
- View view = namedViews.get(originalTarget);
- if (view != null) {
- String target = view.getTransitionName();
- if (isEnd) {
- setNameOverride(state.nameOverrides, source, target);
- } else {
- setNameOverride(state.nameOverrides, target, source);
- }
- }
- }
- }
-
- private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
- boolean isEnd) {
- int count = namedViews == null ? 0 : namedViews.size();
- for (int i = 0; i < count; i++) {
- String source = namedViews.keyAt(i);
- String target = namedViews.valueAt(i).getTransitionName();
- if (isEnd) {
- setNameOverride(state.nameOverrides, source, target);
- } else {
- setNameOverride(state.nameOverrides, target, source);
- }
- }
+ private static boolean isFragmentPostponed(Op op) {
+ final Fragment fragment = op.fragment;
+ return (fragment.mAdded && fragment.mView != null && !fragment.mDetached &&
+ !fragment.mHidden && fragment.isPostponed());
}
public String getName() {
@@ -1713,36 +909,4 @@
public boolean isEmpty() {
return mOps.isEmpty();
}
-
- public class TransitionState {
- public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>();
- public View enteringEpicenterView;
- public View nonExistentView;
- }
-
- /**
- * Tracks the last fragment added and first fragment removed for fragment transitions.
- * This also tracks which fragments are changed by push or pop transactions.
- */
- public static class FragmentContainerTransition {
- /**
- * The last fragment added/attached/shown in its container
- */
- public Fragment lastIn;
-
- /**
- * true when lastIn was added during a pop transaction or false if added with a push
- */
- public boolean lastInIsPop;
-
- /**
- * The first fragment with a View that was removed/detached/hidden in its container.
- */
- public Fragment firstOut;
-
- /**
- * true when firstOut was removed during a pop transaction or false otherwise
- */
- public boolean firstOutIsPop;
- }
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index c6736eef..27a0200 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -123,6 +123,7 @@
mIsReadyForTransition = true;
hideViews(mSharedElements);
if (getViewsTransition() != null && mTransitioningViews != null) {
+ stripOffscreenViews();
hideViews(mTransitioningViews);
}
if (mIsReturning) {
@@ -518,9 +519,6 @@
mIsViewsTransitionStarted = true;
if (mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
viewsTransition = configureTransition(getViewsTransition(), true);
- if (viewsTransition != null && !mIsReturning) {
- stripOffscreenViews();
- }
}
if (viewsTransition == null) {
viewsTransitionComplete();
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 6ea170e..8124232 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -31,6 +31,8 @@
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.transition.Transition;
@@ -375,15 +377,6 @@
int mState = INITIALIZING;
- // Non-null if the fragment's view hierarchy is currently animating away,
- // meaning we need to wait a bit on completely destroying it. This is the
- // animation that is running.
- Animator mAnimatingAway;
-
- // If mAnimatingAway != null, this is the state we should move to once the
- // animation is done.
- int mStateAfterAnimating;
-
// When instantiated from saved state, this is the saved state.
Bundle mSavedFragmentState;
SparseArray<Parcelable> mSavedViewState;
@@ -478,9 +471,6 @@
// Used to verify that subclasses call through to super class.
boolean mCalled;
- // If app has requested a specific animation, this is the one to use.
- int mNextAnim;
-
// The parent container of the fragment after dynamically added to UI.
ViewGroup mContainer;
@@ -498,17 +488,18 @@
boolean mLoadersStarted;
boolean mCheckedForLoaderManager;
- private Transition mEnterTransition = null;
- private Transition mReturnTransition = USE_DEFAULT_TRANSITION;
- private Transition mExitTransition = null;
- private Transition mReenterTransition = USE_DEFAULT_TRANSITION;
- private Transition mSharedElementEnterTransition = null;
- private Transition mSharedElementReturnTransition = USE_DEFAULT_TRANSITION;
- private Boolean mAllowReturnTransitionOverlap;
- private Boolean mAllowEnterTransitionOverlap;
+ // The animation and transition information for the fragment. This will be null
+ // unless the elements are explicitly accessed and should remain null for Fragments
+ // without Views.
+ AnimationInfo mAnimationInfo;
- SharedElementCallback mEnterTransitionCallback = SharedElementCallback.NULL_CALLBACK;
- SharedElementCallback mExitTransitionCallback = SharedElementCallback.NULL_CALLBACK;
+ // True if the View was added, and its animation has yet to be run. This could
+ // also indicate that the fragment view hasn't been made visible, even if there is no
+ // animation for this fragment.
+ boolean mIsNewlyAdded;
+
+ // True if mHidden has been changed and the animation should be scheduled.
+ boolean mHiddenChanged;
/**
* State information that has been retrieved from a fragment instance
@@ -1390,26 +1381,41 @@
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.Fragment);
- mEnterTransition = loadTransition(context, a, mEnterTransition, null,
- com.android.internal.R.styleable.Fragment_fragmentEnterTransition);
- mReturnTransition = loadTransition(context, a, mReturnTransition, USE_DEFAULT_TRANSITION,
- com.android.internal.R.styleable.Fragment_fragmentReturnTransition);
- mExitTransition = loadTransition(context, a, mExitTransition, null,
- com.android.internal.R.styleable.Fragment_fragmentExitTransition);
- mReenterTransition = loadTransition(context, a, mReenterTransition, USE_DEFAULT_TRANSITION,
- com.android.internal.R.styleable.Fragment_fragmentReenterTransition);
- mSharedElementEnterTransition = loadTransition(context, a, mSharedElementEnterTransition,
- null, com.android.internal.R.styleable.Fragment_fragmentSharedElementEnterTransition);
- mSharedElementReturnTransition = loadTransition(context, a, mSharedElementReturnTransition,
+ setEnterTransition(loadTransition(context, a, getEnterTransition(), null,
+ com.android.internal.R.styleable.Fragment_fragmentEnterTransition));
+ setReturnTransition(loadTransition(context, a, getReturnTransition(),
USE_DEFAULT_TRANSITION,
- com.android.internal.R.styleable.Fragment_fragmentSharedElementReturnTransition);
- if (mAllowEnterTransitionOverlap == null) {
- mAllowEnterTransitionOverlap = a.getBoolean(
- com.android.internal.R.styleable.Fragment_fragmentAllowEnterTransitionOverlap, true);
+ com.android.internal.R.styleable.Fragment_fragmentReturnTransition));
+ setExitTransition(loadTransition(context, a, getExitTransition(), null,
+ com.android.internal.R.styleable.Fragment_fragmentExitTransition));
+
+ setReenterTransition(loadTransition(context, a, getReenterTransition(),
+ USE_DEFAULT_TRANSITION,
+ com.android.internal.R.styleable.Fragment_fragmentReenterTransition));
+ setSharedElementEnterTransition(loadTransition(context, a,
+ getSharedElementEnterTransition(), null,
+ com.android.internal.R.styleable.Fragment_fragmentSharedElementEnterTransition));
+ setSharedElementReturnTransition(loadTransition(context, a,
+ getSharedElementReturnTransition(), USE_DEFAULT_TRANSITION,
+ com.android.internal.R.styleable.Fragment_fragmentSharedElementReturnTransition));
+ boolean isEnterSet;
+ boolean isReturnSet;
+ if (mAnimationInfo == null) {
+ isEnterSet = false;
+ isReturnSet = false;
+ } else {
+ isEnterSet = mAnimationInfo.mAllowEnterTransitionOverlap != null;
+ isReturnSet = mAnimationInfo.mAllowReturnTransitionOverlap != null;
}
- if (mAllowReturnTransitionOverlap == null) {
- mAllowReturnTransitionOverlap = a.getBoolean(
- com.android.internal.R.styleable.Fragment_fragmentAllowReturnTransitionOverlap, true);
+ if (!isEnterSet) {
+ setAllowEnterTransitionOverlap(a.getBoolean(
+ com.android.internal.R.styleable.Fragment_fragmentAllowEnterTransitionOverlap,
+ true));
+ }
+ if (!isReturnSet) {
+ setAllowReturnTransitionOverlap(a.getBoolean(
+ com.android.internal.R.styleable.Fragment_fragmentAllowReturnTransitionOverlap,
+ true));
}
a.recycle();
@@ -1934,16 +1940,12 @@
*/
public void setEnterSharedElementCallback(SharedElementCallback callback) {
if (callback == null) {
+ if (mAnimationInfo == null) {
+ return; // already a null callback
+ }
callback = SharedElementCallback.NULL_CALLBACK;
}
- mEnterTransitionCallback = callback;
- }
-
- /**
- * @hide
- */
- public void setEnterSharedElementTransitionCallback(SharedElementCallback callback) {
- setEnterSharedElementCallback(callback);
+ ensureAnimationInfo().mEnterTransitionCallback = callback;
}
/**
@@ -1955,16 +1957,12 @@
*/
public void setExitSharedElementCallback(SharedElementCallback callback) {
if (callback == null) {
+ if (mAnimationInfo == null) {
+ return; // already a null callback
+ }
callback = SharedElementCallback.NULL_CALLBACK;
}
- mExitTransitionCallback = callback;
- }
-
- /**
- * @hide
- */
- public void setExitSharedElementTransitionCallback(SharedElementCallback callback) {
- setExitSharedElementCallback(callback);
+ ensureAnimationInfo().mExitTransitionCallback = callback;
}
/**
@@ -1979,7 +1977,9 @@
* @attr ref android.R.styleable#Fragment_fragmentEnterTransition
*/
public void setEnterTransition(Transition transition) {
- mEnterTransition = transition;
+ if (shouldChangeTransition(transition, null)) {
+ ensureAnimationInfo().mEnterTransition = transition;
+ }
}
/**
@@ -1993,7 +1993,10 @@
* @attr ref android.R.styleable#Fragment_fragmentEnterTransition
*/
public Transition getEnterTransition() {
- return mEnterTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mEnterTransition;
}
/**
@@ -2011,7 +2014,9 @@
* @attr ref android.R.styleable#Fragment_fragmentExitTransition
*/
public void setReturnTransition(Transition transition) {
- mReturnTransition = transition;
+ if (shouldChangeTransition(transition, USE_DEFAULT_TRANSITION)) {
+ ensureAnimationInfo().mReturnTransition = transition;
+ }
}
/**
@@ -2028,8 +2033,11 @@
* @attr ref android.R.styleable#Fragment_fragmentExitTransition
*/
public Transition getReturnTransition() {
- return mReturnTransition == USE_DEFAULT_TRANSITION ? getEnterTransition()
- : mReturnTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mReturnTransition == USE_DEFAULT_TRANSITION ? getEnterTransition()
+ : mAnimationInfo.mReturnTransition;
}
/**
@@ -2046,7 +2054,9 @@
* @attr ref android.R.styleable#Fragment_fragmentExitTransition
*/
public void setExitTransition(Transition transition) {
- mExitTransition = transition;
+ if (shouldChangeTransition(transition, null)) {
+ ensureAnimationInfo().mExitTransition = transition;
+ }
}
/**
@@ -2063,7 +2073,10 @@
* @attr ref android.R.styleable#Fragment_fragmentExitTransition
*/
public Transition getExitTransition() {
- return mExitTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mExitTransition;
}
/**
@@ -2080,7 +2093,9 @@
* @attr ref android.R.styleable#Fragment_fragmentReenterTransition
*/
public void setReenterTransition(Transition transition) {
- mReenterTransition = transition;
+ if (shouldChangeTransition(transition, USE_DEFAULT_TRANSITION)) {
+ ensureAnimationInfo().mReenterTransition = transition;
+ }
}
/**
@@ -2097,8 +2112,11 @@
* @attr ref android.R.styleable#Fragment_fragmentReenterTransition
*/
public Transition getReenterTransition() {
- return mReenterTransition == USE_DEFAULT_TRANSITION ? getExitTransition()
- : mReenterTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mReenterTransition == USE_DEFAULT_TRANSITION ? getExitTransition()
+ : mAnimationInfo.mReenterTransition;
}
/**
@@ -2112,7 +2130,9 @@
* @attr ref android.R.styleable#Fragment_fragmentSharedElementEnterTransition
*/
public void setSharedElementEnterTransition(Transition transition) {
- mSharedElementEnterTransition = transition;
+ if (shouldChangeTransition(transition, null)) {
+ ensureAnimationInfo().mSharedElementEnterTransition = transition;
+ }
}
/**
@@ -2126,7 +2146,10 @@
* @attr ref android.R.styleable#Fragment_fragmentSharedElementEnterTransition
*/
public Transition getSharedElementEnterTransition() {
- return mSharedElementEnterTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mSharedElementEnterTransition;
}
/**
@@ -2143,7 +2166,9 @@
* @attr ref android.R.styleable#Fragment_fragmentSharedElementReturnTransition
*/
public void setSharedElementReturnTransition(Transition transition) {
- mSharedElementReturnTransition = transition;
+ if (shouldChangeTransition(transition, USE_DEFAULT_TRANSITION)) {
+ ensureAnimationInfo().mSharedElementReturnTransition = transition;
+ }
}
/**
@@ -2160,8 +2185,12 @@
* @attr ref android.R.styleable#Fragment_fragmentSharedElementReturnTransition
*/
public Transition getSharedElementReturnTransition() {
- return mSharedElementReturnTransition == USE_DEFAULT_TRANSITION ?
- getSharedElementEnterTransition() : mSharedElementReturnTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mSharedElementReturnTransition == USE_DEFAULT_TRANSITION
+ ? getSharedElementEnterTransition()
+ : mAnimationInfo.mSharedElementReturnTransition;
}
/**
@@ -2174,7 +2203,7 @@
* @attr ref android.R.styleable#Fragment_fragmentAllowEnterTransitionOverlap
*/
public void setAllowEnterTransitionOverlap(boolean allow) {
- mAllowEnterTransitionOverlap = allow;
+ ensureAnimationInfo().mAllowEnterTransitionOverlap = allow;
}
/**
@@ -2187,7 +2216,8 @@
* @attr ref android.R.styleable#Fragment_fragmentAllowEnterTransitionOverlap
*/
public boolean getAllowEnterTransitionOverlap() {
- return (mAllowEnterTransitionOverlap == null) ? true : mAllowEnterTransitionOverlap;
+ return (mAnimationInfo == null || mAnimationInfo.mAllowEnterTransitionOverlap == null)
+ ? true : mAnimationInfo.mAllowEnterTransitionOverlap;
}
/**
@@ -2200,7 +2230,7 @@
* @attr ref android.R.styleable#Fragment_fragmentAllowReturnTransitionOverlap
*/
public void setAllowReturnTransitionOverlap(boolean allow) {
- mAllowReturnTransitionOverlap = allow;
+ ensureAnimationInfo().mAllowReturnTransitionOverlap = allow;
}
/**
@@ -2213,7 +2243,90 @@
* @attr ref android.R.styleable#Fragment_fragmentAllowReturnTransitionOverlap
*/
public boolean getAllowReturnTransitionOverlap() {
- return (mAllowReturnTransitionOverlap == null) ? true : mAllowReturnTransitionOverlap;
+ return (mAnimationInfo == null || mAnimationInfo.mAllowReturnTransitionOverlap == null)
+ ? true : mAnimationInfo.mAllowReturnTransitionOverlap;
+ }
+
+ /**
+ * Postpone the entering Fragment transition until {@link #startPostponedEnterTransition()}
+ * or {@link FragmentManager#executePendingTransactions()} has been called.
+ * <p>
+ * This method gives the Fragment the ability to delay Fragment animations
+ * until all data is loaded. Until then, the added, shown, and
+ * attached Fragments will be INVISIBLE and removed, hidden, and detached Fragments won't
+ * be have their Views removed. The transaction runs when all postponed added Fragments in the
+ * transaction have called {@link #startPostponedEnterTransition()}.
+ * <p>
+ * This method should be called before being added to the FragmentTransaction or
+ * in {@link #onCreate(Bundle), {@link #onAttach(Context)}, or
+ * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}}.
+ * {@link #startPostponedEnterTransition()} must be called to allow the Fragment to
+ * start the transitions.
+ * <p>
+ * When a FragmentTransaction is started that may affect a postponed FragmentTransaction,
+ * based on which containers are in their operations, the postponed FragmentTransaction
+ * will have its start triggered. The early triggering may result in faulty or nonexistent
+ * animations in the postponed transaction. FragmentTransactions that operate only on
+ * independent containers will not interfere with each other's postponement.
+ * <p>
+ * Calling postponeEnterTransition on Fragments with a null View will not postpone the
+ * transition. Likewise, postponement only works if FragmentTransaction optimizations are
+ * enabled.
+ *
+ * @see Activity#postponeEnterTransition()
+ * @see FragmentTransaction#setAllowOptimization(boolean)
+ */
+ public void postponeEnterTransition() {
+ ensureAnimationInfo().mEnterTransitionPostponed = true;
+ }
+
+ /**
+ * Begin postponed transitions after {@link #postponeEnterTransition()} was called.
+ * If postponeEnterTransition() was called, you must call startPostponedEnterTransition()
+ * or {@link FragmentManager#executePendingTransactions()} to complete the FragmentTransaction.
+ * If postponement was interrupted with {@link FragmentManager#executePendingTransactions()},
+ * before {@code startPostponedEnterTransition()}, animations may not run or may execute
+ * improperly.
+ *
+ * @see Activity#startPostponedEnterTransition()
+ */
+ public void startPostponedEnterTransition() {
+ if (mFragmentManager == null || mFragmentManager.mHost == null) {
+ ensureAnimationInfo().mEnterTransitionPostponed = false;
+ } else if (Looper.myLooper() != mFragmentManager.mHost.getHandler().getLooper()) {
+ mFragmentManager.mHost.getHandler().
+ postAtFrontOfQueue(this::callStartTransitionListener);
+ } else {
+ callStartTransitionListener();
+ }
+ }
+
+ /**
+ * Calls the start transition listener. This must be called on the UI thread.
+ */
+ private void callStartTransitionListener() {
+ final OnStartEnterTransitionListener listener;
+ if (mAnimationInfo == null) {
+ listener = null;
+ } else {
+ mAnimationInfo.mEnterTransitionPostponed = false;
+ listener = mAnimationInfo.mStartEnterTransitionListener;
+ mAnimationInfo.mStartEnterTransitionListener = null;
+ }
+ if (listener != null) {
+ listener.onStartEnterTransition();
+ }
+ }
+
+ /**
+ * Returns true if mAnimationInfo is not null or the transition differs from the default value.
+ * This is broken out to ensure mAnimationInfo is properly locked when checking.
+ */
+ private boolean shouldChangeTransition(Transition transition, Transition defaultValue) {
+ if (transition == defaultValue) {
+ return mAnimationInfo != null;
+ }
+ return true;
}
/**
@@ -2274,8 +2387,8 @@
writer.print(" mTargetRequestCode=");
writer.println(mTargetRequestCode);
}
- if (mNextAnim != 0) {
- writer.print(prefix); writer.print("mNextAnim="); writer.println(mNextAnim);
+ if (getNextAnim() != 0) {
+ writer.print(prefix); writer.print("mNextAnim="); writer.println(getNextAnim());
}
if (mContainer != null) {
writer.print(prefix); writer.print("mContainer="); writer.println(mContainer);
@@ -2283,10 +2396,11 @@
if (mView != null) {
writer.print(prefix); writer.print("mView="); writer.println(mView);
}
- if (mAnimatingAway != null) {
- writer.print(prefix); writer.print("mAnimatingAway="); writer.println(mAnimatingAway);
+ if (getAnimatingAway() != null) {
+ writer.print(prefix); writer.print("mAnimatingAway=");
+ writer.println(getAnimatingAway());
writer.print(prefix); writer.print("mStateAfterAnimating=");
- writer.println(mStateAfterAnimating);
+ writer.println(getStateAfterAnimating());
}
if (mLoaderManager != null) {
writer.print(prefix); writer.println("Loader Manager:");
@@ -2613,6 +2727,23 @@
}
}
+ void setOnStartEnterTransitionListener(OnStartEnterTransitionListener listener) {
+ ensureAnimationInfo();
+ if (listener == mAnimationInfo.mStartEnterTransitionListener) {
+ return;
+ }
+ if (listener != null && mAnimationInfo.mStartEnterTransitionListener != null) {
+ throw new IllegalStateException("Trying to set a replacement " +
+ "startPostponedEnterTransition on " + this);
+ }
+ if (mAnimationInfo.mEnterTransitionPostponed) {
+ mAnimationInfo.mStartEnterTransitionListener = listener;
+ }
+ if (listener != null) {
+ listener.startListening();
+ }
+ }
+
private static Transition loadTransition(Context context, TypedArray typedArray,
Transition currentValue, Transition defaultValue, int id) {
if (currentValue != defaultValue) {
@@ -2631,4 +2762,158 @@
return transition;
}
+ private AnimationInfo ensureAnimationInfo() {
+ if (mAnimationInfo == null) {
+ mAnimationInfo = new AnimationInfo();
+ }
+ return mAnimationInfo;
+ }
+
+ int getNextAnim() {
+ if (mAnimationInfo == null) {
+ return 0;
+ }
+ return mAnimationInfo.mNextAnim;
+ }
+
+ void setNextAnim(int animResourceId) {
+ if (mAnimationInfo == null && animResourceId == 0) {
+ return; // no change!
+ }
+ ensureAnimationInfo().mNextAnim = animResourceId;
+ }
+
+ int getNextTransition() {
+ if (mAnimationInfo == null) {
+ return 0;
+ }
+ return mAnimationInfo.mNextTransition;
+ }
+
+ void setNextTransition(int nextTransition, int nextTransitionStyle) {
+ if (mAnimationInfo == null && nextTransition == 0 && nextTransitionStyle == 0) {
+ return; // no change!
+ }
+ ensureAnimationInfo();
+ mAnimationInfo.mNextTransition = nextTransition;
+ mAnimationInfo.mNextTransitionStyle = nextTransitionStyle;
+ }
+
+ int getNextTransitionStyle() {
+ if (mAnimationInfo == null) {
+ return 0;
+ }
+ return mAnimationInfo.mNextTransitionStyle;
+ }
+
+ SharedElementCallback getEnterTransitionCallback() {
+ if (mAnimationInfo == null) {
+ return SharedElementCallback.NULL_CALLBACK;
+ }
+ return mAnimationInfo.mEnterTransitionCallback;
+ }
+
+ SharedElementCallback getExitTransitionCallback() {
+ if (mAnimationInfo == null) {
+ return SharedElementCallback.NULL_CALLBACK;
+ }
+ return mAnimationInfo.mExitTransitionCallback;
+ }
+
+ Animator getAnimatingAway() {
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mAnimatingAway;
+ }
+
+ void setAnimatingAway(Animator animator) {
+ ensureAnimationInfo().mAnimatingAway = animator;
+ }
+
+ int getStateAfterAnimating() {
+ if (mAnimationInfo == null) {
+ return 0;
+ }
+ return mAnimationInfo.mStateAfterAnimating;
+ }
+
+ void setStateAfterAnimating(int state) {
+ ensureAnimationInfo().mStateAfterAnimating = state;
+ }
+
+ boolean isPostponed() {
+ if (mAnimationInfo == null) {
+ return false;
+ }
+ return mAnimationInfo.mEnterTransitionPostponed;
+ }
+
+ boolean isHideReplaced() {
+ if (mAnimationInfo == null) {
+ return false;
+ }
+ return mAnimationInfo.mIsHideReplaced;
+ }
+
+ void setHideReplaced(boolean replaced) {
+ ensureAnimationInfo().mIsHideReplaced = replaced;
+ }
+
+ /**
+ * Used internally to be notified when {@link #startPostponedEnterTransition()} has
+ * been called. This listener will only be called once and then be removed from the
+ * listeners.
+ */
+ interface OnStartEnterTransitionListener {
+ void onStartEnterTransition();
+ void startListening();
+ }
+
+ /**
+ * Contains all the animation and transition information for a fragment. This will only
+ * be instantiated for Fragments that have Views.
+ */
+ static class AnimationInfo {
+ // Non-null if the fragment's view hierarchy is currently animating away,
+ // meaning we need to wait a bit on completely destroying it. This is the
+ // animation that is running.
+ Animator mAnimatingAway;
+
+ // If mAnimatingAway != null, this is the state we should move to once the
+ // animation is done.
+ int mStateAfterAnimating;
+
+ // If app has requested a specific animation, this is the one to use.
+ int mNextAnim;
+
+ // If app has requested a specific transition, this is the one to use.
+ int mNextTransition;
+
+ // If app has requested a specific transition style, this is the one to use.
+ int mNextTransitionStyle;
+
+ private Transition mEnterTransition = null;
+ private Transition mReturnTransition = USE_DEFAULT_TRANSITION;
+ private Transition mExitTransition = null;
+ private Transition mReenterTransition = USE_DEFAULT_TRANSITION;
+ private Transition mSharedElementEnterTransition = null;
+ private Transition mSharedElementReturnTransition = USE_DEFAULT_TRANSITION;
+ private Boolean mAllowReturnTransitionOverlap;
+ private Boolean mAllowEnterTransitionOverlap;
+
+ SharedElementCallback mEnterTransitionCallback = SharedElementCallback.NULL_CALLBACK;
+ SharedElementCallback mExitTransitionCallback = SharedElementCallback.NULL_CALLBACK;
+
+ // True when postponeEnterTransition has been called and startPostponeEnterTransition
+ // hasn't been called yet.
+ boolean mEnterTransitionPostponed;
+
+ // Listener to wait for startPostponeEnterTransition. After being called, it will
+ // be set to null
+ OnStartEnterTransitionListener mStartEnterTransitionListener;
+
+ // True if the View was hidden, but the transition is handling the hide
+ boolean mIsHideReplaced;
+ }
}
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index d869168..7e415e9 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -54,7 +54,8 @@
private boolean mLoadersStarted;
public FragmentHostCallback(Context context, Handler handler, int windowAnimations) {
- this(null /*activity*/, context, handler, windowAnimations);
+ this((context instanceof Activity) ? (Activity)context : null, context,
+ chooseHandler(context, handler), windowAnimations);
}
FragmentHostCallback(Activity activity) {
@@ -70,6 +71,19 @@
}
/**
+ * Used internally in {@link #FragmentHostCallback(Context, Handler, int)} to choose
+ * the Activity's handler or the provided handler.
+ */
+ private static Handler chooseHandler(Context context, Handler handler) {
+ if (handler == null && context instanceof Activity) {
+ Activity activity = (Activity) context;
+ return activity.mHandler;
+ } else {
+ return handler;
+ }
+ }
+
+ /**
* Print internal state into the given stream.
*
* @param prefix Desired prefix to prepend at each line of output.
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 674c3f7..233fee7 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -28,7 +28,6 @@
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Debug;
-import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
@@ -44,6 +43,7 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+
import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
@@ -160,6 +160,9 @@
* can call this function (only from the main thread) to do so. Note that
* all callbacks and other related behavior will be done from within this
* call, so be careful about where this is called from.
+ * <p>
+ * This also forces the start of any postponed Transactions where
+ * {@link Fragment#postponeEnterTransition()} has been called.
*
* @return Returns true if there were any pending transactions to be
* executed.
@@ -206,7 +209,7 @@
/**
* Like {@link #popBackStack()}, but performs the operation immediately
* inside of the call. This is like calling {@link #executePendingTransactions()}
- * afterwards.
+ * afterwards without forcing the start of postponed Transactions.
* @return Returns true if there was something popped, else false.
*/
public abstract boolean popBackStackImmediate();
@@ -229,7 +232,7 @@
/**
* Like {@link #popBackStack(String, int)}, but performs the operation immediately
* inside of the call. This is like calling {@link #executePendingTransactions()}
- * afterwards.
+ * afterwards without forcing the start of postponed Transactions.
* @return Returns true if there was something popped, else false.
*/
public abstract boolean popBackStackImmediate(String name, int flags);
@@ -253,7 +256,7 @@
/**
* Like {@link #popBackStack(int, int)}, but performs the operation immediately
* inside of the call. This is like calling {@link #executePendingTransactions()}
- * afterwards.
+ * afterwards without forcing the start of postponed Transactions.
* @return Returns true if there was something popped, else false.
*/
public abstract boolean popBackStackImmediate(int id, int flags);
@@ -445,8 +448,7 @@
}
}
- ArrayList<Runnable> mPendingActions;
- Runnable[] mTmpActions;
+ ArrayList<OpGenerator> mPendingActions;
boolean mExecutingActions;
ArrayList<Fragment> mActive;
@@ -463,7 +465,6 @@
int mCurState = Fragment.INITIALIZING;
FragmentHostCallback<?> mHost;
- FragmentController mController;
FragmentContainer mContainer;
Fragment mParent;
@@ -473,10 +474,18 @@
String mNoTransactionsBecause;
boolean mHavePendingDeferredStart;
+ // Temporary vars for optimizing execution of BackStackRecords:
+ ArrayList<BackStackRecord> mTmpRecords;
+ ArrayList<Boolean> mTmpIsPop;
+ ArrayList<Fragment> mTmpAddedFragments;
+
// Temporary vars for state save and restore.
Bundle mStateBundle = null;
SparseArray<Parcelable> mStateArray = null;
-
+
+ // Postponed transactions.
+ ArrayList<StartEnterTransitionListener> mPostponedTransactions;
+
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
@@ -560,61 +569,73 @@
@Override
public boolean executePendingTransactions() {
- return execPendingActions();
+ boolean updates = execPendingActions();
+ forcePostponedTransactions();
+ return updates;
}
@Override
public void popBackStack() {
- enqueueAction(new Runnable() {
- @Override public void run() {
- popBackStackState(mHost.getHandler(), null, -1, 0);
- }
- }, false);
+ enqueueAction(new PopBackStackState(null, -1, 0), false);
}
@Override
public boolean popBackStackImmediate() {
checkStateLoss();
- executePendingTransactions();
- return popBackStackState(mHost.getHandler(), null, -1, 0);
+ return popBackStackImmediate(null, -1, 0);
}
@Override
- public void popBackStack(final String name, final int flags) {
- enqueueAction(new Runnable() {
- @Override public void run() {
- popBackStackState(mHost.getHandler(), name, -1, flags);
- }
- }, false);
+ public void popBackStack(String name, int flags) {
+ enqueueAction(new PopBackStackState(name, -1, flags), false);
}
@Override
public boolean popBackStackImmediate(String name, int flags) {
checkStateLoss();
- executePendingTransactions();
- return popBackStackState(mHost.getHandler(), name, -1, flags);
+ return popBackStackImmediate(name, -1, flags);
}
@Override
- public void popBackStack(final int id, final int flags) {
+ public void popBackStack(int id, int flags) {
if (id < 0) {
throw new IllegalArgumentException("Bad id: " + id);
}
- enqueueAction(new Runnable() {
- @Override public void run() {
- popBackStackState(mHost.getHandler(), null, id, flags);
- }
- }, false);
+ enqueueAction(new PopBackStackState(null, id, flags), false);
}
@Override
public boolean popBackStackImmediate(int id, int flags) {
checkStateLoss();
- executePendingTransactions();
if (id < 0) {
throw new IllegalArgumentException("Bad id: " + id);
}
- return popBackStackState(mHost.getHandler(), null, id, flags);
+ return popBackStackImmediate(null, id, flags);
+ }
+
+ /**
+ * Used by all public popBackStackImmediate methods, this executes pending transactions and
+ * returns true if the pop action did anything, regardless of what other pending
+ * transactions did.
+ *
+ * @return true if the pop operation did anything or false otherwise.
+ */
+ private boolean popBackStackImmediate(String name, int id, int flags) {
+ execPendingActions();
+ ensureExecReady(true);
+
+ boolean executePop = popBackStackState(mTmpRecords, mTmpIsPop, name, id, flags);
+ if (executePop) {
+ mExecutingActions = true;
+ try {
+ optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
+ } finally {
+ cleanupExec();
+ }
+ }
+
+ doPendingDeferredStart();
+ return executePop;
}
@Override
@@ -785,7 +806,7 @@
if (N > 0) {
writer.print(prefix); writer.println("Pending Actions:");
for (int i=0; i<N; i++) {
- Runnable r = mPendingActions.get(i);
+ OpGenerator r = mPendingActions.get(i);
writer.print(prefix); writer.print(" #"); writer.print(i);
writer.print(": "); writer.println(r);
}
@@ -817,14 +838,14 @@
Animator loadAnimator(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
- Animator animObj = fragment.onCreateAnimator(transit, enter,
- fragment.mNextAnim);
+ Animator animObj = fragment.onCreateAnimator(transit, enter, fragment.getNextAnim());
if (animObj != null) {
return animObj;
}
- if (fragment.mNextAnim != 0) {
- Animator anim = AnimatorInflater.loadAnimator(mHost.getContext(), fragment.mNextAnim);
+ if (fragment.getNextAnim() != 0) {
+ Animator anim = AnimatorInflater.loadAnimator(mHost.getContext(),
+ fragment.getNextAnim());
if (anim != null) {
return anim;
}
@@ -900,13 +921,13 @@
if (f.mFromLayout && !f.mInLayout) {
return;
}
- if (f.mAnimatingAway != null) {
+ if (f.getAnimatingAway() != null) {
// The fragment is currently being animated... but! Now we
// want to move our state back up. Give up on waiting for the
// animation, move to whatever the final state should be once
// the animation is done, and then we can proceed from there.
- f.mAnimatingAway = null;
- moveToState(f, f.mStateAfterAnimating, 0, 0, true);
+ f.setAnimatingAway(null);
+ moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
}
switch (f.mState) {
case Fragment.INITIALIZING:
@@ -997,16 +1018,13 @@
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
- Animator anim = loadAnimator(f, transit, true,
- transitionStyle);
- if (anim != null) {
- anim.setTarget(f.mView);
- setHWLayerAnimListenerIfAlpha(f.mView, anim);
- anim.start();
- }
container.addView(f.mView);
+ f.mIsNewlyAdded = true;
}
- if (f.mHidden) f.mView.setVisibility(View.GONE);
+ if (f.mHidden) {
+ f.mView.setVisibility(View.GONE);
+ f.mIsNewlyAdded = false; // No animation required
+ }
f.onViewCreated(f.mView, f.mSavedFragmentState);
}
}
@@ -1061,7 +1079,8 @@
f.performDestroyView();
if (f.mView != null && f.mContainer != null) {
Animator anim = null;
- if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
+ if (mCurState > Fragment.INITIALIZING && !mDestroyed &&
+ f.mView.getVisibility() == View.VISIBLE) {
anim = loadAnimator(f, transit, false,
transitionStyle);
}
@@ -1070,15 +1089,15 @@
final View view = f.mView;
final Fragment fragment = f;
container.startViewTransition(view);
- f.mAnimatingAway = anim;
- f.mStateAfterAnimating = newState;
+ f.setAnimatingAway(anim);
+ f.setStateAfterAnimating(newState);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator anim) {
container.endViewTransition(view);
- if (fragment.mAnimatingAway != null) {
- fragment.mAnimatingAway = null;
- moveToState(fragment, fragment.mStateAfterAnimating,
+ if (fragment.getAnimatingAway() != null) {
+ fragment.setAnimatingAway(null);
+ moveToState(fragment, fragment.getStateAfterAnimating(),
0, 0, false);
}
}
@@ -1096,24 +1115,24 @@
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
if (mDestroyed) {
- if (f.mAnimatingAway != null) {
+ if (f.getAnimatingAway() != null) {
// The fragment's containing activity is
// being destroyed, but this fragment is
// currently animating away. Stop the
// animation right now -- it is not needed,
// and we can't wait any more on destroying
// the fragment.
- Animator anim = f.mAnimatingAway;
- f.mAnimatingAway = null;
+ Animator anim = f.getAnimatingAway();
+ f.setAnimatingAway(null);
anim.cancel();
}
}
- if (f.mAnimatingAway != null) {
+ if (f.getAnimatingAway() != null) {
// We are waiting for the fragment's view to finish
// animating away. Just make a note of the state
// the fragment now should move to once the animation
// is done.
- f.mStateAfterAnimating = newState;
+ f.setStateAfterAnimating(newState);
newState = Fragment.CREATED;
} else {
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
@@ -1149,26 +1168,138 @@
moveToState(f, mCurState, 0, 0, false);
}
- void moveToState(int newState, boolean always) {
- moveToState(newState, 0, 0, always);
+ /**
+ * Fragments that have been shown or hidden don't have their visibility changed or
+ * animations run during the {@link #showFragment(Fragment)} or {@link #hideFragment(Fragment)}
+ * calls. After fragments are brought to their final state in
+ * {@link #moveFragmentToExpectedState(Fragment)} the fragments that have been shown or
+ * hidden must have their visibility changed and their animations started here.
+ *
+ * @param fragment The fragment with mHiddenChanged = true that should change its View's
+ * visibility and start the show or hide animation.
+ */
+ void completeShowHideFragment(final Fragment fragment) {
+ if (fragment.mView != null) {
+ Animator anim = loadAnimator(fragment, fragment.getNextTransition(), !fragment.mHidden,
+ fragment.getNextTransitionStyle());
+ if (anim != null) {
+ anim.setTarget(fragment.mView);
+ if (fragment.mHidden) {
+ if (fragment.isHideReplaced()) {
+ fragment.setHideReplaced(false);
+ } else {
+ // Delay the actual hide operation until the animation finishes, otherwise
+ // the fragment will just immediately disappear
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ animation.removeListener(this);
+ if (fragment.mView != null) {
+ fragment.mView.setVisibility(View.GONE);
+ }
+ }
+ });
+ }
+ }
+ setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
+ anim.start();
+ } else {
+ final int visibility = fragment.mHidden && !fragment.isHideReplaced()
+ ? View.GONE
+ : View.VISIBLE;
+ fragment.mView.setVisibility(visibility);
+ if (fragment.isHideReplaced()) {
+ fragment.setHideReplaced(false);
+ }
+ }
+ }
+ if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
+ mNeedMenuInvalidate = true;
+ }
+ fragment.mHiddenChanged = false;
+ fragment.onHiddenChanged(fragment.mHidden);
}
-
- void moveToState(int newState, int transit, int transitStyle, boolean always) {
+
+ /**
+ * Moves a fragment to its expected final state or the fragment manager's state, depending
+ * on whether the fragment manager's state is raised properly.
+ *
+ * @param f The fragment to change.
+ */
+ void moveFragmentToExpectedState(final Fragment f) {
+ if (f == null) {
+ return;
+ }
+ int nextState = mCurState;
+ if (f.mRemoving) {
+ if (f.isInBackStack()) {
+ nextState = Fragment.CREATED;
+ } else {
+ nextState = Fragment.INITIALIZING;
+ }
+ }
+
+ moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);
+
+ if (f.mView != null) {
+ // Move the view if it is out of order
+ Fragment underFragment = findFragmentUnder(f);
+ if (underFragment != null) {
+ final View underView = underFragment.mView;
+ // make sure this fragment is in the right order.
+ final ViewGroup container = f.mContainer;
+ int underIndex = container.indexOfChild(underView);
+ int viewIndex = container.indexOfChild(f.mView);
+ if (viewIndex < underIndex) {
+ container.removeViewAt(viewIndex);
+ container.addView(f.mView, underIndex);
+ }
+ }
+ if (f.mIsNewlyAdded && f.mContainer != null) {
+ // Make it visible and run the animations
+ f.mView.setVisibility(View.VISIBLE);
+ f.mIsNewlyAdded = false;
+ // run animations:
+ Animator anim = loadAnimator(f, f.getNextTransition(), true, f.getNextTransitionStyle());
+ if (anim != null) {
+ anim.setTarget(f.mView);
+ setHWLayerAnimListenerIfAlpha(f.mView, anim);
+ anim.start();
+ }
+ }
+ }
+ if (f.mHiddenChanged) {
+ completeShowHideFragment(f);
+ }
+ }
+
+ void moveToState(int newState) {
if (mHost == null && newState != Fragment.INITIALIZING) {
throw new IllegalStateException("No activity");
}
- if (!always && mCurState == newState) {
- return;
- }
-
mCurState = newState;
+
if (mActive != null) {
boolean loadersRunning = false;
- for (int i=0; i<mActive.size(); i++) {
+
+ // Must add them in the proper order. mActive fragments may be out of order
+ final int numAdded = mAdded.size();
+ for (int i = 0; i < numAdded; i++) {
+ Fragment f = mAdded.get(i);
+ moveFragmentToExpectedState(f);
+ if (f.mLoaderManager != null) {
+ loadersRunning |= f.mLoaderManager.hasRunningLoaders();
+ }
+ }
+
+ // Now iterate through all active fragments. These will include those that are removed
+ // and detached.
+ final int numActive = mActive.size();
+ for (int i = 0; i < numActive; i++) {
Fragment f = mActive.get(i);
- if (f != null) {
- moveToState(f, newState, transit, transitStyle, false);
+ if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {
+ moveFragmentToExpectedState(f);
if (f.mLoaderManager != null) {
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
}
@@ -1185,7 +1316,7 @@
}
}
}
-
+
void startPendingDeferredFragments() {
if (mActive == null) return;
@@ -1244,6 +1375,9 @@
mAdded.add(fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
+ if (fragment.mView == null) {
+ fragment.mHiddenChanged = false;
+ }
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
@@ -1252,8 +1386,8 @@
}
}
}
-
- public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
+
+ public void removeFragment(Fragment fragment) {
if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
final boolean inactive = !fragment.isInBackStack();
if (!fragment.mDetached || inactive) {
@@ -1273,66 +1407,42 @@
}
fragment.mAdded = false;
fragment.mRemoving = true;
- moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
- transition, transitionStyle, false);
}
}
-
- public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
+
+ /**
+ * Marks a fragment as hidden to be later animated in with
+ * {@link #completeShowHideFragment(Fragment)}.
+ *
+ * @param fragment The fragment to be shown.
+ */
+ public void hideFragment(Fragment fragment) {
if (DEBUG) Log.v(TAG, "hide: " + fragment);
if (!fragment.mHidden) {
fragment.mHidden = true;
- if (fragment.mView != null) {
- Animator anim = loadAnimator(fragment, transition, false,
- transitionStyle);
- if (anim != null) {
- anim.setTarget(fragment.mView);
- // Delay the actual hide operation until the animation finishes, otherwise
- // the fragment will just immediately disappear
- final Fragment finalFragment = fragment;
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (finalFragment.mView != null) {
- finalFragment.mView.setVisibility(View.GONE);
- }
- }
- });
- setHWLayerAnimListenerIfAlpha(finalFragment.mView, anim);
- anim.start();
- } else {
- fragment.mView.setVisibility(View.GONE);
- }
- }
- if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
- mNeedMenuInvalidate = true;
- }
- fragment.onHiddenChanged(true);
+ // Toggle hidden changed so that if a fragment goes through show/hide/show
+ // it doesn't go through the animation.
+ fragment.mHiddenChanged = !fragment.mHiddenChanged;
}
}
-
- public void showFragment(Fragment fragment, int transition, int transitionStyle) {
+
+ /**
+ * Marks a fragment as shown to be later animated in with
+ * {@link #completeShowHideFragment(Fragment)}.
+ *
+ * @param fragment The fragment to be shown.
+ */
+ public void showFragment(Fragment fragment) {
if (DEBUG) Log.v(TAG, "show: " + fragment);
if (fragment.mHidden) {
fragment.mHidden = false;
- if (fragment.mView != null) {
- Animator anim = loadAnimator(fragment, transition, true,
- transitionStyle);
- if (anim != null) {
- anim.setTarget(fragment.mView);
- setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
- anim.start();
- }
- fragment.mView.setVisibility(View.VISIBLE);
- }
- if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
- mNeedMenuInvalidate = true;
- }
- fragment.onHiddenChanged(false);
+ // Toggle hidden changed so that if a fragment goes through show/hide/show
+ // it doesn't go through the animation.
+ fragment.mHiddenChanged = !fragment.mHiddenChanged;
}
}
-
- public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
+
+ public void detachFragment(Fragment fragment) {
if (DEBUG) Log.v(TAG, "detach: " + fragment);
if (!fragment.mDetached) {
fragment.mDetached = true;
@@ -1346,12 +1456,11 @@
mNeedMenuInvalidate = true;
}
fragment.mAdded = false;
- moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false);
}
}
}
- public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
+ public void attachFragment(Fragment fragment) {
if (DEBUG) Log.v(TAG, "attach: " + fragment);
if (fragment.mDetached) {
fragment.mDetached = false;
@@ -1368,7 +1477,6 @@
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
- moveToState(fragment, mCurState, transition, transitionStyle, false);
}
}
}
@@ -1447,7 +1555,7 @@
* @param allowStateLoss whether to allow loss of state information
* @throws IllegalStateException if the activity has been destroyed
*/
- public void enqueueAction(Runnable action, boolean allowStateLoss) {
+ public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
@@ -1456,10 +1564,25 @@
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
- mPendingActions = new ArrayList<Runnable>();
+ mPendingActions = new ArrayList<>();
}
mPendingActions.add(action);
- if (mPendingActions.size() == 1) {
+ scheduleCommit();
+ }
+ }
+
+ /**
+ * Schedules the execution when one hasn't been scheduled already. This should happen
+ * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when
+ * a postponed transaction has been started with
+ * {@link Fragment#startPostponedEnterTransition()}
+ */
+ private void scheduleCommit() {
+ synchronized (this) {
+ boolean postponeReady =
+ mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
+ boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
+ if (postponeReady || pendingReady) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
@@ -1522,7 +1645,13 @@
}
}
- public void execSingleAction(Runnable action, boolean allowStateLoss) {
+ /**
+ * Broken out from exec*, this prepares for gathering and executing operations.
+ *
+ * @param allowStateLoss true if state loss should be ignored or false if it should be
+ * checked.
+ */
+ private void ensureExecReady(boolean allowStateLoss) {
if (mExecutingActions) {
throw new IllegalStateException("FragmentManager is already executing transactions");
}
@@ -1535,55 +1664,50 @@
checkStateLoss();
}
- mExecutingActions = true;
- try {
- action.run();
- } finally {
- mExecutingActions = false;
+ if (mTmpRecords == null) {
+ mTmpRecords = new ArrayList<>();
+ mTmpIsPop = new ArrayList<>();
+ }
+ executePostponedTransaction(null, null);
+ }
+
+ public void execSingleAction(OpGenerator action, boolean allowStateLoss) {
+ ensureExecReady(allowStateLoss);
+ if (action.generateOps(mTmpRecords, mTmpIsPop)) {
+ mExecutingActions = true;
+ try {
+ optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
+ } finally {
+ cleanupExec();
+ }
}
doPendingDeferredStart();
}
/**
+ * Broken out of exec*, this cleans up the mExecutingActions and the temporary structures
+ * used in executing operations.
+ */
+ private void cleanupExec() {
+ mExecutingActions = false;
+ mTmpIsPop.clear();
+ mTmpRecords.clear();
+ }
+
+ /**
* Only call from main thread!
*/
public boolean execPendingActions() {
- if (mExecutingActions) {
- throw new IllegalStateException("Recursive entry to executePendingTransactions");
- }
-
- if (Looper.myLooper() != mHost.getHandler().getLooper()) {
- throw new IllegalStateException("Must be called from main thread of process");
- }
+ ensureExecReady(true);
boolean didSomething = false;
-
- while (true) {
- int numActions;
-
- synchronized (this) {
- if (mPendingActions == null || mPendingActions.size() == 0) {
- break;
- }
-
- numActions = mPendingActions.size();
- if (mTmpActions == null || mTmpActions.length < numActions) {
- mTmpActions = new Runnable[numActions];
- }
- mPendingActions.toArray(mTmpActions);
- mPendingActions.clear();
- mHost.getHandler().removeCallbacks(mExecCommit);
- }
-
+ while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
mExecutingActions = true;
try {
- for (int i = 0; i < numActions; i++) {
- mTmpActions[i].run();
- mTmpActions[i] = null;
- }
+ optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
} finally {
- mExecutingActions = false;
+ cleanupExec();
}
didSomething = true;
}
@@ -1593,6 +1717,382 @@
return didSomething;
}
+ /**
+ * Complete the execution of transactions that have previously been postponed, but are
+ * now ready.
+ */
+ private void executePostponedTransaction(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop) {
+ int numPostponed = mPostponedTransactions == null ? 0 : mPostponedTransactions.size();
+ for (int i = 0; i < numPostponed; i++) {
+ StartEnterTransitionListener listener = mPostponedTransactions.get(i);
+ if (records != null && !listener.mIsBack) {
+ int index = records.indexOf(listener.mRecord);
+ if (index != -1 && isRecordPop.get(index)) {
+ listener.cancelTransaction();
+ continue;
+ }
+ }
+ if (listener.isReady() || (records != null &&
+ listener.mRecord.interactsWith(records, 0, records.size()))) {
+ mPostponedTransactions.remove(i);
+ i--;
+ numPostponed--;
+ int index;
+ if (records != null && !listener.mIsBack &&
+ (index = records.indexOf(listener.mRecord)) != -1 &&
+ isRecordPop.get(index)) {
+ // This is popping a postponed transaction
+ listener.cancelTransaction();
+ } else {
+ listener.completeTransaction();
+ }
+ }
+ }
+ }
+
+ /**
+ * Optimizes BackStackRecord operations. This method merges operations of proximate records
+ * that allow optimization. See {@link FragmentTransaction#setAllowOptimization(boolean)}.
+ * <p>
+ * For example, a transaction that adds to the back stack and then another that pops that
+ * back stack record will be optimized.
+ * <p>
+ * Likewise, two transactions committed that are executed at the same time will be optimized
+ * as well as two pop operations executed together.
+ *
+ * @param records The records pending execution
+ * @param isRecordPop The direction that these records are being run.
+ */
+ private void optimizeAndExecuteOps(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop) {
+ if (records == null || records.isEmpty()) {
+ return;
+ }
+
+ if (isRecordPop == null || records.size() != isRecordPop.size()) {
+ throw new IllegalStateException("Internal error with the back stack records");
+ }
+
+ // Force start of any postponed transactions that interact with scheduled transactions:
+ executePostponedTransaction(records, isRecordPop);
+
+ final int numRecords = records.size();
+ int startIndex = 0;
+ for (int recordNum = 0; recordNum < numRecords; recordNum++) {
+ final boolean canOptimize = records.get(recordNum).mAllowOptimization;
+ if (!canOptimize) {
+ // execute all previous transactions
+ if (startIndex != recordNum) {
+ executeOpsTogether(records, isRecordPop, startIndex, recordNum);
+ }
+ // execute all unoptimized together
+ int optimizeEnd;
+ for (optimizeEnd = recordNum + 1; optimizeEnd < numRecords; optimizeEnd++) {
+ if (records.get(optimizeEnd).mAllowOptimization) {
+ break;
+ }
+ }
+ executeOpsTogether(records, isRecordPop, recordNum, optimizeEnd);
+ startIndex = optimizeEnd;
+ recordNum = optimizeEnd - 1;
+ }
+ }
+ if (startIndex != numRecords) {
+ executeOpsTogether(records, isRecordPop, startIndex, numRecords);
+ }
+ }
+
+ /**
+ * Optimizes a subset of a list of BackStackRecords, all of which either allow optimization or
+ * do not allow optimization.
+ * @param records A list of BackStackRecords that are to be optimized
+ * @param isRecordPop The direction that these records are being run.
+ * @param startIndex The index of the first record in <code>records</code> to be optimized
+ * @param endIndex One more than the final record index in <code>records</code> to optimize.
+ */
+ private void executeOpsTogether(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
+ final boolean allowOptimization = records.get(startIndex).mAllowOptimization;
+ boolean addToBackStack = false;
+ if (mTmpAddedFragments == null) {
+ mTmpAddedFragments = new ArrayList<>();
+ } else {
+ mTmpAddedFragments.clear();
+ }
+ if (mAdded != null) {
+ mTmpAddedFragments.addAll(mAdded);
+ }
+ for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
+ final BackStackRecord record = records.get(recordNum);
+ final boolean isPop = isRecordPop.get(recordNum);
+ if (!isPop) {
+ record.expandReplaceOps(mTmpAddedFragments);
+ }
+ final int bumpAmount = isPop ? -1 : 1;
+ record.bumpBackStackNesting(bumpAmount);
+ addToBackStack = addToBackStack || record.mAddToBackStack;
+ }
+ mTmpAddedFragments.clear();
+
+ if (!allowOptimization) {
+ FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
+ false);
+ }
+ executeOps(records, isRecordPop, startIndex, endIndex);
+
+ int postponeIndex = endIndex;
+ if (allowOptimization) {
+ moveFragmentsToInvisible();
+ postponeIndex = postponePostponableTransactions(records, isRecordPop,
+ startIndex, endIndex);
+ }
+
+ if (postponeIndex != startIndex && allowOptimization) {
+ // need to run something now
+ FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
+ postponeIndex, true);
+ moveToState(mCurState);
+ }
+
+ for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
+ final BackStackRecord record = records.get(recordNum);
+ final boolean isPop = isRecordPop.get(recordNum);
+ if (isPop && record.mIndex >= 0) {
+ freeBackStackIndex(record.mIndex);
+ record.mIndex = -1;
+ }
+ }
+
+ if (addToBackStack) {
+ reportBackStackChanged();
+ }
+ }
+
+ /**
+ * Examine all transactions and determine which ones are marked as postponed. Those will
+ * have their operations rolled back and moved to the end of the record list (up to endIndex).
+ * It will also add the postponed transaction to the queue.
+ *
+ * @param records A list of BackStackRecords that should be checked.
+ * @param isRecordPop The direction that these records are being run.
+ * @param startIndex The index of the first record in <code>records</code> to be checked
+ * @param endIndex One more than the final record index in <code>records</code> to be checked.
+ * @return The index of the first postponed transaction or endIndex if no transaction was
+ * postponed.
+ */
+ private int postponePostponableTransactions(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
+ int postponeIndex = endIndex;
+ for (int i = endIndex - 1; i >= startIndex; i--) {
+ final BackStackRecord record = records.get(i);
+ final boolean isPop = isRecordPop.get(i);
+ boolean isPostponed = record.isPostponed() &&
+ !record.interactsWith(records, i + 1, endIndex);
+ if (isPostponed) {
+ if (mPostponedTransactions == null) {
+ mPostponedTransactions = new ArrayList<>();
+ }
+ StartEnterTransitionListener listener =
+ new StartEnterTransitionListener(record, isPop);
+ mPostponedTransactions.add(listener);
+ record.setOnStartPostponedListener(listener);
+
+ // roll back the transaction
+ if (isPop) {
+ record.executeOps();
+ } else {
+ record.executePopOps();
+ }
+
+ // move to the end
+ postponeIndex--;
+ if (i != postponeIndex) {
+ records.remove(i);
+ records.add(postponeIndex, record);
+ }
+
+ // different views may be visible now
+ moveFragmentsToInvisible();
+ }
+ }
+ return postponeIndex;
+ }
+
+ /**
+ * When a postponed transaction is ready to be started, this completes the transaction,
+ * removing, hiding, or showing views as well as starting the animations and transitions.
+ * <p>
+ * {@code runtransitions} is set to false when the transaction postponement was interrupted
+ * abnormally -- normally by a new transaction being started that affects the postponed
+ * transaction.
+ *
+ * @param record The transaction to run
+ * @param isPop true if record is popping or false if it is adding
+ * @param runTransitions true if the fragment transition should be run or false otherwise.
+ * @param moveToState true if the state should be changed after executing the operations.
+ * This is false when the transaction is canceled when a postponed
+ * transaction is popped.
+ */
+ private void completeExecute(BackStackRecord record, boolean isPop, boolean runTransitions,
+ boolean moveToState) {
+ ArrayList<BackStackRecord> records = new ArrayList<>(1);
+ ArrayList<Boolean> isRecordPop = new ArrayList<>(1);
+ records.add(record);
+ isRecordPop.add(isPop);
+ executeOps(records, isRecordPop, 0, 1);
+ if (runTransitions) {
+ FragmentTransition.startTransitions(this, records, isRecordPop, 0, 1, true);
+ }
+ if (moveToState) {
+ moveToState(mCurState);
+ } else if (mActive != null) {
+ final int numActive = mActive.size();
+ for (int i = 0; i < numActive; i++) {
+ // Allow added fragments to be removed during the pop since we aren't going
+ // to move them to the final state with moveToState(mCurState).
+ Fragment fragment = mActive.get(i);
+ if (fragment.mView != null && fragment.mIsNewlyAdded &&
+ record.interactsWith(fragment.mContainerId)) {
+ fragment.mIsNewlyAdded = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Find a fragment within the fragment's container whose View should be below the passed
+ * fragment. {@code null} is returned when the fragment has no View or if there should be
+ * no fragment with a View below the given fragment.
+ *
+ * As an example, if mAdded has two Fragments with Views sharing the same container:
+ * FragmentA
+ * FragmentB
+ *
+ * Then, when processing FragmentB, FragmentA will be returned. If, however, FragmentA
+ * had no View, null would be returned.
+ *
+ * @param f The fragment that may be on top of another fragment.
+ * @return The fragment with a View under f, if one exists or null if f has no View or
+ * there are no fragments with Views in the same container.
+ */
+ private Fragment findFragmentUnder(Fragment f) {
+ final ViewGroup container = f.mContainer;
+ final View view = f.mView;
+
+ if (container == null || view == null) {
+ return null;
+ }
+
+ final int fragmentIndex = mAdded.indexOf(f);
+ for (int i = fragmentIndex - 1; i >= 0; i--) {
+ Fragment underFragment = mAdded.get(i);
+ if (underFragment.mContainer == container && underFragment.mView != null) {
+ // Found the fragment under this one
+ return underFragment;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Run the operations in the BackStackRecords, either to push or pop.
+ *
+ * @param records The list of records whose operations should be run.
+ * @param isRecordPop The direction that these records are being run.
+ * @param startIndex The index of the first entry in records to run.
+ * @param endIndex One past the index of the final entry in records to run.
+ */
+ private static void executeOps(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
+ for (int i = startIndex; i < endIndex; i++) {
+ final BackStackRecord record = records.get(i);
+ final boolean isPop = isRecordPop.get(i);
+ if (isPop) {
+ record.executePopOps();
+ } else {
+ record.executeOps();
+ }
+ }
+ }
+
+ /**
+ * Ensure that fragments that are added are moved to at least the CREATED state.
+ * Any newly-added Views are made INVISIBLE so that the Transaction can be postponed
+ * with {@link Fragment#postponeEnterTransition()}.
+ */
+ private void moveFragmentsToInvisible() {
+ if (mCurState < Fragment.CREATED) {
+ return;
+ }
+ // We want to leave the fragment in the started state
+ final int state = Math.min(mCurState, Fragment.STARTED);
+ final int numAdded = mAdded == null ? 0 : mAdded.size();
+ for (int i = 0; i < numAdded; i++) {
+ Fragment fragment = mAdded.get(i);
+ if (fragment.mState < state) {
+ moveToState(fragment, state, fragment.getNextAnim(), fragment.getNextTransition(), false);
+ if (fragment.mView != null && !fragment.mHidden && fragment.mIsNewlyAdded) {
+ fragment.mView.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+ }
+
+ /**
+ * Starts all postponed transactions regardless of whether they are ready or not.
+ */
+ private void forcePostponedTransactions() {
+ if (mPostponedTransactions != null) {
+ while (!mPostponedTransactions.isEmpty()) {
+ mPostponedTransactions.remove(0).completeTransaction();
+ }
+ }
+ }
+
+ /**
+ * Ends the animations of fragments so that they immediately reach the end state.
+ * This is used prior to saving the state so that the correct state is saved.
+ */
+ private void endAnimatingAwayFragments() {
+ final int numFragments = mActive == null ? 0 : mActive.size();
+ for (int i = 0; i < numFragments; i++) {
+ Fragment fragment = mActive.get(i);
+ if (fragment != null && fragment.getAnimatingAway() != null) {
+ // Give up waiting for the animation and just end it.
+ fragment.getAnimatingAway().end();
+ }
+ }
+ }
+
+ /**
+ * Adds all records in the pending actions to records and whether they are add or pop
+ * operations to isPop. After executing, the pending actions will be empty.
+ *
+ * @param records All pending actions will generate BackStackRecords added to this.
+ * This contains the transactions, in order, to execute.
+ * @param isPop All pending actions will generate booleans to add to this. This contains
+ * an entry for each entry in records to indicate whether or not it is a
+ * pop action.
+ */
+ private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isPop) {
+ int numActions;
+ synchronized (this) {
+ if (mPendingActions == null || mPendingActions.size() == 0) {
+ return false;
+ }
+
+ numActions = mPendingActions.size();
+ for (int i = 0; i < numActions; i++) {
+ mPendingActions.get(i).generateOps(records, isPop);
+ }
+ mPendingActions.clear();
+ mHost.getHandler().removeCallbacks(mExecCommit);
+ }
+ return numActions > 0;
+ }
+
void doPendingDeferredStart() {
if (mHavePendingDeferredStart) {
boolean loadersRunning = false;
@@ -1624,24 +2124,19 @@
mBackStack.add(state);
reportBackStackChanged();
}
-
- boolean popBackStackState(Handler handler, String name, int id, int flags) {
+
+ boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
+ String name, int id, int flags) {
if (mBackStack == null) {
return false;
}
- if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
- int last = mBackStack.size()-1;
+ if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) {
+ int last = mBackStack.size() - 1;
if (last < 0) {
return false;
}
- final BackStackRecord bss = mBackStack.remove(last);
- SparseArray<BackStackRecord.FragmentContainerTransition> transitioningFragments =
- new SparseArray<>();
- if (mCurState >= Fragment.CREATED) {
- bss.calculateBackFragments(transitioningFragments);
- }
- bss.popFromBackStack(true, null, transitioningFragments);
- reportBackStackChanged();
+ records.add(mBackStack.remove(last));
+ isRecordPop.add(true);
} else {
int index = -1;
if (name != null || id >= 0) {
@@ -1678,25 +2173,10 @@
if (index == mBackStack.size()-1) {
return false;
}
- final ArrayList<BackStackRecord> states
- = new ArrayList<BackStackRecord>();
- for (int i=mBackStack.size()-1; i>index; i--) {
- states.add(mBackStack.remove(i));
+ for (int i = mBackStack.size() - 1; i > index; i--) {
+ records.add(mBackStack.remove(i));
+ isRecordPop.add(true);
}
- final int LAST = states.size()-1;
- SparseArray<BackStackRecord.FragmentContainerTransition> transitioningFragments =
- new SparseArray<>();
- if (mCurState >= Fragment.CREATED) {
- for (int i = 0; i <= LAST; i++) {
- states.get(i).calculateBackFragments(transitioningFragments);
- }
- }
- BackStackRecord.TransitionState state = null;
- for (int i=0; i<=LAST; i++) {
- if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
- state = states.get(i).popFromBackStack(i == LAST, state, transitioningFragments);
- }
- reportBackStackChanged();
}
return true;
}
@@ -1795,6 +2275,8 @@
Parcelable saveAllState() {
// Make sure all pending operations have now been executed to get
// our state update-to-date.
+ forcePostponedTransactions();
+ endAnimatingAwayFragments();
execPendingActions();
mStateSaved = true;
@@ -2036,40 +2518,40 @@
public void dispatchCreate() {
mStateSaved = false;
- moveToState(Fragment.CREATED, false);
+ moveToState(Fragment.CREATED);
}
public void dispatchActivityCreated() {
mStateSaved = false;
- moveToState(Fragment.ACTIVITY_CREATED, false);
+ moveToState(Fragment.ACTIVITY_CREATED);
}
public void dispatchStart() {
mStateSaved = false;
- moveToState(Fragment.STARTED, false);
+ moveToState(Fragment.STARTED);
}
public void dispatchResume() {
mStateSaved = false;
- moveToState(Fragment.RESUMED, false);
+ moveToState(Fragment.RESUMED);
}
public void dispatchPause() {
- moveToState(Fragment.STARTED, false);
+ moveToState(Fragment.STARTED);
}
public void dispatchStop() {
- moveToState(Fragment.STOPPED, false);
+ moveToState(Fragment.STOPPED);
}
public void dispatchDestroyView() {
- moveToState(Fragment.CREATED, false);
+ moveToState(Fragment.CREATED);
}
public void dispatchDestroy() {
mDestroyed = true;
execPendingActions();
- moveToState(Fragment.INITIALIZING, false);
+ moveToState(Fragment.INITIALIZING);
mHost = null;
mContainer = null;
mParent = null;
@@ -2363,4 +2845,121 @@
LayoutInflater.Factory2 getLayoutInflaterFactory() {
return this;
}
+
+ /**
+ * An add or pop transaction to be scheduled for the UI thread.
+ */
+ interface OpGenerator {
+ /**
+ * Generate transactions to add to {@code records} and whether or not the transaction is
+ * an add or pop to {@code isRecordPop}.
+ *
+ * records and isRecordPop must be added equally so that each transaction in records
+ * matches the boolean for whether or not it is a pop in isRecordPop.
+ *
+ * @param records A list to add transactions to.
+ * @param isRecordPop A list to add whether or not the transactions added to records is
+ * a pop transaction.
+ * @return true if something was added or false otherwise.
+ */
+ boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop);
+ }
+
+ /**
+ * A pop operation OpGenerator. This will be run on the UI thread and will generate the
+ * transactions that will be popped if anything can be popped.
+ */
+ private class PopBackStackState implements OpGenerator {
+ final String mName;
+ final int mId;
+ final int mFlags;
+
+ public PopBackStackState(String name, int id, int flags) {
+ mName = name;
+ mId = id;
+ mFlags = flags;
+ }
+
+ @Override
+ public boolean generateOps(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop) {
+ return popBackStackState(records, isRecordPop, mName, mId, mFlags);
+ }
+ }
+
+ /**
+ * A listener for a postponed transaction. This waits until
+ * {@link Fragment#startPostponedEnterTransition()} is called or a transaction is started
+ * that interacts with this one, based on interactions with the fragment container.
+ */
+ static class StartEnterTransitionListener
+ implements Fragment.OnStartEnterTransitionListener {
+ private final boolean mIsBack;
+ private final BackStackRecord mRecord;
+ private int mNumPostponed;
+
+ public StartEnterTransitionListener(BackStackRecord record, boolean isBack) {
+ mIsBack = isBack;
+ mRecord = record;
+ }
+
+ /**
+ * Called from {@link Fragment#startPostponedEnterTransition()}, this decreases the
+ * number of Fragments that are postponed. This may cause the transaction to schedule
+ * to finish running and run transitions and animations.
+ */
+ @Override
+ public void onStartEnterTransition() {
+ mNumPostponed--;
+ if (mNumPostponed != 0) {
+ return;
+ }
+ mRecord.mManager.scheduleCommit();
+ }
+
+ /**
+ * Called from {@link Fragment#
+ * setOnStartEnterTransitionListener(Fragment.OnStartEnterTransitionListener)}, this
+ * increases the number of fragments that are postponed as part of this transaction.
+ */
+ @Override
+ public void startListening() {
+ mNumPostponed++;
+ }
+
+ /**
+ * @return true if there are no more postponed fragments as part of the transaction.
+ */
+ public boolean isReady() {
+ return mNumPostponed == 0;
+ }
+
+ /**
+ * Completes the transaction and start the animations and transitions. This may skip
+ * the transitions if this is called before all fragments have called
+ * {@link Fragment#startPostponedEnterTransition()}.
+ */
+ public void completeTransaction() {
+ final boolean canceled;
+ canceled = mNumPostponed > 0;
+ FragmentManagerImpl manager = mRecord.mManager;
+ final int numAdded = manager.mAdded.size();
+ for (int i = 0; i < numAdded; i++) {
+ final Fragment fragment = manager.mAdded.get(i);
+ fragment.setOnStartEnterTransitionListener(null);
+ if (canceled && fragment.isPostponed()) {
+ fragment.startPostponedEnterTransition();
+ }
+ }
+ mRecord.mManager.completeExecute(mRecord, mIsBack, !canceled, true);
+ }
+
+ /**
+ * Cancels this transaction instead of completing it. That means that the state isn't
+ * changed, so the pop results in no change to the state.
+ */
+ public void cancelTransaction() {
+ mRecord.mManager.completeExecute(mRecord, mIsBack, false, false);
+ }
+ }
}
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 633e85b..25a7839 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -260,6 +260,32 @@
public abstract FragmentTransaction setBreadCrumbShortTitle(CharSequence text);
/**
+ * Sets whether or not to allow optimizing operations within and across
+ * transactions. Optimizing fragment transaction's operations can eliminate
+ * operations that cancel. For example, if two transactions are executed
+ * together, one that adds a fragment A and the next replaces it with fragment B,
+ * the operations will cancel and only fragment B will be added. That means that
+ * fragment A may not go through the creation/destruction lifecycle.
+ * <p>
+ * The side effect of optimization is that fragments may have state changes
+ * out of the expected order. For example, one transaction adds fragment A,
+ * a second adds fragment B, then a third removes fragment A. Without optimization,
+ * fragment B could expect that while it is being created, fragment A will also
+ * exist because fragment A will be removed after fragment B was added.
+ * With optimization, fragment B cannot expect fragment A to exist when
+ * it has been created because fragment A's add/remove will be optimized out.
+ * <p>
+ * The default is {@code false} for applications targeting version
+ * versions prior to O and {@code true} for applications targeting O and
+ * later.
+ *
+ * @param allowOptimization {@code true} to enable optimizing operations
+ * or {@code false} to disable optimizing
+ * operations on this transaction.
+ */
+ public abstract FragmentTransaction setAllowOptimization(boolean allowOptimization);
+
+ /**
* Schedules a commit of this transaction. The commit does
* not happen immediately; it will be scheduled as work on the main thread
* to be done the next time that thread is ready.
diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java
new file mode 100644
index 0000000..d27dff5
--- /dev/null
+++ b/core/java/android/app/FragmentTransition.java
@@ -0,0 +1,1363 @@
+/*
+ * 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.app;
+
+import android.graphics.Rect;
+import android.os.Build;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Contains the Fragment Transition functionality for both optimized and unoptimized
+ * Fragment Transactions. With optimized fragment transactions, all Views have been
+ * added to the View hierarchy prior to calling startTransitions. With
+ */
+class FragmentTransition {
+ /**
+ * The inverse of all BackStackRecord operation commands. This assumes that
+ * REPLACE operations have already been replaced by add/remove operations.
+ */
+ private static final int[] INVERSE_OPS = {
+ BackStackRecord.OP_NULL, // inverse of OP_NULL (error)
+ BackStackRecord.OP_REMOVE, // inverse of OP_ADD
+ BackStackRecord.OP_NULL, // inverse of OP_REPLACE (error)
+ BackStackRecord.OP_ADD, // inverse of OP_REMOVE
+ BackStackRecord.OP_SHOW, // inverse of OP_HIDE
+ BackStackRecord.OP_HIDE, // inverse of OP_SHOW
+ BackStackRecord.OP_ATTACH, // inverse of OP_DETACH
+ BackStackRecord.OP_DETACH, // inverse of OP_ATTACH
+ };
+
+ /**
+ * The main entry point for Fragment Transitions, this starts the transitions
+ * set on the leaving Fragment's {@link Fragment#getExitTransition()}, the
+ * entering Fragment's {@link Fragment#getEnterTransition()} and
+ * {@link Fragment#getSharedElementEnterTransition()}. When popping,
+ * the leaving Fragment's {@link Fragment#getReturnTransition()} and
+ * {@link Fragment#getSharedElementReturnTransition()} and the entering
+ * {@link Fragment#getReenterTransition()} will be run.
+ * <p>
+ * With optimized Fragment Transitions, all Views have been added to the
+ * View hierarchy prior to calling this method. The incoming Fragment's Views
+ * will be INVISIBLE. With unoptimized Fragment Transitions, this method
+ * is called before any change has been made to the hierarchy. That means
+ * that the added Fragments have not created their Views yet and the hierarchy
+ * is unknown.
+ *
+ * @param fragmentManager The executing FragmentManagerImpl
+ * @param records The list of transactions being executed.
+ * @param isRecordPop For each transaction, whether it is a pop transaction or not.
+ * @param startIndex The first index into records and isRecordPop to execute as
+ * part of this transition.
+ * @param endIndex One past the last index into records and isRecordPop to execute
+ * as part of this transition.
+ * @param isOptimized true if this is an optimized transaction, meaning that the
+ * Views of incoming fragments have been added. false if the
+ * transaction has yet to be run and Views haven't been created.
+ */
+ static void startTransitions(FragmentManagerImpl fragmentManager,
+ ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
+ int startIndex, int endIndex, boolean isOptimized) {
+ if (fragmentManager.mCurState < Fragment.CREATED) {
+ return;
+ }
+ SparseArray<FragmentContainerTransition> transitioningFragments =
+ new SparseArray<>();
+ for (int i = startIndex; i < endIndex; i++) {
+ final BackStackRecord record = records.get(i);
+ final boolean isPop = isRecordPop.get(i);
+ if (isPop) {
+ calculatePopFragments(record, transitioningFragments, isOptimized);
+ } else {
+ calculateFragments(record, transitioningFragments, isOptimized);
+ }
+ }
+
+ if (transitioningFragments.size() != 0) {
+ final View nonExistentView = new View(fragmentManager.mHost.getContext());
+ final int numContainers = transitioningFragments.size();
+ for (int i = 0; i < numContainers; i++) {
+ int containerId = transitioningFragments.keyAt(i);
+ ArrayMap<String, String> nameOverrides = calculateNameOverrides(containerId,
+ records, isRecordPop, startIndex, endIndex);
+
+ FragmentContainerTransition containerTransition = transitioningFragments.valueAt(i);
+
+ if (isOptimized) {
+ configureTransitionsOptimized(fragmentManager, containerId,
+ containerTransition, nonExistentView, nameOverrides);
+ } else {
+ configureTransitionsUnoptimized(fragmentManager, containerId,
+ containerTransition, nonExistentView, nameOverrides);
+ }
+ }
+ }
+ }
+
+ /**
+ * Iterates through the transactions that affect a given fragment container
+ * and tracks the shared element names across transactions. This is most useful
+ * in pop transactions where the names of shared elements are known.
+ *
+ * @param containerId The container ID that is executing the transition.
+ * @param records The list of transactions being executed.
+ * @param isRecordPop For each transaction, whether it is a pop transaction or not.
+ * @param startIndex The first index into records and isRecordPop to execute as
+ * part of this transition.
+ * @param endIndex One past the last index into records and isRecordPop to execute
+ * as part of this transition.
+ * @return A map from the initial shared element name to the final shared element name
+ * before any onMapSharedElements is run.
+ */
+ private static ArrayMap<String, String> calculateNameOverrides(int containerId,
+ ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
+ int startIndex, int endIndex) {
+ ArrayMap<String, String> nameOverrides = new ArrayMap<>();
+ for (int recordNum = endIndex - 1; recordNum >= startIndex; recordNum--) {
+ final BackStackRecord record = records.get(recordNum);
+ if (!record.interactsWith(containerId)) {
+ continue;
+ }
+ final boolean isPop = isRecordPop.get(recordNum);
+ if (record.mSharedElementSourceNames != null) {
+ final int numSharedElements = record.mSharedElementSourceNames.size();
+ final ArrayList<String> sources;
+ final ArrayList<String> targets;
+ if (isPop) {
+ targets = record.mSharedElementSourceNames;
+ sources = record.mSharedElementTargetNames;
+ } else {
+ sources = record.mSharedElementSourceNames;
+ targets = record.mSharedElementTargetNames;
+ }
+ for (int i = 0; i < numSharedElements; i++) {
+ String sourceName = sources.get(i);
+ String targetName = targets.get(i);
+ String previousTarget = nameOverrides.remove(targetName);
+ if (previousTarget != null) {
+ nameOverrides.put(sourceName, previousTarget);
+ } else {
+ nameOverrides.put(sourceName, targetName);
+ }
+ }
+ }
+ }
+ return nameOverrides;
+ }
+
+ /**
+ * Configures a transition for a single fragment container for which the transaction was
+ * optimized. That means that all Fragment Views have been added and incoming fragment
+ * Views are marked invisible.
+ *
+ * @param fragmentManager The executing FragmentManagerImpl
+ * @param containerId The container ID that is executing the transition.
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+ * prevent transitions from acting on other Views when there is no
+ * other target.
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ */
+ private static void configureTransitionsOptimized(FragmentManagerImpl fragmentManager,
+ int containerId, FragmentContainerTransition fragments,
+ View nonExistentView, ArrayMap<String, String> nameOverrides) {
+ ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ if (sceneRoot == null) {
+ return;
+ }
+ final Fragment inFragment = fragments.lastIn;
+ final Fragment outFragment = fragments.firstOut;
+ final boolean inIsPop = fragments.lastInIsPop;
+ final boolean outIsPop = fragments.firstOutIsPop;
+
+ ArrayList<View> sharedElementsIn = new ArrayList<>();
+ ArrayList<View> sharedElementsOut = new ArrayList<>();
+ Transition enterTransition = getEnterTransition(inFragment, inIsPop);
+ Transition exitTransition = getExitTransition(outFragment, outIsPop);
+
+ TransitionSet sharedElementTransition = configureSharedElementsOptimized(sceneRoot,
+ nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
+ enterTransition, exitTransition);
+
+ if (enterTransition == null && sharedElementTransition == null &&
+ exitTransition == null) {
+ return; // no transitions!
+ }
+
+ ArrayList<View> exitingViews = configureEnteringExitingViews(exitTransition,
+ outFragment, sharedElementsOut, nonExistentView);
+
+ ArrayList<View> enteringViews = configureEnteringExitingViews(enterTransition,
+ inFragment, sharedElementsIn, nonExistentView);
+
+ setViewVisibility(enteringViews, View.INVISIBLE);
+
+ Transition transition = mergeTransitions(enterTransition, exitTransition,
+ sharedElementTransition, inFragment, inIsPop);
+
+ if (transition != null) {
+ replaceHide(exitTransition, outFragment, exitingViews);
+ transition.setNameOverrides(nameOverrides);
+ scheduleRemoveTargets(transition,
+ enterTransition, enteringViews, exitTransition, exitingViews,
+ sharedElementTransition, sharedElementsIn);
+ TransitionManager.beginDelayedTransition(sceneRoot, transition);
+ setViewVisibility(enteringViews, View.VISIBLE);
+ // Swap the shared element targets
+ if (sharedElementTransition != null) {
+ sharedElementTransition.getTargets().clear();
+ sharedElementTransition.getTargets().addAll(sharedElementsIn);
+ replaceTargets(sharedElementTransition, sharedElementsOut, sharedElementsIn);
+ }
+ }
+ }
+
+ /**
+ * Configures a transition for a single fragment container for which the transaction was
+ * not optimized. That means that the transaction has not been executed yet, so incoming
+ * Views are not yet known.
+ *
+ * @param fragmentManager The executing FragmentManagerImpl
+ * @param containerId The container ID that is executing the transition.
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+ * prevent transitions from acting on other Views when there is no
+ * other target.
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ */
+ private static void configureTransitionsUnoptimized(FragmentManagerImpl fragmentManager,
+ int containerId, FragmentContainerTransition fragments,
+ View nonExistentView, ArrayMap<String, String> nameOverrides) {
+ ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ if (sceneRoot == null) {
+ return;
+ }
+ final Fragment inFragment = fragments.lastIn;
+ final Fragment outFragment = fragments.firstOut;
+ final boolean inIsPop = fragments.lastInIsPop;
+ final boolean outIsPop = fragments.firstOutIsPop;
+
+ Transition enterTransition = getEnterTransition(inFragment, inIsPop);
+ Transition exitTransition = getExitTransition(outFragment, outIsPop);
+
+ ArrayList<View> sharedElementsOut = new ArrayList<>();
+ ArrayList<View> sharedElementsIn = new ArrayList<>();
+
+ TransitionSet sharedElementTransition = configureSharedElementsUnoptimized(sceneRoot,
+ nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
+ enterTransition, exitTransition);
+
+ if (enterTransition == null && sharedElementTransition == null &&
+ exitTransition == null) {
+ return; // no transitions!
+ }
+
+ ArrayList<View> exitingViews = configureEnteringExitingViews(exitTransition,
+ outFragment, sharedElementsOut, nonExistentView);
+
+ if (exitingViews == null || exitingViews.isEmpty()) {
+ exitTransition = null;
+ }
+
+ if (enterTransition != null) {
+ // Ensure the entering transition doesn't target anything until the views are made
+ // visible
+ enterTransition.addTarget(nonExistentView);
+ }
+
+ Transition transition = mergeTransitions(enterTransition, exitTransition,
+ sharedElementTransition, inFragment, fragments.lastInIsPop);
+
+ if (transition != null) {
+ transition.setNameOverrides(nameOverrides);
+ final ArrayList<View> enteringViews = new ArrayList<>();
+ scheduleRemoveTargets(transition,
+ enterTransition, enteringViews, exitTransition, exitingViews,
+ sharedElementTransition, sharedElementsIn);
+ scheduleTargetChange(sceneRoot, inFragment, nonExistentView, sharedElementsIn,
+ enterTransition, enteringViews, exitTransition, exitingViews);
+
+ TransitionManager.beginDelayedTransition(sceneRoot, transition);
+ }
+ }
+
+ /**
+ * Replace hide operations with visibility changes on the exiting views. Instead of making
+ * the entire fragment's view GONE, make each exiting view INVISIBLE. At the end of the
+ * transition, make the fragment's view GONE.
+ */
+ private static void replaceHide(Transition exitTransition, Fragment exitingFragment,
+ final ArrayList<View> exitingViews) {
+ if (exitingFragment != null && exitTransition != null && exitingFragment.mAdded
+ && exitingFragment.mHidden && exitingFragment.mHiddenChanged) {
+ exitingFragment.setHideReplaced(true);
+ final View fragmentView = exitingFragment.getView();
+ final ViewGroup container = exitingFragment.mContainer;
+ container.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ container.getViewTreeObserver().removeOnPreDrawListener(this);
+ setViewVisibility(exitingViews, View.INVISIBLE);
+ return true;
+ }
+ });
+ exitTransition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ transition.removeListener(this);
+ fragmentView.setVisibility(View.GONE);
+ setViewVisibility(exitingViews, View.VISIBLE);
+ }
+ });
+ }
+ }
+
+ /**
+ * This method is used for fragment transitions for unoptimized transactions to change the
+ * enter and exit transition targets after the call to
+ * {@link TransitionManager#beginDelayedTransition(ViewGroup, Transition)}. The exit transition
+ * must ensure that it does not target any Views and the enter transition must start targeting
+ * the Views of the incoming Fragment.
+ *
+ * @param sceneRoot The fragment container View
+ * @param inFragment The last fragment that is entering
+ * @param nonExistentView A view that does not exist in the hierarchy that is used as a
+ * transition target to ensure no View is targeted.
+ * @param sharedElementsIn The shared element Views of the incoming fragment
+ * @param enterTransition The enter transition of the incoming fragment
+ * @param enteringViews The entering Views of the incoming fragment
+ * @param exitTransition The exit transition of the outgoing fragment
+ * @param exitingViews The exiting views of the outgoing fragment
+ */
+ private static void scheduleTargetChange(final ViewGroup sceneRoot,
+ final Fragment inFragment, final View nonExistentView,
+ final ArrayList<View> sharedElementsIn,
+ final Transition enterTransition, final ArrayList<View> enteringViews,
+ final Transition exitTransition, final ArrayList<View> exitingViews) {
+
+ sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+
+ if (enterTransition != null) {
+ enterTransition.removeTarget(nonExistentView);
+ ArrayList<View> views = configureEnteringExitingViews(
+ enterTransition, inFragment, sharedElementsIn, nonExistentView);
+ enteringViews.addAll(views);
+ }
+
+ if (exitingViews != null) {
+ ArrayList<View> tempExiting = new ArrayList<>();
+ tempExiting.add(nonExistentView);
+ replaceTargets(exitTransition, exitingViews, tempExiting);
+ exitingViews.clear();
+ exitingViews.add(nonExistentView);
+ }
+
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Returns a TransitionSet containing the shared element transition. The wrapping TransitionSet
+ * targets all shared elements to ensure that no other Views are targeted. The shared element
+ * transition can then target any or all shared elements without worrying about accidentally
+ * targeting entering or exiting Views.
+ *
+ * @param inFragment The incoming fragment
+ * @param outFragment the outgoing fragment
+ * @param isPop True if this is a pop transaction or false if it is a normal (add) transaction.
+ * @return A TransitionSet wrapping the shared element transition or null if no such transition
+ * exists.
+ */
+ private static TransitionSet getSharedElementTransition(Fragment inFragment,
+ Fragment outFragment, boolean isPop) {
+ if (inFragment == null || outFragment == null) {
+ return null;
+ }
+ Transition transition = cloneTransition(isPop
+ ? outFragment.getSharedElementReturnTransition()
+ : inFragment.getSharedElementEnterTransition());
+ if (transition == null) {
+ return null;
+ }
+ TransitionSet transitionSet = new TransitionSet();
+ transitionSet.addTransition(transition);
+ return transitionSet;
+ }
+
+ /**
+ * Returns a clone of the enter transition or null if no such transition exists.
+ */
+ private static Transition getEnterTransition(Fragment inFragment, boolean isPop) {
+ if (inFragment == null) {
+ return null;
+ }
+ return cloneTransition(isPop ? inFragment.getReenterTransition() :
+ inFragment.getEnterTransition());
+ }
+
+ /**
+ * Returns a clone of the exit transition or null if no such transition exists.
+ */
+ private static Transition getExitTransition(Fragment outFragment, boolean isPop) {
+ if (outFragment == null) {
+ return null;
+ }
+ return cloneTransition(isPop ? outFragment.getReturnTransition() :
+ outFragment.getExitTransition());
+ }
+
+ /**
+ * Returns a clone of a transition or null if it is null
+ */
+ private static Transition cloneTransition(Transition transition) {
+ if (transition != null) {
+ transition = transition.clone();
+ }
+ return transition;
+ }
+
+ /**
+ * Configures the shared elements of an optimized fragment transaction's transition.
+ * This retrieves the shared elements of the outgoing and incoming fragments, maps the
+ * views, and sets up the epicenter on the transitions.
+ * <p>
+ * The epicenter of exit and shared element transitions is the first shared element
+ * in the outgoing fragment. The epicenter of the entering transition is the first shared
+ * element in the incoming fragment.
+ *
+ * @param sceneRoot The fragment container View
+ * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+ * prevent transitions from acting on other Views when there is no
+ * other target.
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @param sharedElementsOut A list modified to contain the shared elements in the outgoing
+ * fragment
+ * @param sharedElementsIn A list modified to contain the shared elements in the incoming
+ * fragment
+ * @param enterTransition The transition used for entering Views, modified by applying the
+ * epicenter
+ * @param exitTransition The transition used for exiting Views, modified by applying the
+ * epicenter
+ * @return The shared element transition or null if no shared elements exist
+ */
+ private static TransitionSet configureSharedElementsOptimized(final ViewGroup sceneRoot,
+ final View nonExistentView, ArrayMap<String, String> nameOverrides,
+ final FragmentContainerTransition fragments,
+ final ArrayList<View> sharedElementsOut,
+ final ArrayList<View> sharedElementsIn,
+ final Transition enterTransition, final Transition exitTransition) {
+ final Fragment inFragment = fragments.lastIn;
+ final Fragment outFragment = fragments.firstOut;
+ if (inFragment != null) {
+ inFragment.getView().setVisibility(View.VISIBLE);
+ }
+ if (inFragment == null || outFragment == null) {
+ return null; // no shared element without a fragment
+ }
+
+ final boolean inIsPop = fragments.lastInIsPop;
+ TransitionSet sharedElementTransition = nameOverrides.isEmpty() ? null
+ : getSharedElementTransition(inFragment, outFragment, inIsPop);
+
+ ArrayMap<String, View> outSharedElements = captureOutSharedElements(nameOverrides,
+ sharedElementTransition, fragments);
+
+ ArrayMap<String, View> inSharedElements = captureInSharedElements(nameOverrides,
+ sharedElementTransition, fragments);
+
+ if (nameOverrides.isEmpty()) {
+ sharedElementTransition = null;
+ } else {
+ sharedElementsOut.addAll(outSharedElements.values());
+ sharedElementsIn.addAll(inSharedElements.values());
+ }
+
+ if (enterTransition == null && exitTransition == null && sharedElementTransition == null) {
+ // don't call onSharedElementStart/End since there is no transition
+ return null;
+ }
+
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop, outSharedElements, true);
+
+ final Rect epicenter;
+ final View epicenterView;
+ if (sharedElementTransition != null) {
+ sharedElementsIn.add(nonExistentView);
+ setSharedElementTargets(sharedElementTransition, nonExistentView, sharedElementsOut);
+ final boolean outIsPop = fragments.firstOutIsPop;
+ final BackStackRecord outTransaction = fragments.firstOutTransaction;
+ setOutEpicenter(sharedElementTransition, exitTransition, outSharedElements, outIsPop,
+ outTransaction);
+ epicenter = new Rect();
+ epicenterView = getInEpicenterView(inSharedElements, fragments,
+ enterTransition, inIsPop);
+ if (epicenterView != null) {
+ enterTransition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return epicenter;
+ }
+ });
+ }
+ } else {
+ epicenter = null;
+ epicenterView = null;
+ }
+
+ sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+ inSharedElements, false);
+ if (epicenterView != null) {
+ epicenterView.getBoundsOnScreen(epicenter);
+ }
+ return true;
+ }
+ });
+ return sharedElementTransition;
+ }
+
+ /**
+ * Configures the shared elements of an unoptimized fragment transaction's transition.
+ * This retrieves the shared elements of the incoming fragments, and schedules capturing
+ * the incoming fragment's shared elements. It also maps the views, and sets up the epicenter
+ * on the transitions.
+ * <p>
+ * The epicenter of exit and shared element transitions is the first shared element
+ * in the outgoing fragment. The epicenter of the entering transition is the first shared
+ * element in the incoming fragment.
+ *
+ * @param sceneRoot The fragment container View
+ * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+ * prevent transitions from acting on other Views when there is no
+ * other target.
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @param sharedElementsOut A list modified to contain the shared elements in the outgoing
+ * fragment
+ * @param sharedElementsIn A list modified to contain the shared elements in the incoming
+ * fragment
+ * @param enterTransition The transition used for entering Views, modified by applying the
+ * epicenter
+ * @param exitTransition The transition used for exiting Views, modified by applying the
+ * epicenter
+ * @return The shared element transition or null if no shared elements exist
+ */
+ private static TransitionSet configureSharedElementsUnoptimized(final ViewGroup sceneRoot,
+ final View nonExistentView, ArrayMap<String, String> nameOverrides,
+ final FragmentContainerTransition fragments,
+ final ArrayList<View> sharedElementsOut,
+ final ArrayList<View> sharedElementsIn,
+ final Transition enterTransition, final Transition exitTransition) {
+ final Fragment inFragment = fragments.lastIn;
+ final Fragment outFragment = fragments.firstOut;
+
+ if (inFragment == null || outFragment == null) {
+ return null; // no transition
+ }
+
+ final boolean inIsPop = fragments.lastInIsPop;
+ TransitionSet sharedElementTransition = nameOverrides.isEmpty() ? null
+ : getSharedElementTransition(inFragment, outFragment, inIsPop);
+
+ ArrayMap<String, View> outSharedElements = captureOutSharedElements(nameOverrides,
+ sharedElementTransition, fragments);
+
+ if (nameOverrides.isEmpty()) {
+ sharedElementTransition = null;
+ } else {
+ sharedElementsOut.addAll(outSharedElements.values());
+ }
+
+ if (enterTransition == null && exitTransition == null && sharedElementTransition == null) {
+ // don't call onSharedElementStart/End since there is no transition
+ return null;
+ }
+
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop, outSharedElements, true);
+
+ final Rect inEpicenter;
+ if (sharedElementTransition != null) {
+ inEpicenter = new Rect();
+ setSharedElementTargets(sharedElementTransition, nonExistentView, sharedElementsOut);
+ final boolean outIsPop = fragments.firstOutIsPop;
+ final BackStackRecord outTransaction = fragments.firstOutTransaction;
+ setOutEpicenter(sharedElementTransition, exitTransition, outSharedElements, outIsPop,
+ outTransaction);
+ if (enterTransition != null) {
+ enterTransition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ if (inEpicenter.isEmpty()) {
+ return null;
+ }
+ return inEpicenter;
+ }
+ });
+ }
+ } else {
+ inEpicenter = null;
+ }
+
+ TransitionSet finalSharedElementTransition = sharedElementTransition;
+
+ sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ ArrayMap<String, View> inSharedElements = captureInSharedElements(
+ nameOverrides, finalSharedElementTransition, fragments);
+
+ if (inSharedElements != null) {
+ sharedElementsIn.addAll(inSharedElements.values());
+ sharedElementsIn.add(nonExistentView);
+ }
+
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+ inSharedElements, false);
+ if (finalSharedElementTransition != null) {
+ finalSharedElementTransition.getTargets().clear();
+ finalSharedElementTransition.getTargets().addAll(sharedElementsIn);
+ replaceTargets(finalSharedElementTransition, sharedElementsOut,
+ sharedElementsIn);
+
+ final View inEpicenterView = getInEpicenterView(inSharedElements,
+ fragments, enterTransition, inIsPop);
+ if (inEpicenterView != null) {
+ inEpicenterView.getBoundsOnScreen(inEpicenter);
+ }
+ }
+ return true;
+ }
+ });
+ return sharedElementTransition;
+ }
+
+ /**
+ * Finds the shared elements in the outgoing fragment. It also calls
+ * {@link SharedElementCallback#onMapSharedElements(List, Map)} to allow more control
+ * of the shared element mapping. {@code nameOverrides} is updated to match the
+ * actual transition name of the mapped shared elements.
+ *
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ * @param sharedElementTransition The shared element transition
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @return The mapping of shared element names to the Views in the hierarchy or null
+ * if there is no shared element transition.
+ */
+ private static ArrayMap<String, View> captureOutSharedElements(
+ ArrayMap<String, String> nameOverrides, TransitionSet sharedElementTransition,
+ FragmentContainerTransition fragments) {
+ if (nameOverrides.isEmpty() || sharedElementTransition == null) {
+ nameOverrides.clear();
+ return null;
+ }
+ final Fragment outFragment = fragments.firstOut;
+ final ArrayMap<String, View> outSharedElements = new ArrayMap<>();
+ outFragment.getView().findNamedViews(outSharedElements);
+
+ final SharedElementCallback sharedElementCallback;
+ final ArrayList<String> names;
+ final BackStackRecord outTransaction = fragments.firstOutTransaction;
+ if (fragments.firstOutIsPop) {
+ sharedElementCallback = outFragment.getEnterTransitionCallback();
+ names = outTransaction.mSharedElementTargetNames;
+ } else {
+ sharedElementCallback = outFragment.getExitTransitionCallback();
+ names = outTransaction.mSharedElementSourceNames;
+ }
+
+ outSharedElements.retainAll(names);
+ if (sharedElementCallback != null) {
+ sharedElementCallback.onMapSharedElements(names, outSharedElements);
+ for (int i = names.size() - 1; i >= 0; i--) {
+ String name = names.get(i);
+ View view = outSharedElements.get(name);
+ if (view == null) {
+ nameOverrides.remove(name);
+ } else if (!name.equals(view.getTransitionName())) {
+ String targetValue = nameOverrides.remove(name);
+ nameOverrides.put(view.getTransitionName(), targetValue);
+ }
+ }
+ } else {
+ nameOverrides.retainAll(outSharedElements.keySet());
+ }
+ return outSharedElements;
+ }
+
+ /**
+ * Finds the shared elements in the incoming fragment. It also calls
+ * {@link SharedElementCallback#onMapSharedElements(List, Map)} to allow more control
+ * of the shared element mapping. {@code nameOverrides} is updated to match the
+ * actual transition name of the mapped shared elements.
+ *
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ * @param sharedElementTransition The shared element transition
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @return The mapping of shared element names to the Views in the hierarchy or null
+ * if there is no shared element transition.
+ */
+ private static ArrayMap<String, View> captureInSharedElements(
+ ArrayMap<String, String> nameOverrides, TransitionSet sharedElementTransition,
+ FragmentContainerTransition fragments) {
+ Fragment inFragment = fragments.lastIn;
+ final View fragmentView = inFragment.getView();
+ if (nameOverrides.isEmpty() || sharedElementTransition == null || fragmentView == null) {
+ nameOverrides.clear();
+ return null;
+ }
+ final ArrayMap<String, View> inSharedElements = new ArrayMap<>();
+ fragmentView.findNamedViews(inSharedElements);
+
+ final SharedElementCallback sharedElementCallback;
+ final ArrayList<String> names;
+ final BackStackRecord inTransaction = fragments.lastInTransaction;
+ if (fragments.lastInIsPop) {
+ sharedElementCallback = inFragment.getExitTransitionCallback();
+ names = inTransaction.mSharedElementSourceNames;
+ } else {
+ sharedElementCallback = inFragment.getEnterTransitionCallback();
+ names = inTransaction.mSharedElementTargetNames;
+ }
+
+ inSharedElements.retainAll(names);
+ if (sharedElementCallback != null) {
+ sharedElementCallback.onMapSharedElements(names, inSharedElements);
+ for (int i = names.size() - 1; i >= 0; i--) {
+ String name = names.get(i);
+ View view = inSharedElements.get(name);
+ if (view == null) {
+ String key = findKeyForValue(nameOverrides, name);
+ if (key != null) {
+ nameOverrides.remove(key);
+ }
+ } else if (!name.equals(view.getTransitionName())) {
+ String key = findKeyForValue(nameOverrides, name);
+ if (key != null) {
+ nameOverrides.put(key, view.getTransitionName());
+ }
+ }
+ }
+ } else {
+ retainValues(nameOverrides, inSharedElements);
+ }
+ return inSharedElements;
+ }
+
+ /**
+ * Utility to find the String key in {@code map} that maps to {@code value}.
+ */
+ private static String findKeyForValue(ArrayMap<String, String> map, String value) {
+ final int numElements = map.size();
+ for (int i = 0; i < numElements; i++) {
+ if (value.equals(map.valueAt(i))) {
+ return map.keyAt(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the View in the incoming Fragment that should be used as the epicenter.
+ *
+ * @param inSharedElements The mapping of shared element names to Views in the
+ * incoming fragment.
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @param enterTransition The transition used for the incoming Fragment's views
+ * @param inIsPop Is the incoming fragment being added as a pop transaction?
+ */
+ private static View getInEpicenterView(ArrayMap<String, View> inSharedElements,
+ FragmentContainerTransition fragments,
+ Transition enterTransition, boolean inIsPop) {
+ BackStackRecord inTransaction = fragments.lastInTransaction;
+ if (enterTransition != null && inTransaction.mSharedElementSourceNames != null &&
+ !inTransaction.mSharedElementSourceNames.isEmpty()) {
+ final String targetName = inIsPop
+ ? inTransaction.mSharedElementSourceNames.get(0)
+ : inTransaction.mSharedElementTargetNames.get(0);
+ return inSharedElements.get(targetName);
+ }
+ return null;
+ }
+
+ /**
+ * Sets the epicenter for the exit transition.
+ *
+ * @param sharedElementTransition The shared element transition
+ * @param exitTransition The transition for the outgoing fragment's views
+ * @param outSharedElements Shared elements in the outgoing fragment
+ * @param outIsPop Is the outgoing fragment being removed as a pop transaction?
+ * @param outTransaction The transaction that caused the fragment to be removed.
+ */
+ private static void setOutEpicenter(TransitionSet sharedElementTransition,
+ Transition exitTransition, ArrayMap<String, View> outSharedElements, boolean outIsPop,
+ BackStackRecord outTransaction) {
+ if (outTransaction.mSharedElementSourceNames != null &&
+ !outTransaction.mSharedElementSourceNames.isEmpty()) {
+ final String sourceName = outIsPop
+ ? outTransaction.mSharedElementTargetNames.get(0)
+ : outTransaction.mSharedElementSourceNames.get(0);
+ final View outEpicenterView = outSharedElements.get(sourceName);
+ setEpicenter(sharedElementTransition, outEpicenterView);
+
+ if (exitTransition != null) {
+ setEpicenter(exitTransition, outEpicenterView);
+ }
+ }
+ }
+
+ /**
+ * Sets a transition epicenter to the rectangle of a given View.
+ */
+ private static void setEpicenter(Transition transition, View view) {
+ if (view != null) {
+ final Rect epicenter = new Rect();
+ view.getBoundsOnScreen(epicenter);
+
+ transition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return epicenter;
+ }
+ });
+ }
+ }
+
+ /**
+ * A utility to retain only the mappings in {@code nameOverrides} that have a value
+ * that has a key in {@code namedViews}. This is a useful equivalent to
+ * {@link ArrayMap#retainAll(Collection)} for values.
+ */
+ private static void retainValues(ArrayMap<String, String> nameOverrides,
+ ArrayMap<String, View> namedViews) {
+ for (int i = nameOverrides.size() - 1; i >= 0; i--) {
+ final String targetName = nameOverrides.valueAt(i);
+ if (!namedViews.containsKey(targetName)) {
+ nameOverrides.removeAt(i);
+ }
+ }
+ }
+
+ /**
+ * Calls the {@link SharedElementCallback#onSharedElementStart(List, List, List)} or
+ * {@link SharedElementCallback#onSharedElementEnd(List, List, List)} on the appropriate
+ * incoming or outgoing fragment.
+ *
+ * @param inFragment The incoming fragment
+ * @param outFragment The outgoing fragment
+ * @param isPop Is the incoming fragment part of a pop transaction?
+ * @param sharedElements The shared element Views
+ * @param isStart Call the start or end call on the SharedElementCallback
+ */
+ private static void callSharedElementStartEnd(Fragment inFragment, Fragment outFragment,
+ boolean isPop, ArrayMap<String, View> sharedElements, boolean isStart) {
+ SharedElementCallback sharedElementCallback = isPop
+ ? outFragment.getEnterTransitionCallback()
+ : inFragment.getEnterTransitionCallback();
+ if (sharedElementCallback != null) {
+ ArrayList<View> views = new ArrayList<>();
+ ArrayList<String> names = new ArrayList<>();
+ final int count = sharedElements == null ? 0 : sharedElements.size();
+ for (int i = 0; i < count; i++) {
+ names.add(sharedElements.keyAt(i));
+ views.add(sharedElements.valueAt(i));
+ }
+ if (isStart) {
+ sharedElementCallback.onSharedElementStart(names, views, null);
+ } else {
+ sharedElementCallback.onSharedElementEnd(names, views, null);
+ }
+ }
+ }
+
+ /**
+ * Finds all children of the shared elements and sets the wrapping TransitionSet
+ * targets to point to those. It also limits transitions that have no targets to the
+ * specific shared elements. This allows developers to target child views of the
+ * shared elements specifically, but this doesn't happen by default.
+ */
+ private static void setSharedElementTargets(TransitionSet transition,
+ View nonExistentView, ArrayList<View> sharedViews) {
+ final List<View> views = transition.getTargets();
+ views.clear();
+ final int count = sharedViews.size();
+ for (int i = 0; i < count; i++) {
+ final View view = sharedViews.get(i);
+ bfsAddViewChildren(views, view);
+ }
+ views.add(nonExistentView);
+ sharedViews.add(nonExistentView);
+ addTargets(transition, sharedViews);
+ }
+
+ /**
+ * Uses a breadth-first scheme to add startView and all of its children to views.
+ * It won't add a child if it is already in views.
+ */
+ private static void bfsAddViewChildren(final List<View> views, final View startView) {
+ final int startIndex = views.size();
+ if (containedBeforeIndex(views, startView, startIndex)) {
+ return; // This child is already in the list, so all its children are also.
+ }
+ views.add(startView);
+ for (int index = startIndex; index < views.size(); index++) {
+ final View view = views.get(index);
+ if (view instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) view;
+ final int childCount = viewGroup.getChildCount();
+ for (int childIndex = 0; childIndex < childCount; childIndex++) {
+ final View child = viewGroup.getChildAt(childIndex);
+ if (!containedBeforeIndex(views, child, startIndex)) {
+ views.add(child);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Does a linear search through views for view, limited to maxIndex.
+ */
+ private static boolean containedBeforeIndex(final List<View> views, final View view,
+ final int maxIndex) {
+ for (int i = 0; i < maxIndex; i++) {
+ if (views.get(i) == view) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * After the transition has started, remove all targets that we added to the transitions
+ * so that the transitions are left in a clean state.
+ */
+ private static void scheduleRemoveTargets(final Transition overalTransition,
+ final Transition enterTransition, final ArrayList<View> enteringViews,
+ final Transition exitTransition, final ArrayList<View> exitingViews,
+ final TransitionSet sharedElementTransition, final ArrayList<View> sharedElementsIn) {
+ overalTransition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ if (enterTransition != null) {
+ replaceTargets(enterTransition, enteringViews, null);
+ }
+ if (exitTransition != null) {
+ replaceTargets(exitTransition, exitingViews, null);
+ }
+ if (sharedElementTransition != null) {
+ replaceTargets(sharedElementTransition, sharedElementsIn, null);
+ }
+ }
+ });
+ }
+
+ /**
+ * This method removes the views from transitions that target ONLY those views and
+ * replaces them with the new targets list.
+ * The views list should match those added in addTargets and should contain
+ * one view that is not in the view hierarchy (state.nonExistentView).
+ */
+ public static void replaceTargets(Transition transition, ArrayList<View> oldTargets,
+ ArrayList<View> newTargets) {
+ if (transition instanceof TransitionSet) {
+ TransitionSet set = (TransitionSet) transition;
+ int numTransitions = set.getTransitionCount();
+ for (int i = 0; i < numTransitions; i++) {
+ Transition child = set.getTransitionAt(i);
+ replaceTargets(child, oldTargets, newTargets);
+ }
+ } else if (!hasSimpleTarget(transition)) {
+ List<View> targets = transition.getTargets();
+ if (targets != null && targets.size() == oldTargets.size() &&
+ targets.containsAll(oldTargets)) {
+ // We have an exact match. We must have added these earlier in addTargets
+ final int targetCount = newTargets == null ? 0 : newTargets.size();
+ for (int i = 0; i < targetCount; i++) {
+ transition.addTarget(newTargets.get(i));
+ }
+ for (int i = oldTargets.size() - 1; i >= 0; i--) {
+ transition.removeTarget(oldTargets.get(i));
+ }
+ }
+ }
+ }
+
+ /**
+ * This method adds views as targets to the transition, but only if the transition
+ * doesn't already have a target. It is best for views to contain one View object
+ * that does not exist in the view hierarchy (state.nonExistentView) so that
+ * when they are removed later, a list match will suffice to remove the targets.
+ * Otherwise, if you happened to have targeted the exact views for the transition,
+ * the replaceTargets call will remove them unexpectedly.
+ */
+ public static void addTargets(Transition transition, ArrayList<View> views) {
+ if (transition == null) {
+ return;
+ }
+ if (transition instanceof TransitionSet) {
+ TransitionSet set = (TransitionSet) transition;
+ int numTransitions = set.getTransitionCount();
+ for (int i = 0; i < numTransitions; i++) {
+ Transition child = set.getTransitionAt(i);
+ addTargets(child, views);
+ }
+ } else if (!hasSimpleTarget(transition)) {
+ List<View> targets = transition.getTargets();
+ if (isNullOrEmpty(targets)) {
+ // We can just add the target views
+ int numViews = views.size();
+ for (int i = 0; i < numViews; i++) {
+ transition.addTarget(views.get(i));
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns true if there are any targets based on ID, transition or type.
+ */
+ private static boolean hasSimpleTarget(Transition transition) {
+ return !isNullOrEmpty(transition.getTargetIds()) ||
+ !isNullOrEmpty(transition.getTargetNames()) ||
+ !isNullOrEmpty(transition.getTargetTypes());
+ }
+
+ /**
+ * Simple utility to detect if a list is null or has no elements.
+ */
+ private static boolean isNullOrEmpty(List list) {
+ return list == null || list.isEmpty();
+ }
+
+ private static ArrayList<View> configureEnteringExitingViews(Transition transition,
+ Fragment fragment, ArrayList<View> sharedElements, View nonExistentView) {
+ ArrayList<View> viewList = null;
+ if (transition != null) {
+ viewList = new ArrayList<>();
+ View root = fragment.getView();
+ root.captureTransitioningViews(viewList);
+ if (sharedElements != null) {
+ viewList.removeAll(sharedElements);
+ }
+ if (!viewList.isEmpty()) {
+ viewList.add(nonExistentView);
+ addTargets(transition, viewList);
+ }
+ }
+ return viewList;
+ }
+
+ /**
+ * Sets the visibility of all Views in {@code views} to {@code visibility}.
+ */
+ private static void setViewVisibility(ArrayList<View> views, @View.Visibility int visibility) {
+ if (views == null) {
+ return;
+ }
+ for (int i = views.size() - 1; i >= 0; i--) {
+ final View view = views.get(i);
+ view.setVisibility(visibility);
+ }
+ }
+
+ /**
+ * Merges exit, shared element, and enter transitions so that they act together or
+ * sequentially as defined in the fragments.
+ */
+ private static Transition mergeTransitions(Transition enterTransition,
+ Transition exitTransition, Transition sharedElementTransition, Fragment inFragment,
+ boolean isPop) {
+ boolean overlap = true;
+ if (enterTransition != null && exitTransition != null && inFragment != null) {
+ overlap = isPop ? inFragment.getAllowReturnTransitionOverlap() :
+ inFragment.getAllowEnterTransitionOverlap();
+ }
+
+ // Wrap the transitions. Explicit targets like in enter and exit will cause the
+ // views to be targeted regardless of excluded views. If that happens, then the
+ // excluded fragments views (hidden fragments) will still be in the transition.
+
+ Transition transition;
+ if (overlap) {
+ // Regular transition -- do it all together
+ TransitionSet transitionSet = new TransitionSet();
+ if (enterTransition != null) {
+ transitionSet.addTransition(enterTransition);
+ }
+ if (exitTransition != null) {
+ transitionSet.addTransition(exitTransition);
+ }
+ if (sharedElementTransition != null) {
+ transitionSet.addTransition(sharedElementTransition);
+ }
+ transition = transitionSet;
+ } else {
+ // First do exit, then enter, but allow shared element transition to happen
+ // during both.
+ Transition staggered = null;
+ if (exitTransition != null && enterTransition != null) {
+ staggered = new TransitionSet()
+ .addTransition(exitTransition)
+ .addTransition(enterTransition)
+ .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+ } else if (exitTransition != null) {
+ staggered = exitTransition;
+ } else if (enterTransition != null) {
+ staggered = enterTransition;
+ }
+ if (sharedElementTransition != null) {
+ TransitionSet together = new TransitionSet();
+ if (staggered != null) {
+ together.addTransition(staggered);
+ }
+ together.addTransition(sharedElementTransition);
+ transition = together;
+ } else {
+ transition = staggered;
+ }
+ }
+ return transition;
+ }
+
+ /**
+ * Finds the first removed fragment and last added fragments when going forward.
+ * If none of the fragments have transitions, then both lists will be empty.
+ *
+ * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
+ * and last fragments to be added. This will be modified by
+ * this method.
+ */
+ public static void calculateFragments(BackStackRecord transaction,
+ SparseArray<FragmentContainerTransition> transitioningFragments,
+ boolean isOptimized) {
+ final int numOps = transaction.mOps.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ final BackStackRecord.Op op = transaction.mOps.get(opNum);
+ addToFirstInLastOut(transaction, op, transitioningFragments, false, isOptimized);
+ }
+ }
+
+ /**
+ * Finds the first removed fragment and last added fragments when popping the back stack.
+ * If none of the fragments have transitions, then both lists will be empty.
+ *
+ * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
+ * and last fragments to be added. This will be modified by
+ * this method.
+ */
+ public static void calculatePopFragments(BackStackRecord transaction,
+ SparseArray<FragmentContainerTransition> transitioningFragments, boolean isOptimized) {
+ if (!transaction.mManager.mContainer.onHasView()) {
+ return; // nothing to see, so no transitions
+ }
+ final int numOps = transaction.mOps.size();
+ for (int opNum = numOps - 1; opNum >= 0; opNum--) {
+ final BackStackRecord.Op op = transaction.mOps.get(opNum);
+ addToFirstInLastOut(transaction, op, transitioningFragments, true, isOptimized);
+ }
+ }
+
+ /**
+ * Examines the {@code command} and may set the first out or last in fragment for the fragment's
+ * container.
+ *
+ * @param transaction The executing transaction
+ * @param op The operation being run.
+ * @param transitioningFragments A structure holding the first in and last out fragments
+ * for each fragment container.
+ * @param isPop Is the operation a pop?
+ * @param isOptimizedTransaction True if the operations have been partially executed and the
+ * added fragments have Views in the hierarchy or false if the
+ * operations haven't been executed yet.
+ */
+ private static void addToFirstInLastOut(BackStackRecord transaction, BackStackRecord.Op op,
+ SparseArray<FragmentContainerTransition> transitioningFragments, boolean isPop,
+ boolean isOptimizedTransaction) {
+ final Fragment fragment = op.fragment;
+ final int containerId = fragment.mContainerId;
+ if (containerId == 0) {
+ return; // no container, no transition
+ }
+ final int command = isPop ? INVERSE_OPS[op.cmd] : op.cmd;
+ boolean setLastIn = false;
+ boolean wasRemoved = false;
+ boolean setFirstOut = false;
+ boolean wasAdded = false;
+ switch (command) {
+ case BackStackRecord.OP_SHOW:
+ if (isOptimizedTransaction) {
+ setLastIn = fragment.mHiddenChanged && !fragment.mHidden &&
+ fragment.mAdded;
+ } else {
+ setLastIn = fragment.mHidden;
+ }
+ wasAdded = true;
+ break;
+ case BackStackRecord.OP_ADD:
+ case BackStackRecord.OP_ATTACH:
+ if (isOptimizedTransaction) {
+ setLastIn = fragment.mIsNewlyAdded;
+ } else {
+ setLastIn = !fragment.mAdded && !fragment.mHidden;
+ }
+ wasAdded = true;
+ break;
+ case BackStackRecord.OP_HIDE:
+ if (isOptimizedTransaction) {
+ setFirstOut = fragment.mHiddenChanged && fragment.mAdded &&
+ fragment.mHidden;
+ } else {
+ setFirstOut = fragment.mAdded && !fragment.mHidden;
+ }
+ wasRemoved = true;
+ break;
+ case BackStackRecord.OP_REMOVE:
+ case BackStackRecord.OP_DETACH:
+ if (isOptimizedTransaction) {
+ setFirstOut = !fragment.mAdded && fragment.mView != null &&
+ fragment.mView.getVisibility() == View.VISIBLE;
+ } else {
+ setFirstOut = fragment.mAdded && !fragment.mHidden;
+ }
+ wasRemoved = true;
+ break;
+ }
+ FragmentContainerTransition containerTransition = transitioningFragments.get(containerId);
+ if (setLastIn) {
+ containerTransition =
+ ensureContainer(containerTransition, transitioningFragments, containerId);
+ containerTransition.lastIn = fragment;
+ containerTransition.lastInIsPop = isPop;
+ containerTransition.lastInTransaction = transaction;
+ }
+ if (!isOptimizedTransaction && wasAdded) {
+ if (containerTransition != null && containerTransition.firstOut == fragment) {
+ containerTransition.firstOut = null;
+ }
+
+ /**
+ * Ensure that fragments that are entering are at least at the CREATED state
+ * so that they may load Transitions using TransitionInflater.
+ */
+ FragmentManagerImpl manager = transaction.mManager;
+ if (fragment.mState < Fragment.CREATED && manager.mCurState >= Fragment.CREATED &&
+ manager.mHost.getContext().getApplicationInfo().targetSdkVersion >=
+ Build.VERSION_CODES.N && !transaction.mAllowOptimization) {
+ manager.makeActive(fragment);
+ manager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
+ }
+ }
+ if (setFirstOut && (containerTransition == null || containerTransition.firstOut == null)) {
+ containerTransition =
+ ensureContainer(containerTransition, transitioningFragments, containerId);
+ containerTransition.firstOut = fragment;
+ containerTransition.firstOutIsPop = isPop;
+ containerTransition.firstOutTransaction = transaction;
+ }
+
+ if (!isOptimizedTransaction && wasRemoved &&
+ (containerTransition != null && containerTransition.lastIn == fragment)) {
+ containerTransition.lastIn = null;
+ }
+ }
+
+ /**
+ * Ensures that a FragmentContainerTransition has been added to the SparseArray. If so,
+ * it returns the existing one. If not, one is created and added to the SparseArray and
+ * returned.
+ */
+ private static FragmentContainerTransition ensureContainer(
+ FragmentContainerTransition containerTransition,
+ SparseArray<FragmentContainerTransition> transitioningFragments, int containerId) {
+ if (containerTransition == null) {
+ containerTransition = new FragmentContainerTransition();
+ transitioningFragments.put(containerId, containerTransition);
+ }
+ return containerTransition;
+ }
+
+ /**
+ * Tracks the last fragment added and first fragment removed for fragment transitions.
+ * This also tracks which fragments are changed by push or pop transactions.
+ */
+ public static class FragmentContainerTransition {
+ /**
+ * The last fragment added/attached/shown in its container
+ */
+ public Fragment lastIn;
+
+ /**
+ * true when lastIn was added during a pop transaction or false if added with a push
+ */
+ public boolean lastInIsPop;
+
+ /**
+ * The transaction that included the last in fragment
+ */
+ public BackStackRecord lastInTransaction;
+
+ /**
+ * The first fragment with a View that was removed/detached/hidden in its container.
+ */
+ public Fragment firstOut;
+
+ /**
+ * true when firstOut was removed during a pop transaction or false otherwise
+ */
+ public boolean firstOutIsPop;
+
+ /**
+ * The transaction that included the first out fragment
+ */
+ public BackStackRecord firstOutTransaction;
+ }
+}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c9d3682..3670c58 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -274,12 +274,24 @@
/**
* Updates global configuration and applies changes to the entire system.
- * @param values Update values for global configuration.
+ * @param values Update values for global configuration. If null is passed it will request the
+ * Window Manager to compute new config for the default display.
* @throws RemoteException
* @return Returns true if the configuration was updated.
*/
public boolean updateConfiguration(Configuration values) throws RemoteException;
+ /**
+ * Updates override configuration applied to specific display.
+ * @param values Update values for display configuration. If null is passed it will request the
+ * Window Manager to compute new config for the specified display.
+ * @param displayId Id of the display to apply the config to.
+ * @throws RemoteException
+ * @return Returns true if the configuration was updated.
+ */
+ public boolean updateDisplayOverrideConfiguration(Configuration values, int displayId)
+ throws RemoteException;
+
public void setRequestedOrientation(IBinder token,
int requestedOrientation) throws RemoteException;
public int getRequestedOrientation(IBinder token) throws RemoteException;
@@ -568,8 +580,6 @@
public IActivityContainer createStackOnDisplay(int displayId) throws RemoteException;
- public void deleteActivityContainer(IActivityContainer container) throws RemoteException;
-
public int getActivityDisplayId(IBinder activityToken) throws RemoteException;
public void startSystemLockTaskMode(int taskId) throws RemoteException;
@@ -647,6 +657,16 @@
public void enterPictureInPictureMode(IBinder token) throws RemoteException;
+ /**
+ * @return the default bounds of the PIP on the default display.
+ */
+ public Rect getDefaultPictureInPictureBounds(int displayId) throws RemoteException;
+
+ /**
+ * @return the movement bounds of the PIP on the default display.
+ */
+ public Rect getPictureInPictureMovementBounds(int displayId) throws RemoteException;
+
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName)
throws RemoteException;
@@ -1091,4 +1111,7 @@
// Start of O transactions
int REQUEST_ACTIVITY_RELAUNCH = IBinder.FIRST_CALL_TRANSACTION+400;
+ int GET_DEFAULT_PICTURE_IN_PICTURE_BOUNDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 401;
+ int GET_PICTURE_IN_PICTURE_MOVEMENT_BOUNDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 402;
+ int UPDATE_DISPLAY_OVERRIDE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 403;
}
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
new file mode 100644
index 0000000..e2f6fb5
--- /dev/null
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -0,0 +1,163 @@
+/*
+ * 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.app;
+
+import android.app.IInstrumentationWatcher;
+import android.app.IUiAutomationConnection;
+import android.app.ProfilerInfo;
+import android.app.ResultInfo;
+import android.content.ComponentName;
+import android.content.IIntentReceiver;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.content.ReferrerIntent;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * System private API for communicating with the application. This is given to
+ * the activity manager by an application when it starts up, for the activity
+ * manager to tell the application about things it needs to do.
+ *
+ * {@hide}
+ */
+oneway interface IApplicationThread {
+ /**
+ * Don't change the existing transaction Ids as they could be used in the native code.
+ * When adding a new method, assign the next available transaction id.
+ */
+ void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving,
+ int configChanges, boolean dontReport) = 1;
+ void scheduleStopActivity(IBinder token, boolean showWindow,
+ int configChanges) = 3;
+ void scheduleWindowVisibility(IBinder token, boolean showWindow) = 4;
+ void scheduleResumeActivity(IBinder token, int procState, boolean isForward,
+ in Bundle resumeArgs) = 5;
+ void scheduleSendResult(IBinder token, in List<ResultInfo> results) = 6;
+ void scheduleLaunchActivity(in Intent intent, IBinder token, int ident,
+ in ActivityInfo info, in Configuration curConfig, in Configuration overrideConfig,
+ in CompatibilityInfo compatInfo, in String referrer, IVoiceInteractor voiceInteractor,
+ int procState, in Bundle state, in PersistableBundle persistentState,
+ in List<ResultInfo> pendingResults, in List<ReferrerIntent> pendingNewIntents,
+ boolean notResumed, boolean isForward, in ProfilerInfo profilerInfo) = 7;
+ void scheduleNewIntent(
+ in List<ReferrerIntent> intent, IBinder token, boolean andPause) = 8;
+ void scheduleDestroyActivity(IBinder token, boolean finished,
+ int configChanges) = 9;
+ void scheduleReceiver(in Intent intent, in ActivityInfo info,
+ in CompatibilityInfo compatInfo,
+ int resultCode, in String data, in Bundle extras, boolean sync,
+ int sendingUser, int processState) = 10;
+ void scheduleCreateService(IBinder token, in ServiceInfo info,
+ in CompatibilityInfo compatInfo, int processState) = 11;
+ void scheduleStopService(IBinder token) = 12;
+ void bindApplication(in String packageName, in ApplicationInfo info,
+ in List<ProviderInfo> providers, in ComponentName testName,
+ in ProfilerInfo profilerInfo, in Bundle testArguments,
+ IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
+ int debugMode, boolean enableBinderTracking, boolean trackAllocation,
+ boolean restrictedBackupMode, boolean persistent, in Configuration config,
+ in CompatibilityInfo compatInfo, in Map services,
+ in Bundle coreSettings, in String buildSerial) = 13;
+ void scheduleExit() = 14;
+ void scheduleConfigurationChanged(in Configuration config) = 16;
+ void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
+ int flags, in Intent args) = 17;
+ void updateTimeZone() = 18;
+ void processInBackground() = 19;
+ void scheduleBindService(IBinder token,
+ in Intent intent, boolean rebind, int processState) = 20;
+ void scheduleUnbindService(IBinder token,
+ in Intent intent) = 21;
+ void dumpService(in ParcelFileDescriptor fd, IBinder servicetoken,
+ in String[] args) = 22;
+ void scheduleRegisteredReceiver(IIntentReceiver receiver, in Intent intent,
+ int resultCode, in String data, in Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser, int processState) = 23;
+ void scheduleLowMemory() = 24;
+ void scheduleActivityConfigurationChanged(IBinder token, in Configuration overrideConfig,
+ boolean reportToActivity) = 25;
+ void scheduleRelaunchActivity(IBinder token, in List<ResultInfo> pendingResults,
+ in List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
+ in Configuration config, in Configuration overrideConfig, boolean preserveWindow) = 26;
+ void scheduleSleeping(IBinder token, boolean sleeping) = 27;
+ void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType) = 28;
+ void setSchedulingGroup(int group) = 29;
+ void scheduleCreateBackupAgent(in ApplicationInfo app, in CompatibilityInfo compatInfo,
+ int backupMode) = 30;
+ void scheduleDestroyBackupAgent(in ApplicationInfo app,
+ in CompatibilityInfo compatInfo) = 31;
+ void scheduleOnNewActivityOptions(IBinder token, in Bundle options) = 32;
+ void scheduleSuicide() = 33;
+ void dispatchPackageBroadcast(int cmd, in String[] packages) = 34;
+ void scheduleCrash(in String msg) = 35;
+ void dumpHeap(boolean managed, in String path, in ParcelFileDescriptor fd) = 36;
+ void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix,
+ in String[] args) = 37;
+ void clearDnsCache() = 38;
+ void setHttpProxy(in String proxy, in String port, in String exclList,
+ in Uri pacFileUrl) = 39;
+ void setCoreSettings(in Bundle coreSettings) = 40;
+ void updatePackageCompatibilityInfo(in String pkg, in CompatibilityInfo info) = 41;
+ void scheduleTrimMemory(int level) = 42;
+ void dumpMemInfo(in ParcelFileDescriptor fd, in Debug.MemoryInfo mem, boolean checkin,
+ boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable,
+ in String[] args) = 43;
+ void dumpGfxInfo(in ParcelFileDescriptor fd, in String[] args) = 44;
+ void dumpProvider(in ParcelFileDescriptor fd, IBinder servicetoken,
+ in String[] args) = 45;
+ void dumpDbInfo(in ParcelFileDescriptor fd, in String[] args) = 46;
+ void unstableProviderDied(IBinder provider) = 47;
+ void requestAssistContextExtras(IBinder activityToken, IBinder requestToken,
+ int requestType, int sessionId) = 48;
+ void scheduleTranslucentConversionComplete(IBinder token, boolean timeout) = 49;
+ void setProcessState(int state) = 50;
+ void scheduleInstallProvider(in ProviderInfo provider) = 51;
+ void updateTimePrefs(boolean is24Hour) = 52;
+ void scheduleCancelVisibleBehind(IBinder token) = 53;
+ void scheduleBackgroundVisibleBehindChanged(IBinder token, boolean enabled) = 54;
+ void scheduleEnterAnimationComplete(IBinder token) = 55;
+ void notifyCleartextNetwork(in byte[] firstPacket) = 56;
+ void startBinderTracking() = 57;
+ void stopBinderTrackingAndDump(in ParcelFileDescriptor fd) = 58;
+ void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode) = 59;
+ void schedulePictureInPictureModeChanged(IBinder token,
+ boolean isInPictureInPictureMode) = 60;
+ void scheduleLocalVoiceInteractionStarted(IBinder token,
+ IVoiceInteractor voiceInteractor) = 61;
+ void handleTrustStorageUpdate() = 62;
+ void attachAgent(String path) = 63;
+ /**
+ * Don't change the existing transaction Ids as they could be used in the native code.
+ * When adding a new method, assign the next available transaction id.
+ */
+}
\ No newline at end of file
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
deleted file mode 100644
index 4189dd9..0000000
--- a/core/java/android/app/IApplicationThread.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2006 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.app;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.IIntentReceiver;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.ParcelFileDescriptor;
-import android.os.PersistableBundle;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.IInterface;
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.content.ReferrerIntent;
-
-import java.io.FileDescriptor;
-import java.util.List;
-import java.util.Map;
-
-/**
- * System private API for communicating with the application. This is given to
- * the activity manager by an application when it starts up, for the activity
- * manager to tell the application about things it needs to do.
- *
- * {@hide}
- */
-public interface IApplicationThread extends IInterface {
- void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving,
- int configChanges, boolean dontReport) throws RemoteException;
- void scheduleStopActivity(IBinder token, boolean showWindow,
- int configChanges) throws RemoteException;
- void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException;
- void scheduleSleeping(IBinder token, boolean sleeping) throws RemoteException;
- void scheduleResumeActivity(IBinder token, int procState, boolean isForward, Bundle resumeArgs)
- throws RemoteException;
- void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
- void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
- ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
- CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
- int procState, Bundle state, PersistableBundle persistentState,
- List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException;
- void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
- List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
- Configuration config, Configuration overrideConfig, boolean preserveWindow)
- throws RemoteException;
- void scheduleNewIntent(
- List<ReferrerIntent> intent, IBinder token, boolean andPause) throws RemoteException;
- void scheduleDestroyActivity(IBinder token, boolean finished,
- int configChanges) throws RemoteException;
- void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
- int resultCode, String data, Bundle extras, boolean sync,
- int sendingUser, int processState) throws RemoteException;
- static final int BACKUP_MODE_INCREMENTAL = 0;
- static final int BACKUP_MODE_FULL = 1;
- static final int BACKUP_MODE_RESTORE = 2;
- static final int BACKUP_MODE_RESTORE_FULL = 3;
- void scheduleCreateBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo,
- int backupMode) throws RemoteException;
- void scheduleDestroyBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo)
- throws RemoteException;
- void scheduleCreateService(IBinder token, ServiceInfo info,
- CompatibilityInfo compatInfo, int processState) throws RemoteException;
- void scheduleBindService(IBinder token,
- Intent intent, boolean rebind, int processState) throws RemoteException;
- void scheduleUnbindService(IBinder token,
- Intent intent) throws RemoteException;
- void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
- int flags, Intent args) throws RemoteException;
- void scheduleStopService(IBinder token) throws RemoteException;
- static final int DEBUG_OFF = 0;
- static final int DEBUG_ON = 1;
- static final int DEBUG_WAIT = 2;
- void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
- ComponentName testName, ProfilerInfo profilerInfo, Bundle testArguments,
- IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
- int debugMode, boolean enableBinderTracking, boolean trackAllocation,
- boolean restrictedBackupMode, boolean persistent, Configuration config,
- CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings,
- String buildSerial) throws RemoteException;
- void scheduleExit() throws RemoteException;
- void scheduleSuicide() throws RemoteException;
- void scheduleConfigurationChanged(Configuration config) throws RemoteException;
- void updateTimeZone() throws RemoteException;
- void clearDnsCache() throws RemoteException;
- void setHttpProxy(String proxy, String port, String exclList,
- Uri pacFileUrl) throws RemoteException;
- void processInBackground() throws RemoteException;
- void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
- throws RemoteException;
- void dumpProvider(FileDescriptor fd, IBinder servicetoken, String[] args)
- throws RemoteException;
- void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
- int resultCode, String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser, int processState) throws RemoteException;
- void scheduleLowMemory() throws RemoteException;
- void scheduleActivityConfigurationChanged(IBinder token, Configuration overrideConfig,
- boolean reportToActivity) throws RemoteException;
- void profilerControl(boolean start, ProfilerInfo profilerInfo, int profileType)
- throws RemoteException;
- void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd)
- throws RemoteException;
- void setSchedulingGroup(int group) throws RemoteException;
- // the package has been removed, clean up internal references
- static final int PACKAGE_REMOVED = 0;
- static final int EXTERNAL_STORAGE_UNAVAILABLE = 1;
- // the package is being modified in-place, don't kill it and retain references to it
- static final int PACKAGE_REMOVED_DONT_KILL = 2;
- // a previously removed package was replaced with a new version [eg. upgrade, split added, ...]
- static final int PACKAGE_REPLACED = 3;
- void dispatchPackageBroadcast(int cmd, String[] packages) throws RemoteException;
- void scheduleCrash(String msg) throws RemoteException;
- void dumpActivity(FileDescriptor fd, IBinder servicetoken, String prefix, String[] args)
- throws RemoteException;
- void setCoreSettings(Bundle coreSettings) throws RemoteException;
- void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException;
- void scheduleTrimMemory(int level) throws RemoteException;
- void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin, boolean dumpInfo,
- boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable,
- String[] args) throws RemoteException;
- void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException;
- void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException;
- void unstableProviderDied(IBinder provider) throws RemoteException;
- void requestAssistContextExtras(IBinder activityToken, IBinder requestToken, int requestType,
- int sessionId) throws RemoteException;
- void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
- throws RemoteException;
- void scheduleOnNewActivityOptions(IBinder token, ActivityOptions options)
- throws RemoteException;
- void setProcessState(int state) throws RemoteException;
- void scheduleInstallProvider(ProviderInfo provider) throws RemoteException;
- void updateTimePrefs(boolean is24Hour) throws RemoteException;
- void scheduleCancelVisibleBehind(IBinder token) throws RemoteException;
- void scheduleBackgroundVisibleBehindChanged(IBinder token, boolean enabled) throws RemoteException;
- void scheduleEnterAnimationComplete(IBinder token) throws RemoteException;
- void notifyCleartextNetwork(byte[] firstPacket) throws RemoteException;
- void startBinderTracking() throws RemoteException;
- void stopBinderTrackingAndDump(FileDescriptor fd) throws RemoteException;
- void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode) throws RemoteException;
- void schedulePictureInPictureModeChanged(IBinder token, boolean isInPictureInPictureMode) throws RemoteException;
- void scheduleLocalVoiceInteractionStarted(IBinder token, IVoiceInteractor voiceInteractor) throws RemoteException;
- void handleTrustStorageUpdate() throws RemoteException;
-
- String descriptor = "android.app.IApplicationThread";
-
- int SCHEDULE_PAUSE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
- int SCHEDULE_STOP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
- int SCHEDULE_WINDOW_VISIBILITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
- int SCHEDULE_RESUME_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
- int SCHEDULE_SEND_RESULT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+5;
- int SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+6;
- int SCHEDULE_NEW_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+7;
- int SCHEDULE_FINISH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+8;
- int SCHEDULE_RECEIVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+9;
- int SCHEDULE_CREATE_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+10;
- int SCHEDULE_STOP_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+11;
- int BIND_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+12;
- int SCHEDULE_EXIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+13;
-
- int SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+15;
- int SCHEDULE_SERVICE_ARGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+16;
- int UPDATE_TIME_ZONE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+17;
- int PROCESS_IN_BACKGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+18;
- int SCHEDULE_BIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+19;
- int SCHEDULE_UNBIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+20;
- int DUMP_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+21;
- int SCHEDULE_REGISTERED_RECEIVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+22;
- int SCHEDULE_LOW_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+23;
- int SCHEDULE_ACTIVITY_CONFIGURATION_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+24;
- int SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+25;
- int SCHEDULE_SLEEPING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26;
- int PROFILER_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
- int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
- int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
- int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
- int SCHEDULE_ON_NEW_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
- int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
- int DISPATCH_PACKAGE_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
- int SCHEDULE_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34;
- int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35;
- int DUMP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
- int CLEAR_DNS_CACHE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37;
- int SET_HTTP_PROXY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38;
- int SET_CORE_SETTINGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+39;
- int UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+40;
- int SCHEDULE_TRIM_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+41;
- int DUMP_MEM_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+42;
- int DUMP_GFX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+43;
- int DUMP_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+44;
- int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
- int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
- int REQUEST_ASSIST_CONTEXT_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47;
- int SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48;
- int SET_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+49;
- int SCHEDULE_INSTALL_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+50;
- int UPDATE_TIME_PREFS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+51;
- int CANCEL_VISIBLE_BEHIND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+52;
- int BACKGROUND_VISIBLE_BEHIND_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+53;
- int ENTER_ANIMATION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+54;
- int NOTIFY_CLEARTEXT_NETWORK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+55;
- int START_BINDER_TRACKING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+56;
- int STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+57;
- int SCHEDULE_MULTI_WINDOW_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+58;
- int SCHEDULE_PICTURE_IN_PICTURE_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+59;
- int SCHEDULE_LOCAL_VOICE_INTERACTION_STARTED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+60;
- int HANDLE_TRUST_STORAGE_UPDATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+61;
-}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 530b8bb..4150172 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -134,11 +134,11 @@
this.mLockscreenVisibility = lockscreenVisibility;
}
- // Modifiable by apps.
+ // Modifiable by apps on channel creation.
/**
* Sets the ringtone that should be played for notifications posted to this channel if
- * the notifications don't supply a ringtone.
+ * the notifications don't supply a ringtone. Only modifiable on channel creation.
*/
public void setDefaultRingtone(Uri defaultRingtone) {
this.mRingtone = defaultRingtone;
@@ -146,7 +146,7 @@
/**
* Sets whether notifications posted to this channel should display notification lights,
- * on devices that support that feature.
+ * on devices that support that feature. Only modifiable on channel creation.
*/
public void setLights(boolean lights) {
this.mLights = lights;
@@ -154,7 +154,7 @@
/**
* Sets whether notification posted to this channel should vibrate, even if individual
- * notifications are marked as having vibration.
+ * notifications are marked as having vibration only modifiable on channel creation.
*/
public void setVibration(boolean vibration) {
this.mVibration = vibration;
diff --git a/core/java/android/app/ProfilerInfo.aidl b/core/java/android/app/ProfilerInfo.aidl
new file mode 100644
index 0000000..dc744b9
--- /dev/null
+++ b/core/java/android/app/ProfilerInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.app;
+
+/** @hide */
+parcelable ProfilerInfo;
\ No newline at end of file
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index c171a14..2d15beb 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -850,7 +850,10 @@
daj.setCompatibilityInfo(compat);
}
dm = getDisplayMetrics(displayId, daj);
- applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+
+ if (!isDefaultDisplay) {
+ applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+ }
if (hasOverrideConfiguration) {
tmpConfig.updateFrom(key.mOverrideConfiguration);
diff --git a/core/java/android/app/ResultInfo.aidl b/core/java/android/app/ResultInfo.aidl
new file mode 100644
index 0000000..6c12e1c
--- /dev/null
+++ b/core/java/android/app/ResultInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.app;
+
+/** @hide */
+parcelable ResultInfo;
\ No newline at end of file
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4ddcfe5..9ce9dec 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -26,6 +26,7 @@
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.Activity;
+import android.app.admin.PasswordMetrics;
import android.app.admin.SecurityLog.SecurityEvent;
import android.content.ComponentName;
import android.content.Context;
@@ -3523,12 +3524,10 @@
/**
* @hide
*/
- public void setActivePasswordState(int quality, int length, int letters, int uppercase,
- int lowercase, int numbers, int symbols, int nonletter, int userHandle) {
+ public void setActivePasswordState(PasswordMetrics metrics, int userHandle) {
if (mService != null) {
try {
- mService.setActivePasswordState(quality, length, letters, uppercase, lowercase,
- numbers, symbols, nonletter, userHandle);
+ mService.setActivePasswordState(metrics, userHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 8c376bb..22219d7 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -18,6 +18,7 @@
package android.app.admin;
import android.app.admin.SystemUpdatePolicy;
+import android.app.admin.PasswordMetrics;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
@@ -29,6 +30,7 @@
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.UserHandle;
+
import java.util.List;
/**
@@ -117,8 +119,7 @@
void forceRemoveActiveAdmin(in ComponentName policyReceiver, int userHandle);
boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle);
- void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
- int numbers, int symbols, int nonletter, int userHandle);
+ void setActivePasswordState(in PasswordMetrics metrics, int userHandle);
void reportFailedPasswordAttempt(int userHandle);
void reportSuccessfulPasswordAttempt(int userHandle);
void reportFailedFingerprintAttempt(int userHandle);
diff --git a/core/java/android/app/admin/PasswordMetrics.aidl b/core/java/android/app/admin/PasswordMetrics.aidl
new file mode 100644
index 0000000..90d7c69
--- /dev/null
+++ b/core/java/android/app/admin/PasswordMetrics.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 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.app.admin;
+
+parcelable PasswordMetrics;
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
new file mode 100644
index 0000000..ea3f560
--- /dev/null
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -0,0 +1,238 @@
+/*
+ * 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.app.admin;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.admin.DevicePolicyManager;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.io.IOException;
+
+/**
+ * A class that represents the metrics of a password that are used to decide whether or not a
+ * password meets the requirements.
+ *
+ * {@hide}
+ */
+public class PasswordMetrics implements Parcelable {
+ // Maximum allowed number of repeated or ordered characters in a sequence before we'll
+ // consider it a complex PIN/password.
+ public static final int MAX_ALLOWED_SEQUENCE = 3;
+
+ public int quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ public int length = 0;
+ public int letters = 0;
+ public int upperCase = 0;
+ public int lowerCase = 0;
+ public int numeric = 0;
+ public int symbols = 0;
+ public int nonLetter = 0;
+
+ public PasswordMetrics() {}
+
+ public PasswordMetrics(int quality, int length) {
+ this.quality = quality;
+ this.length = length;
+ }
+
+ public PasswordMetrics(int quality, int length, int letters, int upperCase, int lowerCase,
+ int numeric, int symbols, int nonLetter) {
+ this(quality, length);
+ this.letters = letters;
+ this.upperCase = upperCase;
+ this.lowerCase = lowerCase;
+ this.numeric = numeric;
+ this.symbols = symbols;
+ this.nonLetter = nonLetter;
+ }
+
+ private PasswordMetrics(Parcel in) {
+ quality = in.readInt();
+ length = in.readInt();
+ letters = in.readInt();
+ upperCase = in.readInt();
+ lowerCase = in.readInt();
+ numeric = in.readInt();
+ symbols = in.readInt();
+ nonLetter = in.readInt();
+ }
+
+ public boolean isDefault() {
+ return quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
+ && length == 0 && letters == 0 && upperCase == 0 && lowerCase == 0
+ && numeric == 0 && symbols == 0 && nonLetter == 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(quality);
+ dest.writeInt(length);
+ dest.writeInt(letters);
+ dest.writeInt(upperCase);
+ dest.writeInt(lowerCase);
+ dest.writeInt(numeric);
+ dest.writeInt(symbols);
+ dest.writeInt(nonLetter);
+ }
+
+ public static final Parcelable.Creator<PasswordMetrics> CREATOR
+ = new Parcelable.Creator<PasswordMetrics>() {
+ public PasswordMetrics createFromParcel(Parcel in) {
+ return new PasswordMetrics(in);
+ }
+
+ public PasswordMetrics[] newArray(int size) {
+ return new PasswordMetrics[size];
+ }
+ };
+
+ public static PasswordMetrics computeForPassword(@NonNull String password) {
+ // Analyse the characters used
+ int letters = 0;
+ int upperCase = 0;
+ int lowerCase = 0;
+ int numeric = 0;
+ int symbols = 0;
+ int nonLetter = 0;
+ final int length = password.length();
+ for (int i = 0; i < length; i++) {
+ switch (categoryChar(password.charAt(i))) {
+ case CHAR_LOWER_CASE:
+ letters++;
+ lowerCase++;
+ break;
+ case CHAR_UPPER_CASE:
+ letters++;
+ upperCase++;
+ break;
+ case CHAR_DIGIT:
+ numeric++;
+ nonLetter++;
+ break;
+ case CHAR_SYMBOL:
+ symbols++;
+ nonLetter++;
+ break;
+ }
+ }
+
+ // Determine the quality of the password
+ final boolean hasNumeric = numeric > 0;
+ final boolean hasNonNumeric = (letters + symbols) > 0;
+ final int quality;
+ if (hasNonNumeric && hasNumeric) {
+ quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+ } else if (hasNonNumeric) {
+ quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+ } else if (hasNumeric) {
+ quality = maxLengthSequence(password) > MAX_ALLOWED_SEQUENCE
+ ? DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ : DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+ } else {
+ quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ }
+
+ return new PasswordMetrics(
+ quality, length, letters, upperCase, lowerCase, numeric, symbols, nonLetter);
+ }
+
+ /*
+ * Returns the maximum length of a sequential characters. A sequence is defined as
+ * monotonically increasing characters with a constant interval or the same character repeated.
+ *
+ * For example:
+ * maxLengthSequence("1234") == 4
+ * maxLengthSequence("13579") == 5
+ * maxLengthSequence("1234abc") == 4
+ * maxLengthSequence("aabc") == 3
+ * maxLengthSequence("qwertyuio") == 1
+ * maxLengthSequence("@ABC") == 3
+ * maxLengthSequence(";;;;") == 4 (anything that repeats)
+ * maxLengthSequence(":;<=>") == 1 (ordered, but not composed of alphas or digits)
+ *
+ * @param string the pass
+ * @return the number of sequential letters or digits
+ */
+ public static int maxLengthSequence(@NonNull String string) {
+ if (string.length() == 0) return 0;
+ char previousChar = string.charAt(0);
+ @CharacterCatagory int category = categoryChar(previousChar); //current sequence category
+ int diff = 0; //difference between two consecutive characters
+ boolean hasDiff = false; //if we are currently targeting a sequence
+ int maxLength = 0; //maximum length of a sequence already found
+ int startSequence = 0; //where the current sequence started
+ for (int current = 1; current < string.length(); current++) {
+ char currentChar = string.charAt(current);
+ @CharacterCatagory int categoryCurrent = categoryChar(currentChar);
+ int currentDiff = (int) currentChar - (int) previousChar;
+ if (categoryCurrent != category || Math.abs(currentDiff) > maxDiffCategory(category)) {
+ maxLength = Math.max(maxLength, current - startSequence);
+ startSequence = current;
+ hasDiff = false;
+ category = categoryCurrent;
+ }
+ else {
+ if(hasDiff && currentDiff != diff) {
+ maxLength = Math.max(maxLength, current - startSequence);
+ startSequence = current - 1;
+ }
+ diff = currentDiff;
+ hasDiff = true;
+ }
+ previousChar = currentChar;
+ }
+ maxLength = Math.max(maxLength, string.length() - startSequence);
+ return maxLength;
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({CHAR_UPPER_CASE, CHAR_LOWER_CASE, CHAR_DIGIT, CHAR_SYMBOL})
+ private @interface CharacterCatagory {}
+ private static final int CHAR_LOWER_CASE = 0;
+ private static final int CHAR_UPPER_CASE = 1;
+ private static final int CHAR_DIGIT = 2;
+ private static final int CHAR_SYMBOL = 3;
+
+ @CharacterCatagory
+ private static int categoryChar(char c) {
+ if ('a' <= c && c <= 'z') return CHAR_LOWER_CASE;
+ if ('A' <= c && c <= 'Z') return CHAR_UPPER_CASE;
+ if ('0' <= c && c <= '9') return CHAR_DIGIT;
+ return CHAR_SYMBOL;
+ }
+
+ private static int maxDiffCategory(@CharacterCatagory int category) {
+ switch (category) {
+ case CHAR_LOWER_CASE:
+ case CHAR_UPPER_CASE:
+ return 1;
+ case CHAR_DIGIT:
+ return 10;
+ default:
+ return 0;
+ }
+ }
+}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index bb5f7a1..4c8360f 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -377,13 +377,13 @@
* AppWidget provider. Will animate into these new views as needed
*/
public void updateAppWidget(RemoteViews remoteViews) {
- applyRemoteViews(remoteViews);
+ applyRemoteViews(remoteViews, true);
}
/**
* @hide
*/
- protected void applyRemoteViews(RemoteViews remoteViews) {
+ protected void applyRemoteViews(RemoteViews remoteViews, boolean useAsyncIfPossible) {
if (LOGD) Log.d(TAG, "updateAppWidget called mOld=" + mOld);
boolean recycled = false;
@@ -423,7 +423,7 @@
mLayoutId = -1;
mViewMode = VIEW_MODE_DEFAULT;
} else {
- if (mAsyncExecutor != null) {
+ if (mAsyncExecutor != null && useAsyncIfPossible) {
inflateAsync(remoteViews);
return;
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index c71dc7c..542b06b 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -789,19 +789,13 @@
public boolean enableBLE() {
if (!isBleScanAlwaysAvailable()) return false;
- if (isLeEnabled() == true) {
- if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!");
- try {
- mManagerService.updateBleAppCount(mToken, true);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- return true;
- }
-
try {
- if (DBG) Log.d(TAG, "Calling enableBLE");
mManagerService.updateBleAppCount(mToken, true);
+ if (isLeEnabled()) {
+ if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled");
+ return true;
+ }
+ if (DBG) Log.d(TAG, "enableBLE(): Calling enable");
return mManagerService.enable(ActivityThread.currentPackageName());
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1522,6 +1516,36 @@
}
/**
+ * Gets the currently supported profiles by the adapter.
+ *
+ *<p> This can be used to check whether a profile is supported before attempting
+ * to connect to its respective proxy.
+ *
+ * @return a list of integers indicating the ids of supported profiles as defined in
+ * {@link BluetoothProfile}.
+ * @hide
+ */
+ public List<Integer> getSupportedProfiles() {
+ final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>();
+
+ try {
+ synchronized (mManagerCallback) {
+ if (mService != null) {
+ final long supportedProfilesBitMask = mService.getSupportedProfiles();
+
+ for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) {
+ if ((supportedProfilesBitMask & (1 << i)) != 0) {
+ supportedProfiles.add(i);
+ }
+ }
+ }
+ }
+ } catch (RemoteException e) {Log.e(TAG, "getSupportedProfiles:", e);}
+
+ return supportedProfiles;
+ }
+
+ /**
* Get the current connection state of the local Bluetooth adapter.
* This can be used to check whether the local Bluetooth adapter is connected
* to any profile of any other remote Bluetooth Device.
@@ -2030,7 +2054,7 @@
final private IBluetoothManagerCallback mManagerCallback =
new IBluetoothManagerCallback.Stub() {
public void onBluetoothServiceUp(IBluetooth bluetoothService) {
- if (VDBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
+ if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
mServiceLock.writeLock().lock();
mService = bluetoothService;
@@ -2052,7 +2076,7 @@
}
public void onBluetoothServiceDown() {
- if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
+ if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
try {
mServiceLock.writeLock().lock();
@@ -2080,7 +2104,7 @@
}
public void onBrEdrDown() {
- if (VDBG) Log.i(TAG, "on QBrEdrDown: ");
+ if (DBG) Log.i(TAG, "onBrEdrDown:");
}
};
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 2bb9012..0763149 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -44,14 +44,18 @@
private IBluetoothGatt mService;
private BluetoothGattCallback mCallback;
private int mClientIf;
- private boolean mAuthRetry = false;
private BluetoothDevice mDevice;
private boolean mAutoConnect;
+ private int mAuthRetryState;
private int mConnState;
private final Object mStateLock = new Object();
private Boolean mDeviceBusy = false;
private int mTransport;
+ private static final int AUTH_RETRY_STATE_IDLE = 0;
+ private static final int AUTH_RETRY_STATE_NO_MITM = 1;
+ private static final int AUTH_RETRY_STATE_MITM = 2;
+
private static final int CONN_STATE_IDLE = 0;
private static final int CONN_STATE_CONNECTING = 1;
private static final int CONN_STATE_CONNECTED = 2;
@@ -259,17 +263,19 @@
if ((status == GATT_INSUFFICIENT_AUTHENTICATION
|| status == GATT_INSUFFICIENT_ENCRYPTION)
- && mAuthRetry == false) {
+ && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
try {
- mAuthRetry = true;
- mService.readCharacteristic(mClientIf, address, handle, AUTHENTICATION_MITM);
+ final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
+ AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
+ mService.readCharacteristic(mClientIf, address, handle, authReq);
+ mAuthRetryState++;
return;
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
- mAuthRetry = false;
+ mAuthRetryState = AUTH_RETRY_STATE_IDLE;
BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
if (characteristic == null) {
@@ -308,19 +314,20 @@
if ((status == GATT_INSUFFICIENT_AUTHENTICATION
|| status == GATT_INSUFFICIENT_ENCRYPTION)
- && mAuthRetry == false) {
+ && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
try {
- mAuthRetry = true;
+ final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
+ AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
mService.writeCharacteristic(mClientIf, address, handle,
- characteristic.getWriteType(), AUTHENTICATION_MITM,
- characteristic.getValue());
+ characteristic.getWriteType(), authReq, characteristic.getValue());
+ mAuthRetryState++;
return;
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
- mAuthRetry = false;
+ mAuthRetryState = AUTH_RETRY_STATE_IDLE;
try {
mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
@@ -375,17 +382,19 @@
if ((status == GATT_INSUFFICIENT_AUTHENTICATION
|| status == GATT_INSUFFICIENT_ENCRYPTION)
- && mAuthRetry == false) {
+ && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
try {
- mAuthRetry = true;
- mService.readDescriptor(mClientIf, address, handle, AUTHENTICATION_MITM);
+ final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
+ AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
+ mService.readDescriptor(mClientIf, address, handle, authReq);
+ mAuthRetryState++;
return;
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
- mAuthRetry = true;
+ mAuthRetryState = AUTH_RETRY_STATE_IDLE;
try {
mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
@@ -414,18 +423,20 @@
if ((status == GATT_INSUFFICIENT_AUTHENTICATION
|| status == GATT_INSUFFICIENT_ENCRYPTION)
- && mAuthRetry == false) {
+ && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
try {
- mAuthRetry = true;
+ final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
+ AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
mService.writeDescriptor(mClientIf, address, handle,
- AUTHENTICATION_MITM, descriptor.getValue());
+ authReq, descriptor.getValue());
+ mAuthRetryState++;
return;
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
- mAuthRetry = false;
+ mAuthRetryState = AUTH_RETRY_STATE_IDLE;
try {
mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
@@ -499,6 +510,7 @@
mServices = new ArrayList<BluetoothGattService>();
mConnState = CONN_STATE_IDLE;
+ mAuthRetryState = AUTH_RETRY_STATE_IDLE;
}
/**
@@ -512,6 +524,7 @@
unregisterApp();
mConnState = CONN_STATE_CLOSED;
+ mAuthRetryState = AUTH_RETRY_STATE_IDLE;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index eee66d1..20d95cc 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -137,6 +137,13 @@
public static final int PBAP_CLIENT = 17;
/**
+ * Max profile ID. This value should be updated whenever a new profile is added to match
+ * the largest value assigned to a profile.
+ * @hide
+ */
+ public static final int MAX_PROFILE_ID = 17;
+
+ /**
* Default priority for devices that we try to auto-connect to and
* and allow incoming connections for the profile
* @hide
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 2ded4c8..243579a 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -275,6 +275,48 @@
}
/**
+ * Parse UUID to bytes. The returned value is shortest representation, a 16-bit, 32-bit or 128-bit UUID,
+ * Note returned value is little endian (Bluetooth).
+ *
+ * @param uuid uuid to parse.
+ * @return shortest representation of {@code uuid} as bytes.
+ * @throws IllegalArgumentException If the {@code uuid} is null.
+ */
+ public static byte[] uuidToBytes(ParcelUuid uuid) {
+ if (uuid == null) {
+ throw new IllegalArgumentException("uuid cannot be null");
+ }
+
+ if (is16BitUuid(uuid)) {
+ byte[] uuidBytes = new byte[UUID_BYTES_16_BIT];
+ int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
+ uuidBytes[0] = (byte)(uuidVal & 0xFF);
+ uuidBytes[1] = (byte)((uuidVal & 0xFF00) >> 8);
+ return uuidBytes;
+ }
+
+ if (is32BitUuid(uuid)) {
+ byte[] uuidBytes = new byte[UUID_BYTES_32_BIT];
+ int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
+ uuidBytes[0] = (byte)(uuidVal & 0xFF);
+ uuidBytes[1] = (byte)((uuidVal & 0xFF00) >> 8);
+ uuidBytes[2] = (byte)((uuidVal & 0xFF0000) >> 16);
+ uuidBytes[3] = (byte)((uuidVal & 0xFF000000) >> 24);
+ return uuidBytes;
+ }
+
+ // Construct a 128 bit UUID.
+ long msb = uuid.getUuid().getMostSignificantBits();
+ long lsb = uuid.getUuid().getLeastSignificantBits();
+
+ byte[] uuidBytes = new byte[UUID_BYTES_128_BIT];
+ ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
+ buf.putLong(8, msb);
+ buf.putLong(0, lsb);
+ return uuidBytes;
+ }
+
+ /**
* Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid.
*
* @param parcelUuid
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 8c98536..96a1ae8 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -63,6 +63,7 @@
boolean removeBond(in BluetoothDevice device);
int getBondState(in BluetoothDevice device);
boolean isBondingInitiatedLocally(in BluetoothDevice device);
+ long getSupportedProfiles();
int getConnectionState(in BluetoothDevice device);
String getRemoteName(in BluetoothDevice device);
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index ed0ac53..a854b89 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -46,7 +46,7 @@
import java.util.Set;
/**
- * Represents a "launcher shortcut" that can be published via {@link ShortcutManager}.
+ * Represents a shortcut that can be published via {@link ShortcutManager}.
*
* @see ShortcutManager
*/
@@ -776,17 +776,17 @@
* activity is published using
* {@link ShortcutManager#addDynamicShortcuts(List)} or
* {@link ShortcutManager#setDynamicShortcuts(List)},
- * the first main activity defined in the application's <code>AndroidManifest.xml</code>
+ * the first main activity defined in the app's <code>AndroidManifest.xml</code>
* file is used.
*
* <li>Only "main" activities—ones that define the {@link Intent#ACTION_MAIN}
* and {@link Intent#CATEGORY_LAUNCHER} intent filters—can be target
* activities.
*
- * <li>By default, the first main activity defined in the application manifest is
+ * <li>By default, the first main activity defined in the app's manifest is
* the target activity.
*
- * <li>A target activity must belong to the publisher application.
+ * <li>A target activity must belong to the publisher app.
* </ul>
*
* @see ShortcutInfo#getActivity()
@@ -802,7 +802,7 @@
*
* <p>Icons are not available on {@link ShortcutInfo} instances
* returned by {@link ShortcutManager} or {@link LauncherApps}. The default launcher
- * application can use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}
+ * app can use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}
* or {@link LauncherApps#getShortcutBadgedIconDrawable(ShortcutInfo, int)} to fetch
* shortcut icons.
*
@@ -933,8 +933,8 @@
}
/**
- * Sets categories for a shortcut. Launcher applications may use this information to
- * categorise shortcuts.
+ * Sets categories for a shortcut. Launcher apps may use this information to
+ * categorize shortcuts.
*
* @see #SHORTCUT_CATEGORY_CONVERSATION
* @see ShortcutInfo#getCategories()
@@ -953,9 +953,9 @@
* {@link ShortcutManager#addDynamicShortcuts(List)} or
* {@link ShortcutManager#setDynamicShortcuts(List)}.
*
- * <p>A shortcut can launch any intent that the publisher application has permission to
+ * <p>A shortcut can launch any intent that the publisher app has permission to
* launch. For example, a shortcut can launch an unexported activity within the publisher
- * application. A shortcut intent doesn't have to point at the target activity.
+ * app. A shortcut intent doesn't have to point at the target activity.
*
* <p>The given {@code intent} can contain extras, but these extras must contain values
* of primitive types in order for the system to persist these values.
@@ -970,7 +970,9 @@
/**
* Sets multiple intents instead of a single intent, in order to launch an activity with
- * other activities in back stack. Use {@link TaskStackBuilder} to build intents.
+ * other activities in back stack. Use {@link TaskStackBuilder} to build intents. The
+ * last element in the list represents the only intent that doesn't place an activity on
+ * the back stack.
* See the {@link ShortcutManager} javadoc for details.
*
* @see Builder#setIntent(Intent)
@@ -1006,9 +1008,9 @@
}
/**
- * Extras that application can set for any purpose.
+ * Extras that the app can set for any purpose.
*
- * <p>Applications can store arbitrary shortcut metadata in extras and retrieve the
+ * <p>Apps can store arbitrary shortcut metadata in extras and retrieve the
* metadata later using {@link ShortcutInfo#getExtras()}.
*/
@NonNull
@@ -1029,7 +1031,7 @@
/**
* Returns the ID of a shortcut.
*
- * <p>Shortcut IDs are unique within each publisher application and must be stable across
+ * <p>Shortcut IDs are unique within each publisher app and must be stable across
* devices so that shortcuts will still be valid when restored on a different device.
* See {@link ShortcutManager} for details.
*/
@@ -1039,7 +1041,7 @@
}
/**
- * Return the package name of the publisher application.
+ * Return the package name of the publisher app.
*/
@NonNull
public String getPackage() {
@@ -1050,7 +1052,7 @@
* Return the target activity.
*
* <p>This has nothing to do with the activity that this shortcut will launch.
- * Launcher applications should show the launcher icon for the returned activity alongside
+ * Launcher apps should show the launcher icon for the returned activity alongside
* this shortcut.
*
* @see Builder#setActivity
@@ -1102,7 +1104,7 @@
}
/**
- * Return the shorter description of a shortcut.
+ * Return the short description of a shortcut.
*
* @see Builder#setShortLabel(CharSequence)
*/
@@ -1117,7 +1119,7 @@
}
/**
- * Return the longer description of a shortcut.
+ * Return the long description of a shortcut.
*
* @see Builder#setLongLabel(CharSequence)
*/
@@ -1161,7 +1163,7 @@
* Returns the intent that is executed when the user selects this shortcut.
* If setIntents() was used, then return the last intent in the array.
*
- * <p>Launcher applications <b>cannot</b> see the intent. If a {@link ShortcutInfo} is
+ * <p>Launcher apps <b>cannot</b> see the intent. If a {@link ShortcutInfo} is
* obtained via {@link LauncherApps}, then this method will always return null.
* Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}.
*
@@ -1180,7 +1182,7 @@
/**
* Return the intent set with {@link Builder#setIntents(Intent[])}.
*
- * <p>Launcher applications <b>cannot</b> see the intents. If a {@link ShortcutInfo} is
+ * <p>Launcher apps <b>cannot</b> see the intents. If a {@link ShortcutInfo} is
* obtained via {@link LauncherApps}, then this method will always return null.
* Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}.
*
@@ -1219,15 +1221,15 @@
/**
* "Rank" of a shortcut, which is a non-negative, sequential value that's unique for each
- * {@link #getActivity} for each of the two kinds, dynamic shortcuts and manifest shortcuts.
+ * {@link #getActivity} for each of the two types of shortcuts (static and dynamic).
*
- * <p>Because manifest shortcuts and dynamic shortcuts have overlapping ranks,
- * when a launcher application shows shortcuts for an activity, it should first show
- * the manifest shortcuts followed by the dynamic shortcuts. Within each of those categories,
+ * <p>Because static shortcuts and dynamic shortcuts have overlapping ranks,
+ * when a launcher app shows shortcuts for an activity, it should first show
+ * the static shortcuts, followed by the dynamic shortcuts. Within each of those categories,
* shortcuts should be sorted by rank in ascending order.
*
- * <p>"Floating" shortcuts (i.e. shortcuts that are neither dynamic nor manifest) will all
- * have rank 0, because there's no sorting for them.
+ * <p><em>Floating shortcuts</em>, or shortcuts that are neither static nor dynamic, will all
+ * have rank 0, because they aren't sorted.
*
* See the {@link ShortcutManager}'s class javadoc for details.
*
@@ -1274,7 +1276,7 @@
}
/**
- * Extras that application can set to any purposes.
+ * Extras that the app can set for any purpose.
*
* @see Builder#setExtras(PersistableBundle)
*/
@@ -1339,12 +1341,13 @@
}
/**
- * Return whether a shortcut is published from AndroidManifest.xml or not. If {@code true},
- * it's also {@link #isImmutable()}.
+ * Return whether a shortcut is static; that is, whether a shortcut is
+ * published from AndroidManifest.xml. If {@code true}, the shortcut is
+ * also {@link #isImmutable()}.
*
* <p>When an app is upgraded and a shortcut is no longer published from AndroidManifest.xml,
- * this will be set to {@code false}. If the shortcut is not pinned, then it'll just disappear.
- * However, if it's pinned, it will still be alive, and {@link #isEnabled()} will be
+ * this will be set to {@code false}. If the shortcut is not pinned, then it'll disappear.
+ * However, if it's pinned, it will still be visible, {@link #isEnabled()} will be
* {@code false} and {@link #isImmutable()} will be {@code true}.
*/
public boolean isDeclaredInManifest() {
@@ -1358,7 +1361,7 @@
}
/**
- * @return true if pinned but neither dynamic nor manifest.
+ * @return true if pinned but neither static nor dynamic.
* @hide
*/
public boolean isFloating() {
@@ -1374,9 +1377,10 @@
* Return if a shortcut is immutable, in which case it cannot be modified with any of
* {@link ShortcutManager} APIs.
*
- * <p>All manifest shortcuts are immutable. When a manifest shortcut is pinned and then
- * disabled because the app is upgraded and its AndroidManifest.xml no longer publishes it,
- * {@link #isDeclaredInManifest()} returns {@code false}, but it is still immutable.
+ * <p>All static shortcuts are immutable. When a static shortcut is pinned and is then
+ * disabled because it doesn't appear in AndroidManifest.xml for a newer version of the
+ * app, {@link #isDeclaredInManifest()} returns {@code false}, but the shortcut
+ * is still immutable.
*
* <p>All shortcuts originally published via the {@link ShortcutManager} APIs
* are all mutable.
@@ -1561,7 +1565,7 @@
}
/**
- * Replaces the intent
+ * Replaces the intent.
*
* @throws IllegalArgumentException when extra is not compatible with {@link PersistableBundle}.
*
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 96ad67c..a93870e 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -31,87 +31,90 @@
import java.util.List;
/**
- * The ShortcutManager manages "launcher shortcuts" (or simply "shortcuts"). Shortcuts provide
- * users
- * with quick access to activities other than an application's main activity in the currently-active
+ * The ShortcutManager manages an app's <em>shortcuts</em>. Shortcuts provide users
+ * with quick access to activities other than an app's main activity in the currently-active
* launcher. For example,
- * an email application may publish the "compose new email" action, which will directly open the
+ * an email app may publish the "compose new email" action, which will directly open the
* compose activity. The {@link ShortcutInfo} class contains information about each of the
* shortcuts themselves.
*
- * <h3>Dynamic Shortcuts and Manifest Shortcuts</h3>
+ * <h3>Static Shortcuts and Dynamic Shortcuts</h3>
*
- * There are two ways to publish shortcuts: manifest shortcuts and dynamic shortcuts.
+ * <p>
+ * There are two ways to publish shortcuts: static shortcuts and dynamic shortcuts.
*
* <ul>
- * <li>Manifest shortcuts are declared in a resource
- * XML, which is referenced in the publisher application's <code>AndroidManifest.xml</code> file.
- * Manifest shortcuts are published when an application is installed,
- * and the details of these shortcuts change when an application is upgraded with an updated XML
+ * <li>Static shortcuts are declared in a resource
+ * XML file, which is referenced in the publisher app's <code>AndroidManifest.xml</code> file.
+ * Static shortcuts are published when an app is installed,
+ * and the details of these shortcuts change when an app is upgraded with an updated XML
* file.
- * Manifest shortcuts are immutable, and their
+ * Static shortcuts are immutable, and their
* definitions, such as icons and labels, cannot be changed dynamically without upgrading the
- * publisher application.
+ * publisher app.
*
- * <li>Dynamic shortcuts are published at runtime using the {@link ShortcutManager} APIs.
- * Applications can publish, update, and remove dynamic shortcuts at runtime.
+ * <li>Dynamic shortcuts are published at runtime using this class's APIs.
+ * Apps can publish, update, and remove dynamic shortcuts at runtime.
* </ul>
*
- * <p>Only "main" activities—activities that handle the {@code MAIN} action and the
+ * <p>Only main activities—activities that handle the {@code MAIN} action and the
* {@code LAUNCHER} category—can have shortcuts.
- * If an application has multiple main activities, these activities will have different sets
+ * If an app has multiple main activities, these activities have different sets
* of shortcuts.
*
- * <p>Dynamic shortcuts and manifest shortcuts are shown in the currently active launcher when
- * the user long-presses on an application launcher icon. The actual gesture may be different
- * depending on the launcher application.
+ * <p>Static shortcuts and dynamic shortcuts are shown in the currently active launcher when
+ * the user long-presses on an app's launcher icon.
+ *
+ * <p class="note"><strong>Note: </strong>The actual gesture may be different
+ * depending on the launcher app.
*
* <p>Each launcher icon can have at most {@link #getMaxShortcutCountPerActivity()} number of
- * dynamic and manifest shortcuts combined.
+ * static and dynamic shortcuts combined.
*
*
* <h3>Pinning Shortcuts</h3>
*
- * Launcher applications allow users to "pin" shortcuts so they're easier to access. Both manifest
+ * <p>
+ * Launcher apps allow users to <em>pin</em> shortcuts so they're easier to access. Both static
* and dynamic shortcuts can be pinned.
* Pinned shortcuts <b>cannot</b> be removed by publisher
- * applications; they're removed only when the user removes them,
- * when the publisher application is uninstalled, or when the
- * user performs the "clear data" action on the publisher application from the device's Settings
- * application.
+ * apps; they're removed only when the user removes them,
+ * when the publisher app is uninstalled, or when the
+ * user performs the <strong>clear data</strong> action on the publisher app from the device's Settings
+ * app.
*
- * <p>However, the publisher application can <em>disable</em> pinned shortcuts so they cannot be
+ * <p>However, the publisher app can <em>disable</em> pinned shortcuts so they cannot be
* started. See the following sections for details.
*
*
* <h3>Updating and Disabling Shortcuts</h3>
*
* <p>When a dynamic shortcut is pinned, even when the publisher removes it as a dynamic shortcut,
- * the pinned shortcut will still be visible and launchable. This allows an application to have
+ * the pinned shortcut will still be visible and launchable. This allows an app to have
* more than {@link #getMaxShortcutCountPerActivity()} number of shortcuts.
*
* <p>For example, suppose {@link #getMaxShortcutCountPerActivity()} is 5:
- * <ul>
- * <li>A chat application publishes 5 dynamic shortcuts for the 5 most recent
- * conversations, "c1" - "c5".
+ * <ol>
+ * <li>A chat app publishes 5 dynamic shortcuts for the 5 most recent
+ * conversations (c1, c2, ..., c5).
*
* <li>The user pins all 5 of the shortcuts.
*
- * <li>Later, the user has started 3 additional conversations ("c6", "c7", and "c8"),
- * so the publisher application
+ * <li>Later, the user has started 3 additional conversations (c6, c7, and c8),
+ * so the publisher app
* re-publishes its dynamic shortcuts. The new dynamic shortcut list is:
- * "c4", "c5", "c6", "c7", and "c8".
- * The publisher application has to remove "c1", "c2", and "c3" because it can't have more than
+ * c4, c5, ..., c8.
+ * The publisher app has to remove c1, c2, and c3 because it can't have more than
* 5 dynamic shortcuts.
*
- * <li>However, even though "c1", "c2" and "c3" are no longer dynamic shortcuts, the pinned
+ * <li>However, even though c1, c2, and c3 are no longer dynamic shortcuts, the pinned
* shortcuts for these conversations are still available and launchable.
*
* <li>At this point, the user can access a total of 8 shortcuts that link to activities in
- * the publisher application, including the 3 pinned
- * shortcuts, even though it's allowed to have at most 5 dynamic shortcuts.
+ * the publisher app, including the 3 pinned
+ * shortcuts, even though an app can have at most 5 dynamic shortcuts.
*
- * <li>The application can use {@link #updateShortcuts(List)} to update any of the existing
+ * <li>The app can use {@link #updateShortcuts(List)} to update <em>any</em> of the existing
* 8 shortcuts, when, for example, the chat peers' icons have changed.
* </ul>
* The {@link #addDynamicShortcuts(List)} and {@link #setDynamicShortcuts(List)} methods
@@ -121,104 +124,108 @@
* lists of shortcuts to dynamic shortcuts.
*
*
- * <h4>Disabling Manifest Shortcuts</h4>
- * When an application is upgraded and the new version
- * no longer uses a manifest shortcut that appeared in the previous version, this deprecated
- * shortcut will no longer be published as a manifest shortcut.
+ * <h4>Disabling Static Shortcuts</h4>
+ * When an app is upgraded and the new version
+ * no longer uses a static shortcut that appeared in the previous version, this deprecated
+ * shortcut will no longer be published as a static shortcut.
*
* <p>If the deprecated shortcut is pinned, then the pinned shortcut will remain on the launcher,
* but it will be disabled automatically.
- * Note that, in this case, the pinned shortcut is no longer a manifest shortcut, but it's
- * still <b>immutable</b> and cannot be updated using the {@link ShortcutManager} APIs.
+ * Note that, in this case, the pinned shortcut is no longer a static shortcut, but it's
+ * still <b>immutable</b>. Therefore, it cannot be updated using this class's APIs.
*
*
* <h4>Disabling Dynamic Shortcuts</h4>
* Sometimes pinned shortcuts become obsolete and may not be usable. For example, a pinned shortcut
- * to a group chat will be unusable when the associated group chat is deleted. In cases like this,
- * applications should use {@link #disableShortcuts(List)}, which will remove the specified dynamic
- * shortcuts and also make any specified pinned shortcuts un-launchable.
+ * to a group chat becomes unusable when the associated group chat is deleted. In cases like this,
+ * apps should use {@link #disableShortcuts(List)}, which removes the specified dynamic
+ * shortcuts and also makes any specified pinned shortcuts un-launchable.
* The {@link #disableShortcuts(List, CharSequence)} method can also be used to disabled shortcuts
* and show users a custom error message when they attempt to launch the disabled shortcuts.
*
*
- * <h3>Publishing Manifest Shortcuts</h3>
+ * <h3>Publishing Static Shortcuts</h3>
*
- * In order to add manifest shortcuts to your application, first add
+ * <p>
+ * In order to add static shortcuts to your app, first add
* {@code <meta-data android:name="android.app.shortcuts" />} to your main activity in
* AndroidManifest.xml:
* <pre>
- * <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- * package="com.example.myapplication">
- * <application . . .>
- * <activity android:name="Main">
- * <intent-filter>
- * <action android:name="android.intent.action.MAIN" />
- * <category android:name="android.intent.category.LAUNCHER" />
- * </intent-filter>
- * <b><meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/></b>
- * </activity>
- * </application>
- * </manifest>
+ *<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ * package="com.example.myapplication">
+ * <application ... >
+ * <activity android:name="Main">
+ * <intent-filter>
+ * <action android:name="android.intent.action.MAIN" />
+ * <category android:name="android.intent.category.LAUNCHER" />
+ * </intent-filter>
+ * <strong><meta-data android:name="android.app.shortcuts"
+ * android:resource="@xml/shortcuts" /></strong>
+ * </activity>
+ * </application>
+ *</manifest>
* </pre>
*
- * Then, define your application's manifest shortcuts in the <code>res/xml/shortcuts.xml</code>
+ * Then, define your app's static shortcuts in the <code>res/xml/shortcuts.xml</code>
* file:
* <pre>
- * <shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
- * <shortcut
- * android:shortcutId="compose"
- * android:enabled="true"
- * android:icon="@drawable/compose_icon"
- * android:shortcutShortLabel="@string/compose_shortcut_short_label1"
- * android:shortcutLongLabel="@string/compose_shortcut_long_label1"
- * android:shortcutDisabledMessage="@string/compose_disabled_message1"
- * >
- * <intent
- * android:action="android.intent.action.VIEW"
- * android:targetPackage="com.example.myapplication"
- * android:targetClass="com.example.myapplication.ComposeActivity" />
- * <!-- more intents can go here; see below -->
- * <categories android:name="android.shortcut.conversation" />
- * </shortcut>
- * <!-- more shortcuts can go here -->
- * </shortcuts>
+ *<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
+ * <shortcut
+ * android:shortcutId="compose"
+ * android:enabled="true"
+ * android:icon="@drawable/compose_icon"
+ * android:shortcutShortLabel="@string/compose_shortcut_short_label1"
+ * android:shortcutLongLabel="@string/compose_shortcut_long_label1"
+ * android:shortcutDisabledMessage="@string/compose_disabled_message1">
+ * <intent
+ * android:action="android.intent.action.VIEW"
+ * android:targetPackage="com.example.myapplication"
+ * android:targetClass="com.example.myapplication.ComposeActivity" />
+ * <!-- If your shortcut is associated with multiple intents, include them
+ * here. The last intent in the list is what the user sees when they
+ * launch this shortcut. -->
+ * <categories android:name="android.shortcut.conversation" />
+ * </shortcut>
+ * <!-- Specify more shortcuts here. -->
+ *</shortcuts>
* </pre>
*
- * The following list includes descriptions for the different attributes within a manifest shortcut:
+ * The following list includes descriptions for the different attributes within a static shortcut:
* <dl>
- * <dt>android:shortcutId</dt>
+ * <dt>{@code android:shortcutId}</dt>
* <dd>Mandatory shortcut ID</dd>
*
- * <dt>android:enabled</dt>
+ * <dt>{@code android:enabled}</dt>
* <dd>Default is {@code true}. Can be set to {@code false} in order
- * to disable a manifest shortcut that was published in a previous version and and set a custom
- * disabled message. If a custom disabled message is not needed, then a manifest shortcut can
+ * to disable a static shortcut that was published in a previous version and set a custom
+ * disabled message. If a custom disabled message is not needed, then a static shortcut can
* be simply removed from the XML file rather than keeping it with {@code enabled="false"}.</dd>
*
- * <dt>android:icon</dt>
+ * <dt>{@code android:icon}</dt>
* <dd>Shortcut icon.</dd>
*
- * <dt>android:shortcutShortLabel</dt>
+ * <dt>{@code android:shortcutShortLabel}</dt>
* <dd>Mandatory shortcut short label.
* See {@link ShortcutInfo.Builder#setShortLabel(CharSequence)}.</dd>
*
- * <dt>android:shortcutLongLabel</dt>
+ * <dt>{@code android:shortcutLongLabel}</dt>
* <dd>Shortcut long label.
* See {@link ShortcutInfo.Builder#setLongLabel(CharSequence)}.</dd>
*
- * <dt>android:shortcutDisabledMessage</dt>
+ * <dt>{@code android:shortcutDisabledMessage}</dt>
* <dd>When {@code android:enabled} is set to
* {@code false}, this attribute is used to display a custom disabled message.</dd>
*
- * <dt>intent</dt>
+ * <dt>{@code intent}</dt>
* <dd>Intent to launch when the user selects the shortcut.
* {@code android:action} is mandatory.
* See <a href="{@docRoot}guide/topics/ui/settings.html#Intents">Using intents</a> for the
* other supported tags.
- * You can provide multiple intents for a single shortcut so that an activity is launched
- * with other activities in the back stack. See {@link android.app.TaskStackBuilder} for details.
+ * You can provide multiple intents for a single shortcut so that the last defined activity is launched
+ * with the other activities in the <a href="/guide/components/tasks-and-back-stack.html">back stack</a>.
+ * See {@link android.app.TaskStackBuilder} for details.
* </dd>
- * <dt>categories</dt>
+ * <dt>{@code categories}</dt>
* <dd>Specify shortcut categories. Currently only
* {@link ShortcutInfo#SHORTCUT_CATEGORY_CONVERSATION} is defined in the framework.
* </dd>
@@ -226,64 +233,68 @@
*
* <h3>Publishing Dynamic Shortcuts</h3>
*
- * Applications can publish dynamic shortcuts with {@link #setDynamicShortcuts(List)}
+ * <p>
+ * Apps can publish dynamic shortcuts with {@link #setDynamicShortcuts(List)}
* or {@link #addDynamicShortcuts(List)}. The {@link #updateShortcuts(List)} method can also be
* used to update existing, mutable shortcuts.
* Use {@link #removeDynamicShortcuts(List)} or {@link #removeAllDynamicShortcuts()} to remove
* dynamic shortcuts.
*
- * <p>Example:
+ * <p>The following code snippet shows how to create a single dynamic shortcut:
* <pre>
- * ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
+ *ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
*
- * ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
- * .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.mysite.com/")))
- * .setShortLabel("Web site")
- * .setLongLabel("Open the web site")
- * .setIcon(Icon.createWithResource(context, R.drawable.icon_website))
- * .build();
+ *ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
+ * .setShortLabel("Web site")
+ * .setLongLabel("Open the web site")
+ * .setIcon(Icon.createWithResource(context, R.drawable.icon_website))
+ * .setIntent(new Intent(Intent.ACTION_VIEW,
+ * Uri.parse("https://www.mysite.example.com/")))
+ * .build();
*
- * shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));
+ *shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));
* </pre>
*
*
* <h3>Shortcut Intents</h3>
+ * <p>
* Dynamic shortcuts can be published with any set of {@link Intent#addFlags Intent} flags.
* Typically, {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} is specified, possibly along with other
- * flags; otherwise, if the application is already running, the application is simply brought to
+ * flags; otherwise, if the app is already running, the app is simply brought to
* the foreground, and the target activity may not appear.
*
* <p>The {@link ShortcutInfo.Builder#setIntents(Intent[])} method can be used instead of
* {@link ShortcutInfo.Builder#setIntent(Intent)} with {@link android.app.TaskStackBuilder}
* in order to launch an activity with other activities in the back stack.
* When the user selects a shortcut to load an activity with a back stack,
- * then presses the back key, a "parent" activity will be shown instead of the user being
- * navigated back to the launcher.
+ * then presses the back key, a parent activity from the same app will be shown
+ * instead of the user being navigated back to the launcher.
*
- * <p>Manifest shortcuts can also have multiple intents to achieve the same effect.
+ * <p>Static shortcuts can also have multiple intents to achieve the same effect.
* In order to associate multiple {@link Intent} objects with a shortcut, simply list multiple
* <code><intent></code> elements within a single <code><shortcut></code> element.
- * The last intent specifies what the user will see when they launch a shortcut.
+ * The last intent specifies what the user sees when they launch a shortcut.
*
- * <p>Manifest shortcuts <b>cannot</b> have custom intent flags.
- * The first intent of a manifest shortcut will always have {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+ * <p>Static shortcuts <b>cannot</b> have custom intent flags.
+ * The first intent of a static shortcut will always have {@link Intent#FLAG_ACTIVITY_NEW_TASK}
* and {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} set.
- * This means, when the application is already running, all the existing activities will be
- * destroyed when a manifest shortcut is launched.
+ * This means, when the app is already running, all the existing activities will be
+ * destroyed when a static shortcut is launched.
* If this behavior is not desirable, you can use a <em>trampoline activity</em>,
* or an invisible activity that starts another activity in {@link Activity#onCreate},
* then calls {@link Activity#finish()}.
* The first activity should include an attribute setting
- * of {@code android:taskAffinity=""} in the application's <code>AndroidManifest.xml</code>
- * file, and the intent within the manifest shortcut should point at this first activity.
+ * of {@code android:taskAffinity=""} in the app's <code>AndroidManifest.xml</code>
+ * file, and the intent within the static shortcut should point at this first activity.
*
*
* <h3>Showing New Information in a Shortcut</h3>
+ * <p>
* In order to avoid confusion, you should not use {@link #updateShortcuts(List)} to update
* a shortcut so that it contains conceptually different information.
*
- * <p>For example, a phone application may publish the most frequently called contact as a dynamic
- * shortcut. Over time, this contact may change; when it does, the application should
+ * <p>For example, a phone app may publish the most frequently called contact as a dynamic
+ * shortcut. Over time, this contact may change. When it does, the app should
* represent the changed contact with a new shortcut that contains a different ID, using either
* {@link #setDynamicShortcuts(List)} or {@link #addDynamicShortcuts(List)}, rather than updating
* the existing shortcut with {@link #updateShortcuts(List)}.
@@ -291,7 +302,7 @@
* it to reference a different contact will likely confuse the user.
*
* <p>On the other hand, when the
- * contact's information has changed, such as the name or picture, the application should
+ * contact's information has changed, such as the name or picture, the app should
* use {@link #updateShortcuts(List)} so that the pinned shortcut is updated too.
*
*
@@ -299,21 +310,21 @@
* When the launcher displays the shortcuts that are associated with a particular launcher icon,
* the shortcuts should appear in the following order:
* <ul>
- * <li>First show manifest shortcuts
+ * <li>First show static shortcuts
* (if {@link ShortcutInfo#isDeclaredInManifest()} is {@code true}),
* and then show dynamic shortcuts (if {@link ShortcutInfo#isDynamic()} is {@code true}).
- * <li>Within each category of shortcuts (manifest and dynamic), sort the shortcuts in order
+ * <li>Within each category of shortcuts (static and dynamic), sort the shortcuts in order
* of increasing rank according to {@link ShortcutInfo#getRank()}.
* </ul>
- * <p>Shortcut ranks are non-negative sequential integers
+ * <p>Shortcut ranks are non-negative, sequential integers
* that determine the order in which shortcuts appear, assuming that the shortcuts are all in
* the same category.
* Ranks of existing shortcuts can be updated with
- * {@link #updateShortcuts(List)}; you can use {@link #addDynamicShortcuts(List)} and
- * {@link #setDynamicShortcuts(List)}, too.
+ * {@link #updateShortcuts(List)}. You can also use {@link #addDynamicShortcuts(List)} and
+ * {@link #setDynamicShortcuts(List)}.
*
* <p>Ranks are auto-adjusted so that they're unique for each target activity in each category
- * (dynamic or manifest). For example, if there are 3 dynamic shortcuts with ranks 0, 1 and 2,
+ * (static or dynamic). For example, if there are 3 dynamic shortcuts with ranks 0, 1 and 2,
* adding another dynamic shortcut with a rank of 1 represents a request to place this shortcut at
* the second position.
* In response, the third and fourth shortcuts move closer to the bottom of the shortcut list,
@@ -321,119 +332,120 @@
*
* <h3>Rate Limiting</h3>
*
+ * <p>
* Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)}, and
- * {@link #updateShortcuts(List)} may be rate-limited when called by background applications, or
- * applications with no foreground activity or service. When you attempt to call these methods
- * from a background application after exceeding the rate limit, these APIs return {@code false}.
+ * {@link #updateShortcuts(List)} may be rate-limited when called by <em>background apps</em>, or
+ * apps with no foreground activity or service. When you attempt to call these methods
+ * from a background app after exceeding the rate limit, these APIs return {@code false}.
*
- * <p>Applications with a foreground activity or service are not rate-limited.
+ * <p>Apps with a foreground activity or service are not rate-limited.
*
- * <p>Rate-limiting will be reset upon certain events, so that even background applications
- * can call these APIs again until the rate limit is reached again.
+ * <p>Rate-limiting is reset upon certain events, so that even background apps
+ * can call these APIs until the rate limit is reached again.
* These events include the following:
* <ul>
- * <li>When an application comes to the foreground.
- * <li>When the system locale changes.
- * <li>When the user performs an "inline reply" action on a notification.
+ * <li>An app comes to the foreground.
+ * <li>The system locale changes.
+ * <li>The user performs the <strong>inline reply</strong> action on a notification.
* </ul>
*
* <p>When rate-limiting is active, {@link #isRateLimitingActive()} returns {@code true}.
*
* <h4>Resetting rate-limiting for testing</h4>
*
- * If your application is rate-limited during development or testing, you can use the
- * "Reset ShortcutManager rate-limiting" development option or the following adb command to reset
- * it:
- * <pre>
- * adb shell cmd shortcut reset-throttling [ --user USER-ID ]
+ * <p>
+ * If your app is rate-limited during development or testing, you can use the
+ * <strong>Reset ShortcutManager rate-limiting</strong> development option or
+ * the following {@code adb} command to reset it:
+ * <pre class="no-pretty-print">
+ *$ adb shell cmd shortcut reset-throttling [ --user USER-ID ]
* </pre>
*
* <h3>Handling System Locale Changes</h3>
*
- * Applications should update dynamic and pinned shortcuts when the system locale changes
+ * <p>
+ * Apps should update dynamic and pinned shortcuts when the system locale changes
* using the {@link Intent#ACTION_LOCALE_CHANGED} broadcast.
*
- * <p>When the system locale changes, rate-limiting is reset, so even background applications
- * can set dynamic shortcuts, add dynamic shortcuts, and update shortcuts until the rate limit
- * is reached again.
+ * <p>When the system locale changes, rate-limiting is reset, so even background apps
+ * can add and update dynamic shortcuts until the rate limit is reached again.
*
*
* <h3>Backup and Restore</h3>
*
- * When an application has the {@code android:allowBackup="true"} attribute assignment included
+ * <p>
+ * When an app has the {@code android:allowBackup="true"} attribute assignment included
* in its <code>AndroidManifest.xml</code> file, pinned shortcuts are
* backed up automatically and are restored when the user sets up a new device.
*
- * <h4>Categories of Shortcuts that are Backed Up</h4>
+ * <h4>Categories of shortcuts that are backed up</h4>
*
* <ul>
* <li>Pinned shortcuts are backed up. Bitmap icons are not backed up by the system,
- * but launcher applications should back them up and restore them so that the user still sees icons
- * for pinned shortcuts on the launcher. Applications can always use
+ * so launcher apps should back them up and restore them so that the user still sees icons
+ * for pinned shortcuts on the launcher. Apps can always use
* {@link #updateShortcuts(List)} to re-publish icons.
*
- * <li>Manifest shortcuts are not backed up, but when an application is re-installed on a new
- * device, they are re-published from the <code>AndroidManifest.xml</code> file, anyway.
+ * <li>Static shortcuts aren't backed up, but when an app is re-installed on a new
+ * device, they are re-published from the <code>AndroidManifest.xml</code> file.
*
- * <li>Dynamic shortcuts are <b>not</b> backed up.
+ * <li>Dynamic shortcuts <b>aren't</b> backed up.
* </ul>
*
- * <p>Because dynamic shortcuts are not restored, it is recommended that applications check
+ * <p>Because dynamic shortcuts are not restored, it is recommended that apps check
* currently-published dynamic shortcuts using {@link #getDynamicShortcuts()}
* each time they are launched, and they should re-publish
* dynamic shortcuts when necessary.
*
* <pre>
- * public class MainActivity extends Activity {
- * public void onCreate(Bundle savedInstanceState) {
- * super.onCreate(savedInstanceState);
+ *public class MainActivity extends Activity {
+ * public void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ * ShortcutManager shortcutManager =
+ * getSystemService(ShortcutManager.class);
*
- * ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
- *
- * if (shortcutManager.getDynamicShortcuts().size() == 0) {
- * // Application restored; re-publish dynamic shortcuts.
- *
- * if (shortcutManager.getPinnedShortcuts().size() > 0) {
- * // Pinned shortcuts have been restored. Use updateShortcuts() to make sure
- * // they have up-to-date information.
- * }
- * }
- * }
- * :
- *
- * }
+ * if (shortcutManager.getDynamicShortcuts().size() == 0) {
+ * // Application restored. Need to re-publish dynamic shortcuts.
+ * if (shortcutManager.getPinnedShortcuts().size() > 0) {
+ * // Pinned shortcuts have been restored. Use
+ * // updateShortcuts() to make sure they contain
+ * // up-to-date information.
+ * }
+ * }
+ * }
+ * // ...
+ *}
* </pre>
*
*
* <h4>Backup/restore and shortcut IDs</h4>
- *
- * Because pinned shortcuts are backed up and restored on new devices, shortcut IDs should be
- * meaningful across devices; that is, IDs should contain either stable, constant strings
- * or server-side identifiers,
+ * <p>
+ * Because pinned shortcuts are backed up and restored on new devices, shortcut IDs
+ * should contain either stable, constant strings or server-side identifiers,
* rather than identifiers generated locally that might not make sense on other devices.
*
*
* <h3>Report Shortcut Usage and Prediction</h3>
- *
- * Launcher applications may be capable of predicting which shortcuts will most likely be
+ * <p>
+ * Launcher apps may be capable of predicting which shortcuts will most likely be
* used at a given time by examining the shortcut usage history data.
*
- * <p>In order to provide launchers with such data, publisher applications should
+ * <p>In order to provide launchers with such data, publisher apps should
* report the shortcuts that are used with {@link #reportShortcutUsed(String)}
* when a shortcut is selected,
* <b>or when an action equivalent to a shortcut is taken by the user even if it wasn't started
* with the shortcut</b>.
*
- * <p>For example, suppose a GPS navigation application supports "navigate to work" as a shortcut.
+ * <p>For example, suppose a navigation app supports "navigate to work" as a shortcut.
* It should then report when the user selects this shortcut <b>and</b> when the user chooses
- * to navigate to work within the application itself.
- * This helps the launcher application
+ * to navigate to work within the app itself.
+ * This helps the launcher app
* learn that the user wants to navigate to work at a certain time every
* weekday, and it can then show this shortcut in a suggestion list at the right time.
*
* <h3>Launcher API</h3>
*
- * The {@link LauncherApps} class provides APIs for launcher applications to access shortcuts.
+ * The {@link LauncherApps} class provides APIs for launcher apps to access shortcuts.
*
*
* <h3>Direct Boot and Shortcuts</h3>
@@ -465,7 +477,7 @@
}
/**
- * Publish the list of shortcuts. All existing dynamic shortcuts from the caller application
+ * Publish the list of shortcuts. All existing dynamic shortcuts from the caller app
* will be replaced. If there are already pinned shortcuts with the same IDs,
* the mutable pinned shortcuts are updated.
*
@@ -488,7 +500,7 @@
}
/**
- * Return all dynamic shortcuts from the caller application.
+ * Return all dynamic shortcuts from the caller app.
*
* @throws IllegalStateException when the user is locked.
*/
@@ -503,7 +515,7 @@
}
/**
- * Return all manifest shortcuts from the caller application.
+ * Return all static (manifest) shortcuts from the caller app.
*
* @throws IllegalStateException when the user is locked.
*/
@@ -554,7 +566,7 @@
}
/**
- * Delete all dynamic shortcuts from the caller application.
+ * Delete all dynamic shortcuts from the caller app.
*
* @throws IllegalStateException when the user is locked.
*/
@@ -567,7 +579,7 @@
}
/**
- * Return all pinned shortcuts from the caller application.
+ * Return all pinned shortcuts from the caller app.
*
* @throws IllegalStateException when the user is locked.
*/
@@ -661,7 +673,7 @@
/**
* Re-enable pinned shortcuts that were previously disabled. If the target shortcuts
- * already enabled, this method does nothing.
+ * are already enabled, this method does nothing.
*
* @throws IllegalArgumentException If trying to enable immutable shortcuts.
*
@@ -684,7 +696,7 @@
}
/**
- * Return the maximum number of dynamic and manifest shortcuts that each launcher icon
+ * Return the maximum number of static and dynamic shortcuts that each launcher icon
* can have at a time.
*/
public int getMaxShortcutCountPerActivity() {
@@ -697,7 +709,7 @@
}
/**
- * Return the number of times the caller application can call the rate-limited APIs
+ * Return the number of times the caller app can call the rate-limited APIs
* before the rate limit counter is reset.
*
* @see #getRateLimitResetTime()
@@ -729,7 +741,7 @@
}
/**
- * Return {@code true} when rate-limiting is active for the caller application.
+ * Return {@code true} when rate-limiting is active for the caller app.
*
* <p>See the class level javadoc for details.
*
@@ -769,13 +781,13 @@
}
/**
- * Applications that publish shortcuts should call this method
- * whenever the user selects the shortcut containing the given ID or when the user completes
- * an action in the application that is equivalent to selecting the shortcut.
+ * Apps that publish shortcuts should call this method whenever the user
+ * selects the shortcut containing the given ID or when the user completes
+ * an action in the app that is equivalent to selecting the shortcut.
* For more details, see the Javadoc for the {@link ShortcutManager} class
*
* <p>The information is accessible via {@link UsageStatsManager#queryEvents}
- * Typically, launcher applications use this information to build a prediction model
+ * Typically, launcher apps use this information to build a prediction model
* so that they can promote the shortcuts that are likely to be used at the moment.
*
* @throws IllegalStateException when the user is locked.
@@ -790,9 +802,9 @@
}
/**
- * Called internally when an application is considered to have come to foreground
+ * Called internally when an app is considered to have come to the foreground
* even when technically it's not. This method resets the throttling for this package.
- * For example, when the user sends an "inline reply" on an notification, the system UI will
+ * For example, when the user sends an "inline reply" on a notification, the system UI will
* call it.
*
* @hide
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 1d85493..62b7f32 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -722,6 +722,36 @@
}
/**
+ * This method is called when camera device's input capture queue becomes empty,
+ * and is ready to accept the next request.
+ *
+ * <p>Pending capture requests exist in one of two queues: the in-flight queue where requests
+ * are already in different stages of processing pipeline, and an input queue where requests
+ * wait to enter the in-flight queue. The input queue is needed because more requests may be
+ * submitted than the current camera device pipeline depth.</p>
+ *
+ * <p>This callback is fired when the input queue becomes empty, and the camera device may
+ * have to fall back to the repeating request if set, or completely skip the next frame from
+ * the sensor. This can cause glitches to camera preview output, for example. This callback
+ * will only fire after requests queued by capture() or captureBurst(), not after a
+ * repeating request or burst enters the in-flight queue. For example, in the common case
+ * of a repeating request and a single-shot JPEG capture, this callback only fires when the
+ * JPEG request has entered the in-flight queue for capture.</p>
+ *
+ * <p>By only sending a new {@link #capture} or {@link #captureBurst} when the input
+ * queue is empty, pipeline latency can be minimized.</p>
+ *
+ * <p>This callback is not fired when the session is first created. It is different from
+ * {@link #onReady}, which is fired when all requests in both queues have been processed.</p>
+ *
+ * @param session
+ * The session returned by {@link CameraDevice#createCaptureSession}
+ */
+ public void onCaptureQueueEmpty(@NonNull CameraCaptureSession session) {
+ // default empty implementation
+ }
+
+ /**
* This method is called when the session is closed.
*
* <p>A session is closed when a new session is created by the parent camera device,
diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java
index dac2ef8..e6e448e 100644
--- a/core/java/android/hardware/camera2/impl/CallbackProxies.java
+++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java
@@ -173,6 +173,11 @@
}
@Override
+ public void onCaptureQueueEmpty(CameraCaptureSession session) {
+ mProxy.invoke("onCaptureQueueEmpty", session);
+ }
+
+ @Override
public void onClosed(CameraCaptureSession session) {
mProxy.invoke("onClosed", session);
}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index b10c341..5e9fd66 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -604,11 +604,16 @@
}
@Override
- public void onSurfacePrepared(Surface surface) {
- if (DEBUG) Log.v(TAG, mIdString + "onPrepared");
- mStateCallback.onSurfacePrepared(session, surface);
+ public void onRequestQueueEmpty() {
+ if (DEBUG) Log.v(TAG, mIdString + "onRequestQueueEmpty");
+ mStateCallback.onCaptureQueueEmpty(session);
}
+ @Override
+ public void onSurfacePrepared(Surface surface) {
+ if (DEBUG) Log.v(TAG, mIdString + "onSurfacePrepared");
+ mStateCallback.onSurfacePrepared(session, surface);
+ }
};
}
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 1c8e124..4481a74 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -291,6 +291,11 @@
}
@Override
+ public void onCaptureQueueEmpty(CameraCaptureSession session) {
+ mCallback.onCaptureQueueEmpty(CameraConstrainedHighSpeedCaptureSessionImpl.this);
+ }
+
+ @Override
public void onClosed(CameraCaptureSession session) {
mCallback.onClosed(CameraConstrainedHighSpeedCaptureSessionImpl.this);
}
@@ -300,7 +305,5 @@
mCallback.onSurfacePrepared(CameraConstrainedHighSpeedCaptureSessionImpl.this,
surface);
}
-
-
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ee8a6d7..97ca56ef 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1204,6 +1204,14 @@
}
/**
+ * This method is called when camera device's non-repeating request queue is empty,
+ * and is ready to start capturing next image.
+ */
+ public void onRequestQueueEmpty() {
+ // Default empty implementation
+ }
+
+ /**
* The method called when the camera device has finished preparing
* an output Surface
*/
@@ -1906,6 +1914,23 @@
sessionCallback.onSurfacePrepared(surface);
}
+ @Override
+ public void onRequestQueueEmpty() {
+ final StateCallbackKK sessionCallback;
+
+ if (DEBUG) {
+ Log.v(TAG, "Request queue becomes empty");
+ }
+
+ synchronized(mInterfaceLock) {
+ sessionCallback = mSessionStateCallback;
+ }
+
+ if (sessionCallback == null) return;
+
+ sessionCallback.onRequestQueueEmpty();
+ }
+
/**
* Called by onDeviceError for handling single-capture failures.
*/
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index b9e75ee..2a9bf6b 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -206,6 +206,7 @@
private static final int RESULT_RECEIVED = 3;
private static final int PREPARED = 4;
private static final int REPEATING_REQUEST_ERROR = 5;
+ private static final int REQUEST_QUEUE_EMPTY = 6;
private final HandlerThread mHandlerThread;
private Handler mHandler;
@@ -262,7 +263,6 @@
getHandler().sendMessage(msg);
}
-
@Override
public void onRepeatingRequestError(long lastFrameNumber) {
Message msg = getHandler().obtainMessage(REPEATING_REQUEST_ERROR,
@@ -272,6 +272,13 @@
}
@Override
+ public void onRequestQueueEmpty() {
+ Message msg = getHandler().obtainMessage(REQUEST_QUEUE_EMPTY,
+ /* arg1 */ 0, /* arg2 */ 0);
+ getHandler().sendMessage(msg);
+ }
+
+ @Override
public IBinder asBinder() {
// This is solely intended to be used for in-process binding.
return null;
@@ -327,6 +334,10 @@
mCallbacks.onRepeatingRequestError(lastFrameNumber);
break;
}
+ case REQUEST_QUEUE_EMPTY: {
+ mCallbacks.onRequestQueueEmpty();
+ break;
+ }
default:
throw new IllegalArgumentException(
"Unknown callback message " + msg.what);
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index e0d3905..a05a8ec 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -248,7 +248,8 @@
return program;
}
- private void drawFrame(SurfaceTexture st, int width, int height, int flipType) {
+ private void drawFrame(SurfaceTexture st, int width, int height, int flipType)
+ throws LegacyExceptionUtils.BufferQueueAbandonedException {
checkGlError("onDrawFrame start");
st.getTransformMatrix(mSTMatrix);
@@ -343,7 +344,7 @@
/*offset*/ 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /*offset*/ 0, /*count*/ 4);
- checkGlError("glDrawArrays");
+ checkGlDrawError("glDrawArrays");
}
/**
@@ -548,7 +549,29 @@
private void checkGlError(String msg) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
- throw new IllegalStateException(msg + ": GLES20 error: 0x" + Integer.toHexString(error));
+ throw new IllegalStateException(
+ msg + ": GLES20 error: 0x" + Integer.toHexString(error));
+ }
+ }
+
+ private void checkGlDrawError(String msg)
+ throws LegacyExceptionUtils.BufferQueueAbandonedException {
+ int error;
+ boolean surfaceAbandoned = false;
+ boolean glError = false;
+ while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+ if (error == GLES20.GL_OUT_OF_MEMORY) {
+ surfaceAbandoned = true;
+ } else {
+ glError = true;
+ }
+ }
+ if (glError) {
+ throw new IllegalStateException(
+ msg + ": GLES20 error: 0x" + Integer.toHexString(error));
+ }
+ if (surfaceAbandoned) {
+ throw new LegacyExceptionUtils.BufferQueueAbandonedException();
}
}
@@ -759,9 +782,14 @@
if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
makeCurrent(holder.eglSurface);
// glReadPixels reads from the bottom of the buffer, so add an extra vertical flip
- drawFrame(mSurfaceTexture, holder.width, holder.height,
- (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
- FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL);
+ try {
+ drawFrame(mSurfaceTexture, holder.width, holder.height,
+ (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
+ FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL);
+ } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+ // Should never hit this.
+ throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
+ }
mPBufferPixels.clear();
GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPBufferPixels);
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 160e261..ca2b1f8 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -21,6 +21,9 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.os.ParcelFileDescriptor;
+
+import com.android.internal.util.Preconditions;
+
import dalvik.system.CloseGuard;
import java.io.FileDescriptor;
@@ -273,7 +276,40 @@
* {@link UsbRequest#queue(ByteBuffer, int)}
*/
public UsbRequest requestWait() {
- UsbRequest request = native_request_wait();
+ // -1 is special value indicating infinite wait
+ UsbRequest request = native_request_wait(-1);
+ if (request != null) {
+ request.dequeue();
+ }
+ return request;
+ }
+
+ /**
+ * Waits for the result of a {@link android.hardware.usb.UsbRequest#queue} operation
+ * <p>Note that this may return requests queued on multiple
+ * {@link android.hardware.usb.UsbEndpoint}s. When multiple endpoints are in use,
+ * {@link android.hardware.usb.UsbRequest#getEndpoint} and {@link
+ * android.hardware.usb.UsbRequest#getClientData} can be useful in determining how to process
+ * the result of this function.</p>
+ * <p>Position and array offset of the request's buffer are ignored and assumed to be 0. The
+ * position will be set to the number of bytes read/written.</p>
+ * <p>Android processes {@link UsbRequest UsbRequests} asynchronously. Hence it is not
+ * guaranteed that {@link #requestWait(int) requestWait(0)} returns a request that has been
+ * queued right before even if the request could have been processed immediately.</p>
+ *
+ * @param timeout timeout in milliseconds. If 0 this method does not wait.
+ *
+ * @return a completed USB request, or {@code null} if an error or time out occurred
+ *
+ * @throws IllegalArgumentException if the number of bytes read or written is more than the
+ * limit of the request's buffer. The number of bytes is
+ * determined by the {@code length} parameter of
+ * {@link UsbRequest#queue(ByteBuffer, int)}
+ */
+ public UsbRequest requestWait(int timeout) {
+ timeout = Preconditions.checkArgumentNonnegative(timeout);
+
+ UsbRequest request = native_request_wait(timeout);
if (request != null) {
request.dequeue();
}
@@ -318,7 +354,7 @@
int index, byte[] buffer, int offset, int length, int timeout);
private native int native_bulk_request(int endpoint, byte[] buffer,
int offset, int length, int timeout);
- private native UsbRequest native_request_wait();
+ private native UsbRequest native_request_wait(int timeout);
private native String native_get_serial();
private native boolean native_reset_device();
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9e5aaf5..0073dde 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2679,6 +2679,7 @@
public static final int CALLBACK_IP_CHANGED = BASE + 7;
/** @hide */
public static final int CALLBACK_RELEASED = BASE + 8;
+ // TODO: consider deleting CALLBACK_EXIT and shifting following enum codes down by 1.
/** @hide */
public static final int CALLBACK_EXIT = BASE + 9;
/** @hide obj = NetworkCapabilities, arg1 = seq number */
@@ -2709,24 +2710,17 @@
}
private class CallbackHandler extends Handler {
- private final HashMap<NetworkRequest, NetworkCallback>mCallbackMap;
- private final AtomicInteger mRefCount;
private static final String TAG = "ConnectivityManager.CallbackHandler";
- private final ConnectivityManager mCm;
private static final boolean DBG = false;
- CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallback>callbackMap,
- AtomicInteger refCount, ConnectivityManager cm) {
+ CallbackHandler(Looper looper) {
super(looper);
- mCallbackMap = callbackMap;
- mRefCount = refCount;
- mCm = cm;
}
@Override
public void handleMessage(Message message) {
- NetworkRequest request = (NetworkRequest) getObject(message, NetworkRequest.class);
- Network network = (Network) getObject(message, Network.class);
+ NetworkRequest request = getObject(message, NetworkRequest.class);
+ Network network = getObject(message, Network.class);
if (DBG) {
Log.d(TAG, whatToString(message.what) + " for network " + network);
}
@@ -2769,9 +2763,7 @@
case CALLBACK_CAP_CHANGED: {
NetworkCallback callback = getCallback(request, "CAP_CHANGED");
if (callback != null) {
- NetworkCapabilities cap = (NetworkCapabilities)getObject(message,
- NetworkCapabilities.class);
-
+ NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
callback.onCapabilitiesChanged(network, cap);
}
break;
@@ -2779,9 +2771,7 @@
case CALLBACK_IP_CHANGED: {
NetworkCallback callback = getCallback(request, "IP_CHANGED");
if (callback != null) {
- LinkProperties lp = (LinkProperties)getObject(message,
- LinkProperties.class);
-
+ LinkProperties lp = getObject(message, LinkProperties.class);
callback.onLinkPropertiesChanged(network, lp);
}
break;
@@ -2801,24 +2791,16 @@
break;
}
case CALLBACK_RELEASED: {
- NetworkCallback callback = null;
- synchronized(mCallbackMap) {
- callback = mCallbackMap.remove(request);
+ final NetworkCallback callback;
+ synchronized(sCallbacks) {
+ callback = sCallbacks.remove(request);
}
- if (callback != null) {
- synchronized(mRefCount) {
- if (mRefCount.decrementAndGet() == 0) {
- getLooper().quit();
- }
- }
- } else {
+ if (callback == null) {
Log.e(TAG, "callback not found for RELEASED message");
}
break;
}
case CALLBACK_EXIT: {
- Log.d(TAG, "Listener quitting");
- getLooper().quit();
break;
}
case EXPIRE_LEGACY_REQUEST: {
@@ -2828,14 +2810,14 @@
}
}
- private Object getObject(Message msg, Class c) {
- return msg.getData().getParcelable(c.getSimpleName());
+ private <T> T getObject(Message msg, Class<T> c) {
+ return (T) msg.getData().getParcelable(c.getSimpleName());
}
private NetworkCallback getCallback(NetworkRequest req, String name) {
NetworkCallback callback;
- synchronized(mCallbackMap) {
- callback = mCallbackMap.get(req);
+ synchronized(sCallbacks) {
+ callback = sCallbacks.get(req);
}
if (callback == null) {
Log.e(TAG, "callback not found for " + name + " message");
@@ -2844,63 +2826,56 @@
}
}
- private void incCallbackHandlerRefCount() {
- synchronized(sCallbackRefCount) {
- if (sCallbackRefCount.incrementAndGet() == 1) {
- // TODO: switch this to ConnectivityThread
- HandlerThread callbackThread = new HandlerThread("ConnectivityManager");
- callbackThread.start();
- sCallbackHandler = new CallbackHandler(callbackThread.getLooper(),
- sNetworkCallback, sCallbackRefCount, this);
+ private CallbackHandler getHandler() {
+ synchronized (sCallbacks) {
+ if (sCallbackHandler == null) {
+ sCallbackHandler = new CallbackHandler(ConnectivityThread.getInstanceLooper());
}
+ return sCallbackHandler;
}
}
- private void decCallbackHandlerRefCount() {
- synchronized(sCallbackRefCount) {
- if (sCallbackRefCount.decrementAndGet() == 0) {
- sCallbackHandler.obtainMessage(CALLBACK_EXIT).sendToTarget();
- sCallbackHandler = null;
- }
- }
- }
-
- static final HashMap<NetworkRequest, NetworkCallback> sNetworkCallback =
- new HashMap<NetworkRequest, NetworkCallback>();
- static final AtomicInteger sCallbackRefCount = new AtomicInteger(0);
- static CallbackHandler sCallbackHandler = null;
+ static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>();
+ static CallbackHandler sCallbackHandler;
private final static int LISTEN = 1;
private final static int REQUEST = 2;
private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
- NetworkCallback networkCallback, int timeoutMs, int action,
- int legacyType) {
- if (networkCallback == null) {
+ NetworkCallback callback, int timeoutMs, int action, int legacyType) {
+ return sendRequestForNetwork(need, callback, getHandler(), timeoutMs, action, legacyType);
+ }
+
+ private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
+ NetworkCallback callback, Handler handler, int timeoutMs, int action, int legacyType) {
+ if (callback == null) {
throw new IllegalArgumentException("null NetworkCallback");
}
if (need == null && action != REQUEST) {
throw new IllegalArgumentException("null NetworkCapabilities");
}
+ // TODO: throw an exception if callback.networkRequest is not null.
+ // http://b/20701525
+ final NetworkRequest request;
try {
- incCallbackHandlerRefCount();
- synchronized(sNetworkCallback) {
+ synchronized(sCallbacks) {
+ Messenger messenger = new Messenger(handler);
+ Binder binder = new Binder();
if (action == LISTEN) {
- networkCallback.networkRequest = mService.listenForNetwork(need,
- new Messenger(sCallbackHandler), new Binder());
+ request = mService.listenForNetwork(need, messenger, binder);
} else {
- networkCallback.networkRequest = mService.requestNetwork(need,
- new Messenger(sCallbackHandler), timeoutMs, new Binder(), legacyType);
+ request = mService.requestNetwork(
+ need, messenger, timeoutMs, binder, legacyType);
}
- if (networkCallback.networkRequest != null) {
- sNetworkCallback.put(networkCallback.networkRequest, networkCallback);
+ if (request != null) {
+ sCallbacks.put(request, callback);
}
+ callback.networkRequest = request;
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- if (networkCallback.networkRequest == null) decCallbackHandlerRefCount();
- return networkCallback.networkRequest;
+ return request;
}
/**
diff --git a/core/java/android/net/ConnectivityThread.java b/core/java/android/net/ConnectivityThread.java
index 55c3402..0b218e7 100644
--- a/core/java/android/net/ConnectivityThread.java
+++ b/core/java/android/net/ConnectivityThread.java
@@ -27,25 +27,30 @@
* @hide
*/
public final class ConnectivityThread extends HandlerThread {
- private static ConnectivityThread sInstance;
+
+ // A class implementing the lazy holder idiom: the unique static instance
+ // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by
+ // the language specs) the first time that Singleton is referenced in get()
+ // or getInstanceLooper().
+ private static class Singleton {
+ private static final ConnectivityThread INSTANCE = createInstance();
+ }
private ConnectivityThread() {
super("ConnectivityThread");
}
- private static synchronized ConnectivityThread getInstance() {
- if (sInstance == null) {
- sInstance = new ConnectivityThread();
- sInstance.start();
- }
- return sInstance;
+ private static ConnectivityThread createInstance() {
+ ConnectivityThread t = new ConnectivityThread();
+ t.start();
+ return t;
}
public static ConnectivityThread get() {
- return getInstance();
+ return Singleton.INSTANCE;
}
public static Looper getInstanceLooper() {
- return getInstance().getLooper();
+ return Singleton.INSTANCE.getLooper();
}
}
diff --git a/core/java/android/net/IIpConnectivityMetrics.aidl b/core/java/android/net/IIpConnectivityMetrics.aidl
index 8f634bb..d36b766 100644
--- a/core/java/android/net/IIpConnectivityMetrics.aidl
+++ b/core/java/android/net/IIpConnectivityMetrics.aidl
@@ -23,7 +23,8 @@
interface IIpConnectivityMetrics {
/**
- * @return number of remaining available slots in buffer.
+ * @return the number of remaining available slots in buffer,
+ * or -1 if the event was dropped due to rate limiting.
*/
int logEvent(in ConnectivityMetricsEvent event);
}
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index eace8b2..34cde08 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -113,7 +113,7 @@
*/
public boolean isActive() {
try {
- if (hasFlag(FLAG_UP)) {
+ if (isUp()) {
for (byte b : mAddr.getAddress().getAddress()) {
if (b != 0) return true;
}
diff --git a/core/java/android/os/Debug.aidl b/core/java/android/os/Debug.aidl
new file mode 100644
index 0000000..81a2c1f
--- /dev/null
+++ b/core/java/android/os/Debug.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable Debug.MemoryInfo;
\ No newline at end of file
diff --git a/core/java/android/os/HardwarePropertiesManager.java b/core/java/android/os/HardwarePropertiesManager.java
index 9d362d6..6dec0dc 100644
--- a/core/java/android/os/HardwarePropertiesManager.java
+++ b/core/java/android/os/HardwarePropertiesManager.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.Context;
+import android.hardware.thermal.V1_0.Constants;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -56,18 +57,19 @@
/**
* Device temperature types. These must match the values in
* frameworks/native/include/hardwareproperties/HardwarePropertiesManager.h
+ * TODO(b/32022261) Remove this comment.
*/
/** Temperature of CPUs in Celsius. */
- public static final int DEVICE_TEMPERATURE_CPU = 0;
+ public static final int DEVICE_TEMPERATURE_CPU = Constants.TemperatureType.CPU;
/** Temperature of GPUs in Celsius. */
- public static final int DEVICE_TEMPERATURE_GPU = 1;
+ public static final int DEVICE_TEMPERATURE_GPU = Constants.TemperatureType.GPU;
/** Temperature of battery in Celsius. */
- public static final int DEVICE_TEMPERATURE_BATTERY = 2;
+ public static final int DEVICE_TEMPERATURE_BATTERY = Constants.TemperatureType.BATTERY;
/** Temperature of device skin in Celsius. */
- public static final int DEVICE_TEMPERATURE_SKIN = 3;
+ public static final int DEVICE_TEMPERATURE_SKIN = Constants.TemperatureType.SKIN;
/** Get current temperature. */
public static final int TEMPERATURE_CURRENT = 0;
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 5ff79f7..0e7da63 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -38,8 +38,11 @@
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);
+ public native final void registerService(
+ String serviceName, int versionMajor, int versionMinor);
+
+ public static native final IHwBinder getService(
+ String serviceName, int versionMajor, int versionMinor);
// Returns address of the "freeFunction".
private static native final long native_init();
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index 180e8f4..c7612d1 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -16,6 +16,9 @@
package android.os;
+import java.util.ArrayList;
+import java.util.Arrays;
+
import libcore.util.NativeAllocationRegistry;
/** @hide */
@@ -53,14 +56,88 @@
public native final void writeDouble(double val);
public native final void writeString(String val);
- public native final void writeBoolVector(boolean[] val);
- public native final void writeInt8Vector(byte[] val);
- public native final void writeInt16Vector(short[] val);
- public native final void writeInt32Vector(int[] val);
- public native final void writeInt64Vector(long[] val);
- public native final void writeFloatVector(float[] val);
- public native final void writeDoubleVector(double[] val);
- public native final void writeStringVector(String[] val);
+ private native final void writeBoolVector(boolean[] val);
+ private native final void writeInt8Vector(byte[] val);
+ private native final void writeInt16Vector(short[] val);
+ private native final void writeInt32Vector(int[] val);
+ private native final void writeInt64Vector(long[] val);
+ private native final void writeFloatVector(float[] val);
+ private native final void writeDoubleVector(double[] val);
+ private native final void writeStringVector(String[] val);
+
+ public final void writeBoolVector(ArrayList<Boolean> val) {
+ final int n = val.size();
+ boolean[] array = new boolean[n];
+ for (int i = 0; i < n; ++i) {
+ array[i] = val.get(i);
+ }
+
+ writeBoolVector(array);
+ }
+
+ public final void writeInt8Vector(ArrayList<Byte> val) {
+ final int n = val.size();
+ byte[] array = new byte[n];
+ for (int i = 0; i < n; ++i) {
+ array[i] = val.get(i);
+ }
+
+ writeInt8Vector(array);
+ }
+
+ public final void writeInt16Vector(ArrayList<Short> val) {
+ final int n = val.size();
+ short[] array = new short[n];
+ for (int i = 0; i < n; ++i) {
+ array[i] = val.get(i);
+ }
+
+ writeInt16Vector(array);
+ }
+
+ public final void writeInt32Vector(ArrayList<Integer> val) {
+ final int n = val.size();
+ int[] array = new int[n];
+ for (int i = 0; i < n; ++i) {
+ array[i] = val.get(i);
+ }
+
+ writeInt32Vector(array);
+ }
+
+ public final void writeInt64Vector(ArrayList<Long> val) {
+ final int n = val.size();
+ long[] array = new long[n];
+ for (int i = 0; i < n; ++i) {
+ array[i] = val.get(i);
+ }
+
+ writeInt64Vector(array);
+ }
+
+ public final void writeFloatVector(ArrayList<Float> val) {
+ final int n = val.size();
+ float[] array = new float[n];
+ for (int i = 0; i < n; ++i) {
+ array[i] = val.get(i);
+ }
+
+ writeFloatVector(array);
+ }
+
+ public final void writeDoubleVector(ArrayList<Double> val) {
+ final int n = val.size();
+ double[] array = new double[n];
+ for (int i = 0; i < n; ++i) {
+ array[i] = val.get(i);
+ }
+
+ writeDoubleVector(array);
+ }
+
+ public final void writeStringVector(ArrayList<String> val) {
+ writeStringVector(val.toArray(new String[val.size()]));
+ }
public native final void writeStrongBinder(IHwBinder binder);
@@ -74,14 +151,60 @@
public native final double readDouble();
public native final String readString();
- public native final boolean[] readBoolVector();
- public native final byte[] readInt8Vector();
- public native final short[] readInt16Vector();
- public native final int[] readInt32Vector();
- public native final long[] readInt64Vector();
- public native final float[] readFloatVector();
- public native final double[] readDoubleVector();
- public native final String[] readStringVector();
+ private native final boolean[] readBoolVectorAsArray();
+ private native final byte[] readInt8VectorAsArray();
+ private native final short[] readInt16VectorAsArray();
+ private native final int[] readInt32VectorAsArray();
+ private native final long[] readInt64VectorAsArray();
+ private native final float[] readFloatVectorAsArray();
+ private native final double[] readDoubleVectorAsArray();
+ private native final String[] readStringVectorAsArray();
+
+ public final ArrayList<Boolean> readBoolVector() {
+ Boolean[] array = HwBlob.wrapArray(readBoolVectorAsArray());
+
+ return new ArrayList<Boolean>(Arrays.asList(array));
+ }
+
+ public final ArrayList<Byte> readInt8Vector() {
+ Byte[] array = HwBlob.wrapArray(readInt8VectorAsArray());
+
+ return new ArrayList<Byte>(Arrays.asList(array));
+ }
+
+ public final ArrayList<Short> readInt16Vector() {
+ Short[] array = HwBlob.wrapArray(readInt16VectorAsArray());
+
+ return new ArrayList<Short>(Arrays.asList(array));
+ }
+
+ public final ArrayList<Integer> readInt32Vector() {
+ Integer[] array = HwBlob.wrapArray(readInt32VectorAsArray());
+
+ return new ArrayList<Integer>(Arrays.asList(array));
+ }
+
+ public final ArrayList<Long> readInt64Vector() {
+ Long[] array = HwBlob.wrapArray(readInt64VectorAsArray());
+
+ return new ArrayList<Long>(Arrays.asList(array));
+ }
+
+ public final ArrayList<Float> readFloatVector() {
+ Float[] array = HwBlob.wrapArray(readFloatVectorAsArray());
+
+ return new ArrayList<Float>(Arrays.asList(array));
+ }
+
+ public final ArrayList<Double> readDoubleVector() {
+ Double[] array = HwBlob.wrapArray(readDoubleVectorAsArray());
+
+ return new ArrayList<Double>(Arrays.asList(array));
+ }
+
+ public final ArrayList<String> readStringVector() {
+ return new ArrayList<String>(Arrays.asList(readStringVectorAsArray()));
+ }
public native final IHwBinder readStrongBinder();
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index f9dee92..4eee854 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -20,6 +20,7 @@
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
+import android.webkit.WebViewZygote;
import dalvik.system.VMRuntime;
/**
@@ -133,6 +134,12 @@
public static final int CAMERASERVER_UID = 1047;
/**
+ * Defines the UID/GID for the WebView zygote process.
+ * @hide
+ */
+ public static final int WEBVIEW_ZYGOTE_UID = 1051;
+
+ /**
* Defines the start of a range of UIDs (and GIDs), going from this
* number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
* to applications.
@@ -425,6 +432,22 @@
abi, instructionSet, appDataDir, zygoteArgs);
}
+ /** @hide */
+ public static final ProcessStartResult startWebView(final String processClass,
+ final String niceName,
+ int uid, int gid, int[] gids,
+ int debugFlags, int mountExternal,
+ int targetSdkVersion,
+ String seInfo,
+ String abi,
+ String instructionSet,
+ String appDataDir,
+ String[] zygoteArgs) {
+ return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
+ debugFlags, mountExternal, targetSdkVersion, seInfo,
+ abi, instructionSet, appDataDir, zygoteArgs);
+ }
+
/**
* Returns elapsed milliseconds of the time this process has run.
* @return Returns the number of milliseconds this process has return.
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index dbb9650..e4a12e8 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -302,7 +302,7 @@
/**
* Implement parsing and execution of a command. If it isn't a command you understand,
* call {@link #handleDefaultCommands(String)} and return its result as a last resort.
- * User {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()}
+ * Use {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()}
* to process additional command line arguments. Command output can be written to
* {@link #getOutPrintWriter()} and errors to {@link #getErrPrintWriter()}.
*
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index f050d76..c45fe5a 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -19,7 +19,9 @@
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.Zygote;
+import com.android.internal.util.Preconditions;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.IOException;
@@ -110,7 +112,8 @@
}
String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
- Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
+ Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: "
+ + abiListString);
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
@@ -136,6 +139,13 @@
}
/**
+ * Lock object to protect access to the two ZygoteStates below. This lock must be
+ * acquired while communicating over the ZygoteState's socket, to prevent
+ * interleaved access.
+ */
+ private final Object mLock = new Object();
+
+ /**
* The state of the connection to the primary zygote.
*/
private ZygoteState primaryZygoteState;
@@ -207,6 +217,7 @@
*
* @throws ZygoteStartFailedEx if the query failed.
*/
+ @GuardedBy("mLock")
private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
throws IOException {
// Each query starts with the argument count (1 in this case)
@@ -233,6 +244,7 @@
*
* @throws ZygoteStartFailedEx if process start failed for any reason
*/
+ @GuardedBy("mLock")
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList<String> args)
throws ZygoteStartFailedEx {
@@ -320,90 +332,90 @@
String appDataDir,
String[] extraArgs)
throws ZygoteStartFailedEx {
- synchronized(Process.class) {
- ArrayList<String> argsForZygote = new ArrayList<String>();
+ ArrayList<String> argsForZygote = new ArrayList<String>();
- // --runtime-args, --setuid=, --setgid=,
- // and --setgroups= must go first
- argsForZygote.add("--runtime-args");
- argsForZygote.add("--setuid=" + uid);
- argsForZygote.add("--setgid=" + gid);
- if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
- argsForZygote.add("--enable-jni-logging");
- }
- if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
- argsForZygote.add("--enable-safemode");
- }
- if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
- argsForZygote.add("--enable-debugger");
- }
- if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
- argsForZygote.add("--enable-checkjni");
- }
- if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
- argsForZygote.add("--generate-debug-info");
- }
- if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
- argsForZygote.add("--always-jit");
- }
- if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
- argsForZygote.add("--native-debuggable");
- }
- if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
- argsForZygote.add("--enable-assert");
- }
- if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
- argsForZygote.add("--mount-external-default");
- } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
- argsForZygote.add("--mount-external-read");
- } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
- argsForZygote.add("--mount-external-write");
- }
- argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
+ // --runtime-args, --setuid=, --setgid=,
+ // and --setgroups= must go first
+ argsForZygote.add("--runtime-args");
+ argsForZygote.add("--setuid=" + uid);
+ argsForZygote.add("--setgid=" + gid);
+ if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
+ argsForZygote.add("--enable-jni-logging");
+ }
+ if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
+ argsForZygote.add("--enable-safemode");
+ }
+ if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
+ argsForZygote.add("--enable-debugger");
+ }
+ if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
+ argsForZygote.add("--enable-checkjni");
+ }
+ if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
+ argsForZygote.add("--generate-debug-info");
+ }
+ if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
+ argsForZygote.add("--always-jit");
+ }
+ if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
+ argsForZygote.add("--native-debuggable");
+ }
+ if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
+ argsForZygote.add("--enable-assert");
+ }
+ if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
+ argsForZygote.add("--mount-external-default");
+ } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
+ argsForZygote.add("--mount-external-read");
+ } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
+ argsForZygote.add("--mount-external-write");
+ }
+ argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
- //TODO optionally enable debuger
- //argsForZygote.add("--enable-debugger");
+ //TODO optionally enable debuger
+ //argsForZygote.add("--enable-debugger");
- // --setgroups is a comma-separated list
- if (gids != null && gids.length > 0) {
- StringBuilder sb = new StringBuilder();
- sb.append("--setgroups=");
+ // --setgroups is a comma-separated list
+ if (gids != null && gids.length > 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("--setgroups=");
- int sz = gids.length;
- for (int i = 0; i < sz; i++) {
- if (i != 0) {
- sb.append(',');
- }
- sb.append(gids[i]);
+ int sz = gids.length;
+ for (int i = 0; i < sz; i++) {
+ if (i != 0) {
+ sb.append(',');
}
-
- argsForZygote.add(sb.toString());
+ sb.append(gids[i]);
}
- if (niceName != null) {
- argsForZygote.add("--nice-name=" + niceName);
+ argsForZygote.add(sb.toString());
+ }
+
+ if (niceName != null) {
+ argsForZygote.add("--nice-name=" + niceName);
+ }
+
+ if (seInfo != null) {
+ argsForZygote.add("--seinfo=" + seInfo);
+ }
+
+ if (instructionSet != null) {
+ argsForZygote.add("--instruction-set=" + instructionSet);
+ }
+
+ if (appDataDir != null) {
+ argsForZygote.add("--app-data-dir=" + appDataDir);
+ }
+
+ argsForZygote.add(processClass);
+
+ if (extraArgs != null) {
+ for (String arg : extraArgs) {
+ argsForZygote.add(arg);
}
+ }
- if (seInfo != null) {
- argsForZygote.add("--seinfo=" + seInfo);
- }
-
- if (instructionSet != null) {
- argsForZygote.add("--instruction-set=" + instructionSet);
- }
-
- if (appDataDir != null) {
- argsForZygote.add("--app-data-dir=" + appDataDir);
- }
-
- argsForZygote.add(processClass);
-
- if (extraArgs != null) {
- for (String arg : extraArgs) {
- argsForZygote.add(arg);
- }
- }
-
+ synchronized(mLock) {
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
@@ -415,7 +427,9 @@
*/
public void establishZygoteConnectionForAbi(String abi) {
try {
- openZygoteSocketIfNeeded(abi);
+ synchronized(mLock) {
+ openZygoteSocketIfNeeded(abi);
+ }
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
}
@@ -423,9 +437,12 @@
/**
* Tries to open socket to Zygote process if not already open. If
- * already open, does nothing. May block and retry.
+ * already open, does nothing. May block and retry. Requires that mLock be held.
*/
+ @GuardedBy("mLock")
private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
+ Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
+
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
primaryZygoteState = ZygoteState.connect(mSocket);
@@ -453,4 +470,28 @@
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
}
+
+ /**
+ * Instructs the zygote to pre-load the classes and native libraries at the given paths
+ * for the specified abi. Not all zygotes support this function.
+ */
+ public void preloadPackageForAbi(String packagePath, String libsPath, String abi)
+ throws ZygoteStartFailedEx, IOException {
+ synchronized(mLock) {
+ ZygoteState state = openZygoteSocketIfNeeded(abi);
+ state.writer.write("3");
+ state.writer.newLine();
+
+ state.writer.write("--preload-package");
+ state.writer.newLine();
+
+ state.writer.write(packagePath);
+ state.writer.newLine();
+
+ state.writer.write(libsPath);
+ state.writer.newLine();
+
+ state.writer.flush();
+ }
+ }
}
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 8f73518..ecf1770 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
@@ -29,6 +31,7 @@
import android.os.RemoteException;
import android.print.PrintJobInfo;
import android.print.PrinterId;
+import android.print.PrinterInfo;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -247,6 +250,26 @@
public static final String EXTRA_PRINT_DOCUMENT_INFO =
"android.printservice.extra.PRINT_DOCUMENT_INFO";
+ /**
+ * When the {@link PendingIntent} declared via
+ * {@link PrinterInfo.Builder#setInfoIntent(PendingIntent)} is called this boolean extra
+ * will be filled in if the activity can select the printer.
+ *
+ * @see #EXTRA_SELECT_PRINTER
+ */
+ public static final String EXTRA_CAN_SELECT_PRINTER =
+ "android.printservice.extra.CAN_SELECT_PRINTER";
+
+ /**
+ * If this boolean extra is set to {@code true} in the {@link Activity#setResult(int, Intent)
+ * result data} from the activity specified in
+ * {@link PrinterInfo.Builder#setInfoIntent(PendingIntent)} the printer will be selected.
+ *
+ * @see #EXTRA_CAN_SELECT_PRINTER
+ */
+ public static final String EXTRA_SELECT_PRINTER =
+ "android.printservice.extra.SELECT_PRINTER";
+
private Handler mHandler;
private IPrintServiceClient mClient;
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 98371f4..1faada1 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -19,6 +19,10 @@
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.system.OsConstants.SEEK_SET;
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
+import static com.android.internal.util.Preconditions.checkCollectionNotEmpty;
+
import android.annotation.Nullable;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
@@ -55,6 +59,7 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
+import java.util.Objects;
/**
* Defines the contract between a documents provider and the platform.
@@ -372,19 +377,6 @@
public static final int FLAG_SUPPORTS_REMOVE = 1 << 10;
/**
- * Flag indicating that a document is an archive, and it's contents can be
- * obtained via {@link DocumentsProvider#queryChildDocuments}.
- * <p>
- * The <em>provider</em> support library offers utility classes to add common
- * archive support.
- *
- * @see #COLUMN_FLAGS
- * @see DocumentsProvider#queryChildDocuments(String, String[], String)
- * @hide
- */
- public static final int FLAG_ARCHIVE = 1 << 15;
-
- /**
* Flag indicating that a document is not complete, likely its
* contents are being downloaded. Partial files cannot be opened,
* copied, moved in the UI. But they can be deleted and retried
@@ -1311,21 +1303,26 @@
}
/**
- * Finds the canonical path to the root. Document id should be unique across
- * roots.
+ * Finds the canonical path to the top of the tree. The return value starts
+ * from the top of the tree or the root document to the requested document,
+ * both inclusive.
*
- * @param documentUri uri of the document which path is requested.
- * @return the path to the root of the document, or {@code null} if failed.
- * @see DocumentsProvider#findPath(String)
+ * Document id should be unique across roots.
+ *
+ * @param treeUri treeUri of the document which path is requested.
+ * @return a list of documents ID starting from the top of the tree to the
+ * requested document, or {@code null} if failed.
+ * @see DocumentsProvider#findPath(String, String)
*
* {@hide}
*/
- public static Path findPath(ContentResolver resolver, Uri documentUri)
- throws RemoteException {
+ public static List<String> findPath(ContentResolver resolver, Uri treeUri) {
+ checkArgument(isTreeUri(treeUri), treeUri + " is not a tree uri.");
+
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- documentUri.getAuthority());
+ treeUri.getAuthority());
try {
- return findPath(client, documentUri);
+ return findPath(client, treeUri).getPath();
} catch (Exception e) {
Log.w(TAG, "Failed to find path", e);
return null;
@@ -1334,11 +1331,24 @@
}
}
- /** {@hide} */
- public static Path findPath(ContentProviderClient client, Uri documentUri)
- throws RemoteException {
+ /**
+ * Finds the canonical path. If uri is a document uri returns path to a root and
+ * its associated root id. If uri is a tree uri returns the path to the top of
+ * the tree. The {@link Path#getPath()} in the return value starts from the top of
+ * the tree or the root document to the requested document, both inclusive.
+ *
+ * Document id should be unique across roots.
+ *
+ * @param uri uri of the document which path is requested. It can be either a
+ * plain document uri or a tree uri.
+ * @return the path of the document.
+ * @see DocumentsProvider#findPath(String, String)
+ *
+ * {@hide}
+ */
+ public static Path findPath(ContentProviderClient client, Uri uri) throws RemoteException {
final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
+ in.putParcelable(DocumentsContract.EXTRA_URI, uri);
final Bundle out = client.call(METHOD_FIND_PATH, null, in);
@@ -1392,20 +1402,71 @@
*/
public static final class Path implements Parcelable {
- public final String mRootId;
- public final List<String> mPath;
+ private final @Nullable String mRootId;
+ private final List<String> mPath;
/**
* Creates a Path.
- * @param rootId the id of the root
- * @param path the list of document ids from the root document
- * at position 0 to the target document
+ *
+ * @param rootId the ID of the root. May be null.
+ * @param path the list of document ids from the parent document at
+ * position 0 to the child document.
*/
public Path(String rootId, List<String> path) {
+ checkCollectionNotEmpty(path, "path");
+ checkCollectionElementsNotNull(path, "path");
+
mRootId = rootId;
mPath = path;
}
+ /**
+ * Returns the root id or null if the calling package doesn't have
+ * permission to access root information.
+ */
+ public @Nullable String getRootId() {
+ return mRootId;
+ }
+
+ /**
+ * Returns the path. The path is trimmed to the top of tree if
+ * calling package doesn't have permission to access those
+ * documents.
+ */
+ public List<String> getPath() {
+ return mPath;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || !(o instanceof Path)) {
+ return false;
+ }
+ Path path = (Path) o;
+ return Objects.equals(mRootId, path.mRootId) &&
+ Objects.equals(mPath, path.mPath);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRootId, mPath);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("DocumentsContract.Path{")
+ .append("rootId=")
+ .append(mRootId)
+ .append(", path=")
+ .append(mPath)
+ .append("}")
+ .toString();
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mRootId);
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 6234f6a..d75781b 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -36,6 +36,7 @@
import android.Manifest;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
import android.content.ClipDescription;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -54,8 +55,8 @@
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract.Path;
+import android.provider.DocumentsContract.Root;
import android.util.Log;
import libcore.io.IoUtils;
@@ -154,17 +155,7 @@
*/
@Override
public void attachInfo(Context context, ProviderInfo info) {
- mAuthority = info.authority;
-
- mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- mMatcher.addURI(mAuthority, "root", MATCH_ROOTS);
- mMatcher.addURI(mAuthority, "root/*", MATCH_ROOT);
- mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT);
- mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH);
- mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
- mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
- mMatcher.addURI(mAuthority, "tree/*/document/*", MATCH_DOCUMENT_TREE);
- mMatcher.addURI(mAuthority, "tree/*/document/*/children", MATCH_CHILDREN_TREE);
+ registerAuthority(info.authority);
// Sanity check our setup
if (!info.exported) {
@@ -181,6 +172,28 @@
super.attachInfo(context, info);
}
+ /** {@hide} */
+ @Override
+ public void attachInfoForTesting(Context context, ProviderInfo info) {
+ registerAuthority(info.authority);
+
+ super.attachInfoForTesting(context, info);
+ }
+
+ private void registerAuthority(String authority) {
+ mAuthority = authority;
+
+ mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ mMatcher.addURI(mAuthority, "root", MATCH_ROOTS);
+ mMatcher.addURI(mAuthority, "root/*", MATCH_ROOT);
+ mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT);
+ mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH);
+ mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
+ mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
+ mMatcher.addURI(mAuthority, "tree/*/document/*", MATCH_DOCUMENT_TREE);
+ mMatcher.addURI(mAuthority, "tree/*/document/*/children", MATCH_CHILDREN_TREE);
+ }
+
/**
* Test if a document is descendant (child, grandchild, etc) from the given
* parent. For example, providers must implement this to support
@@ -326,23 +339,28 @@
}
/**
- * Finds the canonical path to the root for the requested document. If there are
- * more than one path to this document, return the most typical one.
+ * Finds the canonical path for the requested document. The path must start
+ * from the parent document if parentDocumentId is not null or the root document
+ * if parentDocumentId is null. If there are more than one path to this document,
+ * return the most typical one. Include both the parent document or root document
+ * and the requested document in the returned path.
*
- * <p>This API assumes that document id has enough info to infer the root.
- * Different roots should use different document id to refer to the same
+ * <p>This API assumes that document ID has enough info to infer the root.
+ * Different roots should use different document ID to refer to the same
* document.
*
- * @param documentId the document which path is requested.
- * @return the path of the requested document to the root, or null if
- * such operation is not supported.
+ * @param childDocumentId the document which path is requested.
+ * @param parentDocumentId the document with which path starts if not null, or
+ * null to indicate path to root is requested.
+ * @return the path of the requested document. If parentDocumentId is null
+ * returned root ID must not be null. If parentDocumentId is not null
+ * returned root ID must be null.
*
* @hide
*/
- public Path findPath(String documentId)
+ public Path findPath(String childDocumentId, @Nullable String parentDocumentId)
throws FileNotFoundException {
- Log.w(TAG, "findPath is called on an unsupported provider.");
- return null;
+ throw new UnsupportedOperationException("findPath not supported.");
}
/**
@@ -897,9 +915,27 @@
// It's responsibility of the provider to revoke any grants, as the document may be
// still attached to another parents.
} else if (METHOD_FIND_PATH.equals(method)) {
- getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null);
+ final boolean isTreeUri = isTreeUri(documentUri);
- final Path path = findPath(documentId);
+ if (isTreeUri) {
+ enforceReadPermissionInner(documentUri, getCallingPackage(), null);
+ } else {
+ getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null);
+ }
+
+ final String parentDocumentId = isTreeUri
+ ? DocumentsContract.getTreeDocumentId(documentUri)
+ : null;
+
+ final Path path = findPath(documentId, parentDocumentId);
+
+ // Ensure provider doesn't leak information to unprivileged callers.
+ if (isTreeUri
+ && (path.getRootId() != null
+ || !Objects.equals(path.getPath().get(0), parentDocumentId))) {
+ throw new IllegalStateException(
+ "Provider returns an invalid result for findPath.");
+ }
out.putParcelable(DocumentsContract.EXTRA_RESULT, path);
} else {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0f4afae..d3a978c 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6933,6 +6933,12 @@
public static final String DOCK_SOUNDS_ENABLED = "dock_sounds_enabled";
/**
+ * Whether to play a sound for dock events, only when an accessibility service is on.
+ * @hide
+ */
+ public static final String DOCK_SOUNDS_ENABLED_WHEN_ACCESSIBILITY = "dock_sounds_enabled_when_accessbility";
+
+ /**
* URI for the "device locked" (keyguard shown) sound.
* @hide
*/
@@ -7226,6 +7232,13 @@
*/
public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
+ /**
+ * Size of the event buffer for IP connectivity metrics.
+ * @hide
+ */
+ public static final String CONNECTIVITY_METRICS_BUFFER_SIZE =
+ "connectivity_metrics_buffer_size";
+
/** {@hide} */
public static final String NETSTATS_ENABLED = "netstats_enabled";
/** {@hide} */
@@ -8062,11 +8075,45 @@
public static final String PAC_CHANGE_DELAY = "pac_change_delay";
/**
- * Setting to turn off captive portal detection. Feature is enabled by
- * default and the setting needs to be set to 0 to disable it.
+ * Don't attempt to detect captive portals.
*
* @hide
*/
+ public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0;
+
+ /**
+ * When detecting a captive portal, display a notification that
+ * prompts the user to sign in.
+ *
+ * @hide
+ */
+ public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1;
+
+ /**
+ * When detecting a captive portal, immediately disconnect from the
+ * network and do not reconnect to that network in the future.
+ *
+ * @hide
+ */
+ public static final int CAPTIVE_PORTAL_MODE_AVOID = 2;
+
+ /**
+ * What to do when connecting a network that presents a captive portal.
+ * Must be one of the CAPTIVE_PORTAL_MODE_* constants above.
+ *
+ * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
+ * @hide
+ */
+ public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
+
+ /**
+ * Setting to turn off captive portal detection. Feature is enabled by
+ * default and the setting needs to be set to 0 to disable it.
+ *
+ * @deprecated use CAPTIVE_PORTAL_MODE_IGNORE to disable captive portal detection
+ * @hide
+ */
+ @Deprecated
public static final String
CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled";
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 61d0a1e..4a956c6 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -36,9 +36,6 @@
private final Bundle mSignals;
private final int mUser;
- public static final String GROUP_KEY_OVERRIDE_KEY = "group_key_override";
- public static final String NEEDS_AUTOGROUPING_KEY = "autogroup_needed";
-
/**
* Create a notification adjustment.
*
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
new file mode 100644
index 0000000..f4eb132
--- /dev/null
+++ b/core/java/android/util/Half.java
@@ -0,0 +1,367 @@
+/*
+ * 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.util;
+
+/**
+ * <p>Half is a utility class to manipulate half-precision 16-bit
+ * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
+ * floating point data types (also called fp16 or binary16). A half-precision
+ * float is stored in a short data type. A half-precision float can be
+ * created from or converted to single-precision floats.</p>
+ *
+ * <p>The IEEE 754 standard specifies an fp16 as having the following format:</p>
+ * <ul>
+ * <li>Sign bit: 1 bit</li>
+ * <li>Exponent width: 5 bits</li>
+ * <li>Mantissa: 10 bits</li>
+ * </ul>
+ *
+ * <p>The format is laid out thusly:</p>
+ * <pre>
+ * 1 11111 1111111111
+ * ^ --^-- -----^----
+ * sign | |_______ mantissa
+ * |
+ * -- exponent
+ * </pre>
+ *
+ * @hide
+ */
+public final class Half {
+ /**
+ * The number of bits used to represent a half-precision float value.
+ */
+ public static final int SIZE = 16;
+
+ /**
+ * Epsilon is the difference between 1.0 and the next value representable
+ * by a half-precision floating-point.
+ */
+ public static final short EPSILON = (short) 0x1400;
+ /**
+ * Smallest negative value a half-precision float may have.
+ */
+ public static final short LOWEST_VALUE = (short) 0xfbff;
+ /**
+ * Maximum exponent a finite half-precision float may have.
+ */
+ public static final short MAX_EXPONENT = 15;
+ /**
+ * Maximum positive finite value a half-precision float may have.
+ */
+ public static final short MAX_VALUE = (short) 0x7bff;
+ /**
+ * Minimum exponent a normalized half-precision float may have.
+ */
+ public static final short MIN_EXPONENT = -14;
+ /**
+ * Smallest positive normal value a half-precision float may have.
+ */
+ public static final short MIN_NORMAL = (short) 0x0400;
+ /**
+ * Smallest positive non-zero value a half-precision float may have.
+ */
+ public static final short MIN_VALUE = (short) 0x0001;
+ /**
+ * A Not-a-Number representation of a half-precision float.
+ */
+ public static final short NaN = (short) 0x7e00;
+ /**
+ * Negative infinity of type half-precision float.
+ */
+ public static final short NEGATIVE_INFINITY = (short) 0xfc00;
+ /**
+ * Negative 0 of type half-precision float.
+ */
+ public static final short NEGATIVE_ZERO = (short) 0x8000;
+ /**
+ * Positive infinity of type half-precision float.
+ */
+ public static final short POSITIVE_INFINITY = (short) 0x7c00;
+ /**
+ * Positive 0 of type half-precision float.
+ */
+ public static final short POSITIVE_ZERO = (short) 0x0000;
+
+ private static final int FP16_SIGN_SHIFT = 15;
+ private static final int FP16_EXPONENT_SHIFT = 10;
+ private static final int FP16_EXPONENT_MASK = 0x1f;
+ private static final int FP16_MANTISSA_MASK = 0x3ff;
+ private static final int FP16_EXPONENT_BIAS = 15;
+
+ private static final int FP32_SIGN_SHIFT = 31;
+ private static final int FP32_EXPONENT_SHIFT = 23;
+ private static final int FP32_EXPONENT_MASK = 0xff;
+ private static final int FP32_MANTISSA_MASK = 0x7fffff;
+ private static final int FP32_EXPONENT_BIAS = 127;
+
+ private static final int FP32_DENORMAL_MAGIC = 126 << 23;
+ private static final float FP32_DENORMAL_FLOAT =
+ Float.intBitsToFloat(FP32_DENORMAL_MAGIC);
+
+ private Half() {
+ }
+
+ /**
+ * Returns the sign of the specified half-precision float.
+ *
+ * @param h A half-precision float value
+ * @return 1 if the value is positive, -1 if the value is negative
+ */
+ public static int getSign(short h) {
+ return (h >>> FP16_SIGN_SHIFT) == 0 ? 1 : -1;
+ }
+
+ /**
+ * Returns the unbiased exponent used in the representation of
+ * the specified half-precision float value. if the value is NaN
+ * or infinite, this* method returns {@link #MAX_EXPONENT} + 1.
+ * If the argument is* 0 or denormal, this method returns
+ * {@link #MIN_EXPONENT} - 1.
+ *
+ * @param h A half-precision float value
+ * @return The unbiased exponent of the specified value
+ */
+ public static int getExponent(short h) {
+ return ((h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK) - FP16_EXPONENT_BIAS;
+ }
+
+ /**
+ * Returns the mantissa, or significand, used in the representation
+ * of the specified half-precision float value.
+ *
+ * @param h A half-precision float value
+ * @return The mantissa, or significand, of the specified vlaue
+ */
+ public static int getMantissa(short h) {
+ return h & FP16_MANTISSA_MASK;
+ }
+
+ /**
+ * Returns true if the specified half-precision float value represents
+ * infinity, false otherwise.
+ *
+ * @param h A half-precision float value
+ * @return true if the value is positive infinity or negative infinity,
+ * false otherwise
+ */
+ public static boolean isInfinite(short h) {
+ int e = (h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
+ int m = (h ) & FP16_MANTISSA_MASK;
+ return e == 0x1f && m == 0;
+ }
+
+ /**
+ * Returns true if the specified half-precision float value represents
+ * a Not-a-Number, false otherwise.
+ *
+ * @param h A half-precision float value
+ * @return true if the value is a NaN, false otherwise
+ */
+ public static boolean isNaN(short h) {
+ int e = (h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
+ int m = (h ) & FP16_MANTISSA_MASK;
+ return e == 0x1f && m != 0;
+ }
+
+ /**
+ * <p>Converts the specified half-precision float value into a
+ * single-precision float value with the following special cases:</p>
+ * <ul>
+ * <li>If the input is {@link #NaN}, the returned* value is {@link Float#NaN}</li>
+ * <li>If the input is {@link #POSITIVE_INFINITY} or
+ * {@link #NEGATIVE_INFINITY}, the returned value is respectively
+ * {@link Float#POSITIVE_INFINITY} or {@link Float#NEGATIVE_INFINITY}</li>
+ * <li>If the input is 0 (positive or negative), the returned value is +/-0.0f</li>
+ * <li>Otherwise, the returned value is a normalized single-precision float value</li>
+ * </ul>
+ *
+ * @param h The half-precision float value to convert to single-precision
+ * @return A normalized single-precision float value
+ */
+ public static float toFloat(short h) {
+ int bits = h & 0xffff;
+ int s = (bits >>> FP16_SIGN_SHIFT );
+ int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
+ int m = (bits ) & FP16_MANTISSA_MASK;
+
+ int outE = 0;
+ int outM = 0;
+
+ if (e == 0) { // Denormal or 0
+ if (m != 0) {
+ // Convert denorm fp16 into normalized fp32
+ float o = Float.intBitsToFloat(FP32_DENORMAL_MAGIC + m);
+ o -= FP32_DENORMAL_FLOAT;
+ return s == 0 ? o : -o;
+ }
+ } else {
+ outM = m << 13;
+ if (e == 0x1f) { // Infinite or NaN
+ outE = 0xff;
+ } else {
+ outE = e - FP16_EXPONENT_BIAS + FP32_EXPONENT_BIAS;
+ }
+ }
+
+ int out = (s << FP32_SIGN_SHIFT) | (outE << FP32_EXPONENT_SHIFT) | outM;
+ return Float.intBitsToFloat(out);
+ }
+
+ /**
+ * <p>Converts the specified single-precision float value into a
+ * half-precision float value with the following special cases:</p>
+ * <ul>
+ * <li>If the input is NaN (see {@link Float#isNaN(float)}), the returned
+ * value is {@link #NaN}</li>
+ * <li>If the input is {@link Float#POSITIVE_INFINITY} or
+ * {@link Float#NEGATIVE_INFINITY}, the returned value is respectively
+ * {@link #POSITIVE_INFINITY} or {@link #NEGATIVE_INFINITY}</li>
+ * <li>If the input is 0 (positive or negative), the returned value is
+ * {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li>
+ * <li>If the input is a less than {@link #MIN_VALUE}, the returned value
+ * is flushed to {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li>
+ * <li>If the input is a less than {@link #MIN_NORMAL}, the returned value
+ * is a denorm half-precision float</li>
+ * <li>Otherwise, the returned value is rounded to the nearest
+ * representable half-precision float value</li>
+ * </ul>
+ *
+ * @param f The single-precision float value to convert to half-precision
+ * @return A half-precision float value
+ */
+ @SuppressWarnings("StatementWithEmptyBody")
+ public static short valueOf(float f) {
+ int bits = Float.floatToRawIntBits(f);
+ int s = (bits >>> FP32_SIGN_SHIFT );
+ int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_EXPONENT_MASK;
+ int m = (bits ) & FP32_MANTISSA_MASK;
+
+ int outE = 0;
+ int outM = 0;
+
+ if (e == 0xff) { // Infinite or NaN
+ outE = 0x1f;
+ outM = m != 0 ? 0x200 : 0;
+ } else {
+ e = e - FP32_EXPONENT_BIAS + FP16_EXPONENT_BIAS;
+ if (e >= 0x1f) { // Overflow
+ outE = 0x31;
+ } else if (e <= 0) { // Underflow
+ if (e < -10) {
+ // The absolute fp32 value is less than MIN_VALUE, flush to +/-0
+ } else {
+ // The fp32 value is a normalized float less than MIN_NORMAL,
+ // we convert to a denorm fp16
+ m = (m | 0x800000) >> (1 - e);
+ if ((m & 0x1000) != 0) m += 0x2000;
+ outM = m >> 13;
+ }
+ } else {
+ outE = e;
+ outM = m >> 13;
+ if ((m & 0x1000) != 0) {
+ // Round to nearest "0.5" up
+ int out = (outE << FP16_EXPONENT_SHIFT) | outM;
+ out++;
+ out |= (s << FP16_SIGN_SHIFT);
+ return (short) out;
+ }
+ }
+ }
+
+ int out = (s << FP16_SIGN_SHIFT) | (outE << FP16_EXPONENT_SHIFT) | outM;
+ return (short) out;
+ }
+
+ /**
+ * Returns a string representation of the specified half-precision
+ * float value. Calling this method is equivalent to calling
+ * <code>Float.toString(toFloat(h))</code>. See {@link Float#toString(float)}
+ * for more information on the format of the string representation.
+ *
+ * @param h A half-precision float value
+ * @return A string representation of the specified value
+ */
+ public static String toString(short h) {
+ return Float.toString(toFloat(h));
+ }
+
+ /**
+ * <p>Returns a hexadecimal string representation of the specified half-precision
+ * float value. If the value is a NaN, the result is <code>"NaN"</code>,
+ * otherwise the result follows this format:</p>
+ * <ul>
+ * <li>If the sign is positive, no sign character appears in the result</li>
+ * <li>If the sign is negative, the first character is <code>'-'</code></li>
+ * <li>If the value is inifinity, the string is <code>"Infinity"</code></li>
+ * <li>If the value is 0, the string is <code>"0x0.0p0"</code></li>
+ * <li>If the value has a normalized representation, the exponent and
+ * mantissa are represented in the string in two fields. The mantissa starts
+ * with <code>"0x1."</code> followed by its lowercase hexadecimal
+ * representation. Trailing zeroes are removed unless all digits are 0, then
+ * a single zero is used. The mantissa representation is followed by the
+ * exponent, represented by <code>"p"</code>, itself followed by a decimal
+ * string of the unbiased exponent</li>
+ * <li>If the value has a denormal representation, the mantissa starts
+ * with <code>"0x0."</code> followed by its lowercase hexadecimal
+ * representation. Trailing zeroes are removed unless all digits are 0, then
+ * a single zero is used. The mantissa representation is followed by the
+ * exponent, represented by <code>"p-14"</code></li>
+ * </ul>
+ *
+ * @param h A half-precision float value
+ * @return A hexadecimal string representation of the specified value
+ */
+ public static String toHexString(short h) {
+ StringBuilder o = new StringBuilder();
+
+ int bits = h & 0xffff;
+ int s = (bits >>> FP16_SIGN_SHIFT );
+ int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
+ int m = (bits ) & FP16_MANTISSA_MASK;
+
+ if (e == 0x1f) { // Infinite or NaN
+ if (m == 0) {
+ if (s == 1) o.append('-');
+ o.append("Infinity");
+ } else {
+ o.append("NaN");
+ }
+ } else {
+ if (s == 1) o.append('-');
+ if (e == 0) {
+ if (m == 0) {
+ o.append("0x0.0p0");
+ } else {
+ o.append("0x0.");
+ String mantissa = Integer.toHexString(m);
+ o.append(mantissa.replaceFirst("0{2,}$", ""));
+ o.append("p-14");
+ }
+ } else {
+ o.append("0x1.");
+ String mantissa = Integer.toHexString(m);
+ o.append(mantissa.replaceFirst("0{2,}$", ""));
+ o.append('p');
+ o.append(Integer.toString(e - FP16_EXPONENT_BIAS));
+ }
+ }
+
+ return o.toString();
+ }
+}
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index eb41f9e..106f172 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -51,9 +51,10 @@
if (node == null) throw new IllegalArgumentException("node cannot be null");
DisplayListCanvas canvas = sPool.acquire();
if (canvas == null) {
- canvas = new DisplayListCanvas(width, height);
+ canvas = new DisplayListCanvas(node, width, height);
} else {
- nResetDisplayListCanvas(canvas.mNativeCanvasWrapper, width, height);
+ nResetDisplayListCanvas(canvas.mNativeCanvasWrapper, node.mNativeRenderNode,
+ width, height);
}
canvas.mNode = node;
canvas.mWidth = width;
@@ -79,8 +80,8 @@
// Constructors
///////////////////////////////////////////////////////////////////////////
- private DisplayListCanvas(int width, int height) {
- super(nCreateDisplayListCanvas(width, height));
+ private DisplayListCanvas(@NonNull RenderNode node, int width, int height) {
+ super(nCreateDisplayListCanvas(node.mNativeRenderNode, width, height));
mDensity = 0; // disable bitmap density scaling
}
@@ -230,9 +231,10 @@
}
@FastNative
- private static native long nCreateDisplayListCanvas(int width, int height);
+ private static native long nCreateDisplayListCanvas(long node, int width, int height);
@FastNative
- private static native void nResetDisplayListCanvas(long canvas, int width, int height);
+ private static native void nResetDisplayListCanvas(long canvas, long node,
+ int width, int height);
@FastNative
private static native int nGetMaximumTextureWidth();
@FastNative
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 717b675..e42b42a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -191,10 +191,10 @@
// If there is a change, the new Configuration is returned and the
// caller must call setNewConfiguration() sometime later.
Configuration updateOrientationFromAppTokens(in Configuration currentConfig,
- IBinder freezeThisOneIfNeeded);
- // Notify window manager of the new configuration. Returns an array of stack ids that's
- // affected by the update, ActivityManager should resize these stacks.
- int[] setNewConfiguration(in Configuration config);
+ IBinder freezeThisOneIfNeeded, int displayId);
+ // Notify window manager of the new display override configuration. Returns an array of stack
+ // ids that were affected by the update, ActivityManager should resize these stacks.
+ int[] setNewDisplayOverrideConfiguration(in Configuration overrideConfig, int displayId);
// Retrieves the new bounds after the configuration update evaluated by window manager.
Rect getBoundsForNewConfiguration(int stackId);
@@ -444,7 +444,7 @@
/**
* Retrieves the current stable insets from the primary display.
*/
- void getStableInsets(out Rect outInsets);
+ void getStableInsets(int displayId, out Rect outInsets);
/**
* Register shortcut key. Shortcut code is packed as:
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index abb3051..f14acaa 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1578,30 +1578,30 @@
public float buttonBrightness = BRIGHTNESS_OVERRIDE_NONE;
/**
- * Value for {@link #rotationAnimation} to define the animation used to
- * specify that this window will rotate in or out following a rotation.
+ * Value for {@link #rotationAnimation} which specifies that this
+ * window will visually rotate in or out following a rotation.
*/
public static final int ROTATION_ANIMATION_ROTATE = 0;
/**
- * Value for {@link #rotationAnimation} to define the animation used to
- * specify that this window will fade in or out following a rotation.
+ * Value for {@link #rotationAnimation} which specifies that this
+ * window will fade in or out following a rotation.
*/
public static final int ROTATION_ANIMATION_CROSSFADE = 1;
/**
- * Value for {@link #rotationAnimation} to define the animation used to
- * specify that this window will immediately disappear or appear following
- * a rotation.
+ * Value for {@link #rotationAnimation} which specifies that this window
+ * will immediately disappear or appear following a rotation.
*/
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
+ * can't be applied without pausing the screen. For example, this is ideal
+ * for Camera apps which don't want the viewfinder contents to ever rotate
+ * or fade (and rather to be seamless) but also don't want ROTATION_ANIMATION_JUMPCUT
+ * during app transition scenarios where seamless rotation can't be applied.
*/
public static final int ROTATION_ANIMATION_SEAMLESS = 3;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 939f45f..b840f4a 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2566,6 +2566,13 @@
mProvider.getViewDelegate().onConfigurationChanged(newConfig);
}
+ /**
+ * Creates a new InputConnection for an InputMethod to interact with the WebView.
+ * This is similar to {@link View#onCreateInputConnection} but note that WebView
+ * calls InputConnection methods on a thread other than the UI thread.
+ * If these methods are overridden, then the overriding methods should respect
+ * thread restrictions when calling View methods or accessing data.
+ */
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return mProvider.getViewDelegate().onCreateInputConnection(outAttrs);
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 884a86c..f4ea90b 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -490,6 +490,9 @@
// Log and discard errors at this stage as we must not crash the system server.
Log.e(LOGTAG, "error preparing webview native library", t);
}
+
+ WebViewZygote.onWebViewProviderChanged(packageInfo);
+
return prepareWebViewInSystemServer(nativeLibs);
}
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
new file mode 100644
index 0000000..bc6e7b4
--- /dev/null
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -0,0 +1,135 @@
+/*
+ * 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.webkit;
+
+import android.content.pm.PackageInfo;
+import android.os.Build;
+import android.os.SystemService;
+import android.os.ZygoteProcess;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.TimeoutException;
+
+/** @hide */
+public class WebViewZygote {
+ private static final String LOGTAG = "WebViewZygote";
+
+ private static final String WEBVIEW_ZYGOTE_SERVICE_32 = "webview_zygote32";
+ private static final String WEBVIEW_ZYGOTE_SERVICE_64 = "webview_zygote64";
+
+ private static ZygoteProcess sZygote;
+
+ private static PackageInfo sPackage;
+
+ private static boolean sMultiprocessEnabled = false;
+
+ public static ZygoteProcess getProcess() {
+ connectToZygoteIfNeeded();
+ return sZygote;
+ }
+
+ public static String getPackageName() {
+ return sPackage.packageName;
+ }
+
+ public static void setMultiprocessEnabled(boolean enabled) {
+ sMultiprocessEnabled = enabled;
+
+ // When toggling between multi-process being on/off, start or stop the
+ // service. If it is enabled and the zygote is not yet started, bring up the service.
+ // Otherwise, bring down the service. The name may be null if the package
+ // information has not yet been resolved.
+ final String serviceName = getServiceName();
+ if (serviceName == null) return;
+
+ if (enabled && sZygote == null) {
+ SystemService.start(serviceName);
+ } else {
+ SystemService.stop(serviceName);
+ sZygote = null;
+ }
+ }
+
+ public static void onWebViewProviderChanged(PackageInfo packageInfo) {
+ sPackage = packageInfo;
+
+ // If multi-process is not enabled, then do not start the zygote service.
+ if (!sMultiprocessEnabled) {
+ return;
+ }
+
+ final String serviceName = getServiceName();
+
+ if (SystemService.isStopped(serviceName)) {
+ SystemService.start(serviceName);
+ } else if (sZygote != null) {
+ SystemService.restart(serviceName);
+ }
+
+ try {
+ SystemService.waitForState(serviceName, SystemService.State.RUNNING, 5000);
+ } catch (TimeoutException e) {
+ Log.e(LOGTAG, "Timed out waiting for " + serviceName);
+ return;
+ }
+
+ connectToZygoteIfNeeded();
+ }
+
+ private static String getServiceName() {
+ if (sPackage == null)
+ return null;
+
+ if (Arrays.asList(Build.SUPPORTED_64_BIT_ABIS).contains(
+ sPackage.applicationInfo.primaryCpuAbi)) {
+ return WEBVIEW_ZYGOTE_SERVICE_64;
+ }
+
+ return WEBVIEW_ZYGOTE_SERVICE_32;
+ }
+
+ private static void connectToZygoteIfNeeded() {
+ if (sZygote != null)
+ return;
+
+ if (sPackage == null) {
+ Log.e(LOGTAG, "Cannot connect to zygote, no package specified");
+ return;
+ }
+
+ final String serviceName = getServiceName();
+ if (!SystemService.isRunning(serviceName)) {
+ Log.e(LOGTAG, serviceName + " is not running");
+ return;
+ }
+
+ try {
+ sZygote = new ZygoteProcess("webview_zygote", null);
+
+ String packagePath = sPackage.applicationInfo.sourceDir;
+ String libsPath = sPackage.applicationInfo.nativeLibraryDir;
+
+ Log.d(LOGTAG, "Preloading package " + packagePath + " " + libsPath);
+ sZygote.preloadPackageForAbi(packagePath, libsPath, Build.SUPPORTED_ABIS[0]);
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Error connecting to " + serviceName, e);
+ sZygote = null;
+ }
+ }
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 184453b..d5a933c 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -6284,6 +6284,17 @@
* @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
*/
public void setRemoteViewsAdapter(Intent intent) {
+ setRemoteViewsAdapter(intent, false);
+ }
+
+ /** @hide **/
+ public Runnable setRemoteViewsAdapterAsync(final Intent intent) {
+ return new RemoteViewsAdapter.AsyncRemoteAdapterAction(this, intent);
+ }
+
+ /** @hide **/
+ @Override
+ public void setRemoteViewsAdapter(Intent intent, boolean isAsync) {
// Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
// service handling the specified intent.
if (mRemoteAdapter != null) {
@@ -6296,7 +6307,7 @@
}
mDeferNotifyDataSetChanged = false;
// Otherwise, create a new RemoteViewsAdapter for binding
- mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this);
+ mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this, isAsync);
if (mRemoteAdapter.isDataReady()) {
setAdapter(mRemoteAdapter);
}
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 0e3a69f..6f29368 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -975,8 +975,19 @@
* @param intent the intent used to identify the RemoteViewsService for the adapter to
* connect to.
*/
- @android.view.RemotableViewMethod
+ @android.view.RemotableViewMethod(asyncImpl="setRemoteViewsAdapterAsync")
public void setRemoteViewsAdapter(Intent intent) {
+ setRemoteViewsAdapter(intent, false);
+ }
+
+ /** @hide **/
+ public Runnable setRemoteViewsAdapterAsync(final Intent intent) {
+ return new RemoteViewsAdapter.AsyncRemoteAdapterAction(this, intent);
+ }
+
+ /** @hide **/
+ @Override
+ public void setRemoteViewsAdapter(Intent intent, boolean isAsync) {
// Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
// service handling the specified intent.
if (mRemoteViewsAdapter != null) {
@@ -989,7 +1000,7 @@
}
mDeferNotifyDataSetChanged = false;
// Otherwise, create a new RemoteViewsAdapter for binding
- mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this);
+ mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this, isAsync);
if (mRemoteViewsAdapter.isDataReady()) {
setAdapter(mRemoteViewsAdapter);
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index bf49048..5eaabe7 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2345,7 +2345,7 @@
}
mCorrectionHighlighter.highlight(info);
- mUndoInputFilter.onCommitCorrection();
+ mUndoInputFilter.freezeLastEdit();
}
void onScrollChanged() {
@@ -2477,6 +2477,7 @@
}
mTextView.beginBatchEdit();
+ mUndoInputFilter.freezeLastEdit();
try {
final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
Object localState = event.getLocalState();
@@ -2526,6 +2527,7 @@
}
} finally {
mTextView.endBatchEdit();
+ mUndoInputFilter.freezeLastEdit();
}
}
@@ -5477,10 +5479,12 @@
// Expanding with start handle.
offset = getWordStart(offset);
startOffset = getWordEnd(mStartOffset);
+ if (startOffset == offset) {
+ offset = getNextCursorOffset(offset, false);
+ }
}
mLineSelectionIsOn = currLine;
- Selection.setSelection((Spannable) mTextView.getText(),
- startOffset, offset);
+ Selection.setSelection((Spannable) mTextView.getText(), startOffset, offset);
}
private void updateParagraphBasedSelection(MotionEvent event) {
@@ -5843,7 +5847,7 @@
return null;
}
- void onCommitCorrection() {
+ void freezeLastEdit() {
mEditor.mUndoManager.beginUpdate("Edit text");
EditOperation lastEdit = getLastEdit();
if (lastEdit != null) {
@@ -5904,7 +5908,6 @@
// Add this as the first edit.
if (DEBUG_UNDO) Log.d(TAG, "filter: adding first op " + edit);
um.addOperation(edit, UndoManager.MERGE_MODE_NONE);
- mPreviousOperationWasInSameBatchEdit = mIsUserEdit;
} else if (mergeMode == MERGE_EDIT_MODE_FORCE_MERGE) {
// Forced merges take priority because they could be the result of a non-user-edit
// change and this case should not create a new undo operation.
@@ -5916,7 +5919,6 @@
if (DEBUG_UNDO) Log.d(TAG, "non-user edit, new op " + edit);
um.commitState(mEditor.mUndoOwner);
um.addOperation(edit, UndoManager.MERGE_MODE_NONE);
- mPreviousOperationWasInSameBatchEdit = mIsUserEdit;
} else if (mergeMode == MERGE_EDIT_MODE_NORMAL && lastEdit.mergeWith(edit)) {
// Merge succeeded, nothing else to do.
if (DEBUG_UNDO) Log.d(TAG, "filter: merge succeeded, created " + lastEdit);
@@ -5925,8 +5927,8 @@
if (DEBUG_UNDO) Log.d(TAG, "filter: merge failed, adding " + edit);
um.commitState(mEditor.mUndoOwner);
um.addOperation(edit, UndoManager.MERGE_MODE_NONE);
- mPreviousOperationWasInSameBatchEdit = mIsUserEdit;
}
+ mPreviousOperationWasInSameBatchEdit = mIsUserEdit;
um.endUpdate();
}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 20543fb..82071d7 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -175,7 +175,7 @@
* through the specified intent.
* @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
*/
- @android.view.RemotableViewMethod
+ @android.view.RemotableViewMethod(asyncImpl="setRemoteViewsAdapterAsync")
public void setRemoteViewsAdapter(Intent intent) {
super.setRemoteViewsAdapter(intent);
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 4d405c5..cd80651 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -455,7 +455,16 @@
/** @hide **/
public Runnable setImageResourceAsync(@DrawableRes int resId) {
- return new ImageDrawableCallback(getContext().getDrawable(resId), null, resId);
+ Drawable d = null;
+ if (resId != 0) {
+ try {
+ d = getContext().getDrawable(resId);
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "Unable to find resource: " + resId, e);
+ resId = 0;
+ }
+ }
+ return new ImageDrawableCallback(d, null, resId);
}
/**
@@ -865,7 +874,7 @@
} catch (Exception e) {
Log.w(LOG_TAG, "Unable to find resource: " + mResource, e);
// Don't try again.
- mUri = null;
+ mResource = 0;
}
} else if (mUri != null) {
d = getDrawableFromUri(mUri);
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index e88c7ef..e52c13b 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -448,7 +448,7 @@
* through the specified intent.
* @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
*/
- @android.view.RemotableViewMethod
+ @android.view.RemotableViewMethod(asyncImpl="setRemoteViewsAdapterAsync")
public void setRemoteViewsAdapter(Intent intent) {
super.setRemoteViewsAdapter(intent);
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 6543d0c..316dab5 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -320,6 +320,10 @@
return this;
}
+ public boolean prefersAsyncApply() {
+ return false;
+ }
+
int viewId;
}
@@ -715,20 +719,29 @@
intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
if (target instanceof AbsListView) {
AbsListView v = (AbsListView) target;
- v.setRemoteViewsAdapter(intent);
+ v.setRemoteViewsAdapter(intent, isAsync);
v.setRemoteViewsOnClickHandler(handler);
} else if (target instanceof AdapterViewAnimator) {
AdapterViewAnimator v = (AdapterViewAnimator) target;
- v.setRemoteViewsAdapter(intent);
+ v.setRemoteViewsAdapter(intent, isAsync);
v.setRemoteViewsOnClickHandler(handler);
}
}
+ @Override
+ public Action initActionAsync(ViewTree root, ViewGroup rootParent,
+ OnClickHandler handler) {
+ SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
+ copy.isAsync = true;
+ return copy;
+ }
+
public String getActionName() {
return "SetRemoteViewsAdapterIntent";
}
Intent intent;
+ boolean isAsync = false;
public final static int TAG = 10;
}
@@ -1461,6 +1474,11 @@
// unique from the standpoint of merging.
return "ReflectionAction" + this.methodName + this.type;
}
+
+ @Override
+ public boolean prefersAsyncApply() {
+ return this.type == URI || this.type == ICON;
+ }
}
/**
@@ -1598,6 +1616,11 @@
return MERGE_APPEND;
}
+ @Override
+ public boolean prefersAsyncApply() {
+ return nestedViews != null && nestedViews.prefersAsyncApply();
+ }
+
RemoteViews nestedViews;
public final static int TAG = 4;
@@ -1749,6 +1772,11 @@
return copy;
}
+ @Override
+ public boolean prefersAsyncApply() {
+ return useIcons;
+ }
+
public String getActionName() {
return "TextViewDrawableAction";
}
@@ -3443,6 +3471,24 @@
}
}
+ /**
+ * Returns true if the RemoteViews contains potentially costly operations and should be
+ * applied asynchronously.
+ *
+ * @hide
+ */
+ public boolean prefersAsyncApply() {
+ if (mActions != null) {
+ final int count = mActions.size();
+ for (int i = 0; i < count; i++) {
+ if (mActions.get(i).prefersAsyncApply()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private Context getContextForResources(Context context) {
if (mApplication != null) {
if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index e0f94fd..11e0a3f 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -45,6 +45,12 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
+import java.util.concurrent.Executor;
+
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
/**
* An adapter to a RemoteViewsService which fetches and caches RemoteViews
@@ -73,7 +79,8 @@
private final Context mContext;
private final Intent mIntent;
private final int mAppWidgetId;
- private LayoutInflater mLayoutInflater;
+ private final Executor mAsyncViewLoadExecutor;
+
private RemoteViewsAdapterServiceConnection mServiceConnection;
private WeakReference<RemoteAdapterConnectionCallback> mCallback;
private OnClickHandler mRemoteViewsOnClickHandler;
@@ -120,15 +127,33 @@
/**
* @return whether the adapter was set or not.
*/
- public boolean onRemoteAdapterConnected();
+ boolean onRemoteAdapterConnected();
- public void onRemoteAdapterDisconnected();
+ void onRemoteAdapterDisconnected();
/**
* This defers a notifyDataSetChanged on the pending RemoteViewsAdapter if it has not
* connected yet.
*/
- public void deferNotifyDataSetChanged();
+ void deferNotifyDataSetChanged();
+
+ void setRemoteViewsAdapter(Intent intent, boolean isAsync);
+ }
+
+ public static class AsyncRemoteAdapterAction implements Runnable {
+
+ private final RemoteAdapterConnectionCallback mCallback;
+ private final Intent mIntent;
+
+ public AsyncRemoteAdapterAction(RemoteAdapterConnectionCallback callback, Intent intent) {
+ mCallback = callback;
+ mIntent = intent;
+ }
+
+ @Override
+ public void run() {
+ mCallback.setRemoteViewsAdapter(mIntent, true);
+ }
}
/**
@@ -162,7 +187,7 @@
}
mIsConnecting = true;
} catch (Exception e) {
- Log.e("RemoteViewsAdapterServiceConnection", "bind(): " + e.getMessage());
+ Log.e("RVAServiceConnection", "bind(): " + e.getMessage());
mIsConnecting = false;
mIsConnected = false;
}
@@ -180,7 +205,7 @@
}
mIsConnecting = false;
} catch (Exception e) {
- Log.e("RemoteViewsAdapterServiceConnection", "unbind(): " + e.getMessage());
+ Log.e("RVAServiceConnection", "unbind(): " + e.getMessage());
mIsConnecting = false;
mIsConnected = false;
}
@@ -298,15 +323,29 @@
* Updates this RemoteViewsFrameLayout depending on the view that was loaded.
* @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded
* successfully.
+ * @param forceApplyAsync when true, the host will always try to inflate the view
+ * asynchronously (for eg, when we are already showing the loading
+ * view)
*/
- public void onRemoteViewsLoaded(RemoteViews view, OnClickHandler handler) {
+ public void onRemoteViewsLoaded(RemoteViews view, OnClickHandler handler,
+ boolean forceApplyAsync) {
setOnClickHandler(handler);
- applyRemoteViews(view);
+ applyRemoteViews(view, forceApplyAsync || ((view != null) && view.prefersAsyncApply()));
}
+ /**
+ * Creates a default loading view. Uses the size of the first row as a guide for the
+ * size of the loading view.
+ */
@Override
protected View getDefaultView() {
- return mCache.getMetaData().createDefaultLoadingView(this);
+ int viewHeight = mCache.getMetaData().getLoadingTemplate(getContext()).defaultHeight;
+ // Compose the loading view text
+ TextView loadingTextView = (TextView) LayoutInflater.from(getContext()).inflate(
+ com.android.internal.R.layout.remote_views_adapter_default_loading_view,
+ this, false);
+ loadingTextView.setHeight(viewHeight);
+ return loadingTextView;
}
@Override
@@ -359,7 +398,7 @@
if (refs != null) {
// Notify all the references for that position of the newly loaded RemoteViews
for (final RemoteViewsFrameLayout ref : refs) {
- ref.onRemoteViewsLoaded(view, mRemoteViewsOnClickHandler);
+ ref.onRemoteViewsLoaded(view, mRemoteViewsOnClickHandler, true);
if (mViewToLinkedList.containsKey(ref)) {
mViewToLinkedList.remove(ref);
}
@@ -402,9 +441,7 @@
// Used to determine how to construct loading views. If a loading view is not specified
// by the user, then we try and load the first view, and use its height as the height for
// the default loading view.
- RemoteViews mUserLoadingView;
- RemoteViews mFirstView;
- int mFirstViewHeight;
+ LoadingViewTemplate loadingTemplate;
// A mapping from type id to a set of unique type ids
private final SparseIntArray mTypeIdIndexMap = new SparseIntArray();
@@ -418,7 +455,7 @@
count = d.count;
viewTypeCount = d.viewTypeCount;
hasStableIds = d.hasStableIds;
- setLoadingViewTemplates(d.mUserLoadingView, d.mFirstView);
+ loadingTemplate = d.loadingTemplate;
}
}
@@ -428,20 +465,10 @@
// by default there is at least one dummy view type
viewTypeCount = 1;
hasStableIds = true;
- mUserLoadingView = null;
- mFirstView = null;
- mFirstViewHeight = 0;
+ loadingTemplate = null;
mTypeIdIndexMap.clear();
}
- public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) {
- mUserLoadingView = loadingView;
- if (firstView != null) {
- mFirstView = firstView;
- mFirstViewHeight = -1;
- }
- }
-
public int getMappedViewType(int typeId) {
int mappedTypeId = mTypeIdIndexMap.get(typeId, -1);
if (mappedTypeId == -1) {
@@ -457,33 +484,11 @@
return (mappedType < viewTypeCount);
}
- /**
- * Creates a default loading view. Uses the size of the first row as a guide for the
- * size of the loading view.
- */
- private synchronized View createDefaultLoadingView(ViewGroup parent) {
- final Context context = parent.getContext();
- if (mFirstViewHeight < 0) {
- try {
- View firstView = mFirstView.apply(parent.getContext(), parent);
- firstView.measure(
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
- mFirstViewHeight = firstView.getMeasuredHeight();
- } catch (Exception e) {
- float density = context.getResources().getDisplayMetrics().density;
- mFirstViewHeight = Math.round(sDefaultLoadingViewHeight * density);
- Log.w(TAG, "Error inflating first RemoteViews" + e);
- }
- mFirstView = null;
+ public synchronized LoadingViewTemplate getLoadingTemplate(Context context) {
+ if (loadingTemplate == null) {
+ loadingTemplate = new LoadingViewTemplate(null, context);
}
-
- // Compose the loading view text
- TextView loadingTextView = (TextView) LayoutInflater.from(context).inflate(
- com.android.internal.R.layout.remote_views_adapter_default_loading_view,
- parent, false);
- loadingTextView.setHeight(mFirstViewHeight);
- return loadingTextView;
+ return loadingTemplate;
}
}
@@ -772,7 +777,7 @@
}
public RemoteViewsAdapter(Context context, Intent intent,
- RemoteAdapterConnectionCallback callback) {
+ RemoteAdapterConnectionCallback callback, boolean useAsyncLoader) {
mContext = context;
mIntent = intent;
@@ -781,7 +786,6 @@
}
mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
- mLayoutInflater = LayoutInflater.from(context);
mRequestedViews = new RemoteViewsFrameLayoutRefSet();
// Strip the previously injected app widget id from service intent
@@ -794,6 +798,7 @@
mWorkerThread.start();
mWorkerQueue = new Handler(mWorkerThread.getLooper());
mMainQueue = new Handler(Looper.myLooper(), this);
+ mAsyncViewLoadExecutor = useAsyncLoader ? new HandlerThreadExecutor(mWorkerThread) : null;
if (sCacheRemovalThread == null) {
sCacheRemovalThread = new HandlerThread("RemoteViewsAdapter-cachePruner");
@@ -941,10 +946,14 @@
boolean hasStableIds = factory.hasStableIds();
int viewTypeCount = factory.getViewTypeCount();
int count = factory.getCount();
- RemoteViews loadingView = factory.getLoadingView();
- RemoteViews firstView = null;
- if ((count > 0) && (loadingView == null)) {
- firstView = factory.getViewAt(0);
+ LoadingViewTemplate loadingTemplate =
+ new LoadingViewTemplate(factory.getLoadingView(), mContext);
+ if ((count > 0) && (loadingTemplate.remoteViews == null)) {
+ RemoteViews firstView = factory.getViewAt(0);
+ if (firstView != null) {
+ loadingTemplate.loadFirstViewHeight(firstView, mContext,
+ new HandlerThreadExecutor(mWorkerThread));
+ }
}
final RemoteViewsMetaData tmpMetaData = mCache.getTemporaryMetaData();
synchronized (tmpMetaData) {
@@ -952,7 +961,7 @@
// We +1 because the base view type is the loading view
tmpMetaData.viewTypeCount = viewTypeCount + 1;
tmpMetaData.count = count;
- tmpMetaData.setLoadingViewTemplates(loadingView, firstView);
+ tmpMetaData.loadingTemplate = loadingTemplate;
}
} catch(RemoteException e) {
processException("updateMetaData", e);
@@ -1100,18 +1109,25 @@
hasNewItems = mCache.queuePositionsToBePreloadedFromRequestedPosition(position);
}
- final RemoteViewsFrameLayout layout =
- (convertView instanceof RemoteViewsFrameLayout)
- ? (RemoteViewsFrameLayout) convertView
- : new RemoteViewsFrameLayout(parent.getContext(), mCache);
+ final RemoteViewsFrameLayout layout;
+ if (convertView instanceof RemoteViewsFrameLayout) {
+ layout = (RemoteViewsFrameLayout) convertView;
+ } else {
+ layout = new RemoteViewsFrameLayout(parent.getContext(), mCache);
+ layout.setAsyncExecutor(mAsyncViewLoadExecutor);
+ }
+
if (isInCache) {
- layout.onRemoteViewsLoaded(rv, mRemoteViewsOnClickHandler);
+ // Apply the view synchronously if possible, to avoid flickering
+ layout.onRemoteViewsLoaded(rv, mRemoteViewsOnClickHandler, false);
if (hasNewItems) loadNextIndexInBackground();
} else {
// If the views is not loaded, apply the loading view. If the loading view doesn't
// exist, the layout will create a default view based on the firstView height.
- layout.onRemoteViewsLoaded(mCache.getMetaData().mUserLoadingView,
- mRemoteViewsOnClickHandler);
+ layout.onRemoteViewsLoaded(
+ mCache.getMetaData().getLoadingTemplate(mContext).remoteViews,
+ mRemoteViewsOnClickHandler,
+ false);
mRequestedViews.add(position, layout);
mCache.queueRequestedPositionToLoad(position);
loadNextIndexInBackground();
@@ -1285,4 +1301,58 @@
mMainQueue.removeMessages(sUnbindServiceMessageType);
return mServiceConnection.isConnected();
}
+
+ private static class HandlerThreadExecutor implements Executor {
+ private final HandlerThread mThread;
+
+ HandlerThreadExecutor(HandlerThread thread) {
+ mThread = thread;
+ }
+
+ @Override
+ public void execute(Runnable runnable) {
+ if (Thread.currentThread().getId() == mThread.getId()) {
+ runnable.run();
+ } else {
+ new Handler(mThread.getLooper()).post(runnable);
+ }
+ }
+ }
+
+ private static class LoadingViewTemplate {
+ public final RemoteViews remoteViews;
+ public int defaultHeight;
+
+ LoadingViewTemplate(RemoteViews views, Context context) {
+ remoteViews = views;
+
+ float density = context.getResources().getDisplayMetrics().density;
+ defaultHeight = Math.round(sDefaultLoadingViewHeight * density);
+ }
+
+ public void loadFirstViewHeight(
+ RemoteViews firstView, Context context, Executor executor) {
+ // Inflate the first view on the worker thread
+ firstView.applyAsync(context, new RemoteViewsFrameLayout(context, null), executor,
+ new RemoteViews.OnViewAppliedListener() {
+ @Override
+ public void onViewApplied(View v) {
+ try {
+ v.measure(
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ defaultHeight = v.getMeasuredHeight();
+ } catch (Exception e) {
+ onError(e);
+ }
+ }
+
+ @Override
+ public void onError(Exception e) {
+ // Do nothing. The default height will stay the same.
+ Log.w(TAG, "Error inflating first RemoteViews", e);
+ }
+ });
+ }
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f1c3079..2f9c97b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3685,18 +3685,23 @@
}
/**
- * Makes the TextView at least this many lines tall.
+ * Sets the height of the TextView to be at least {@code minLines} tall.
+ * <p>
+ * This value is used for height calculation if LayoutParams does not force TextView to have an
+ * exact height. Setting this value overrides other previous minimum height configurations such
+ * as {@link #setMinHeight(int)} or {@link #setHeight(int)}. {@link #setSingleLine()} will set
+ * this value to 1.
*
- * Setting this value overrides any other (minimum) height setting. A single line TextView will
- * set this value to 1.
+ * @param minLines the minimum height of TextView in terms of number of lines
*
* @see #getMinLines()
+ * @see #setLines(int)
*
* @attr ref android.R.styleable#TextView_minLines
*/
@android.view.RemotableViewMethod
- public void setMinLines(int minlines) {
- mMinimum = minlines;
+ public void setMinLines(int minLines) {
+ mMinimum = minLines;
mMinMode = LINES;
requestLayout();
@@ -3704,10 +3709,14 @@
}
/**
- * @return the minimum number of lines displayed in this TextView, or -1 if the minimum
- * height was set in pixels instead using {@link #setMinHeight(int) or #setHeight(int)}.
+ * Returns the minimum height of TextView in terms of number of lines or -1 if the minimum
+ * height was set using {@link #setMinHeight(int)} or {@link #setHeight(int)}.
+ *
+ * @return the minimum height of TextView in terms of number of lines or -1 if the minimum
+ * height is not defined in lines
*
* @see #setMinLines(int)
+ * @see #setLines(int)
*
* @attr ref android.R.styleable#TextView_minLines
*/
@@ -3716,15 +3725,26 @@
}
/**
- * Makes the TextView at least this many pixels tall.
+ * Sets the height of the TextView to be at least {@code minPixels} tall.
+ * <p>
+ * This value is used for height calculation if LayoutParams does not force TextView to have an
+ * exact height. Setting this value overrides previous minimum height configurations such as
+ * {@link #setMinLines(int)} or {@link #setLines(int)}.
+ * <p>
+ * The value given here is different than {@link #setMinimumHeight(int)}. Between
+ * {@code minHeight} and the value set in {@link #setMinimumHeight(int)}, the greater one is
+ * used to decide the final height.
*
- * Setting this value overrides any other (minimum) number of lines setting.
+ * @param minPixels the minimum height of TextView in terms of pixels
+ *
+ * @see #getMinHeight()
+ * @see #setHeight(int)
*
* @attr ref android.R.styleable#TextView_minHeight
*/
@android.view.RemotableViewMethod
- public void setMinHeight(int minHeight) {
- mMinimum = minHeight;
+ public void setMinHeight(int minPixels) {
+ mMinimum = minPixels;
mMinMode = PIXELS;
requestLayout();
@@ -3732,10 +3752,14 @@
}
/**
- * @return the minimum height of this TextView expressed in pixels, or -1 if the minimum
- * height was set in number of lines instead using {@link #setMinLines(int) or #setLines(int)}.
+ * Returns the minimum height of TextView in terms of pixels or -1 if the minimum height was
+ * set using {@link #setMinLines(int)} or {@link #setLines(int)}.
+ *
+ * @return the minimum height of TextView in terms of pixels or -1 if the minimum height is not
+ * defined in pixels
*
* @see #setMinHeight(int)
+ * @see #setHeight(int)
*
* @attr ref android.R.styleable#TextView_minHeight
*/
@@ -3744,15 +3768,22 @@
}
/**
- * Makes the TextView at most this many lines tall.
+ * Sets the height of the TextView to be at most {@code maxLines} tall.
+ * <p>
+ * This value is used for height calculation if LayoutParams does not force TextView to have an
+ * exact height. Setting this value overrides previous maximum height configurations such as
+ * {@link #setMaxHeight(int)} or {@link #setLines(int)}.
*
- * Setting this value overrides any other (maximum) height setting.
+ * @param maxLines the maximum height of TextView in terms of number of lines
+ *
+ * @see #getMaxLines()
+ * @see #setLines(int)
*
* @attr ref android.R.styleable#TextView_maxLines
*/
@android.view.RemotableViewMethod
- public void setMaxLines(int maxlines) {
- mMaximum = maxlines;
+ public void setMaxLines(int maxLines) {
+ mMaximum = maxLines;
mMaxMode = LINES;
requestLayout();
@@ -3760,10 +3791,14 @@
}
/**
- * @return the maximum number of lines displayed in this TextView, or -1 if the maximum
- * height was set in pixels instead using {@link #setMaxHeight(int) or #setHeight(int)}.
+ * Returns the maximum height of TextView in terms of number of lines or -1 if the
+ * maximum height was set using {@link #setMaxHeight(int)} or {@link #setHeight(int)}.
+ *
+ * @return the maximum height of TextView in terms of number of lines. -1 if the maximum height
+ * is not defined in lines.
*
* @see #setMaxLines(int)
+ * @see #setLines(int)
*
* @attr ref android.R.styleable#TextView_maxLines
*/
@@ -3772,16 +3807,22 @@
}
/**
- * Makes the TextView at most this many pixels tall. This option is mutually exclusive with the
- * {@link #setMaxLines(int)} method.
+ * Sets the height of the TextView to be at most {@code maxPixels} tall.
+ * <p>
+ * This value is used for height calculation if LayoutParams does not force TextView to have an
+ * exact height. Setting this value overrides previous maximum height configurations such as
+ * {@link #setMaxLines(int)} or {@link #setLines(int)}.
*
- * Setting this value overrides any other (maximum) number of lines setting.
+ * @param maxPixels the maximum height of TextView in terms of pixels
+ *
+ * @see #getMaxHeight()
+ * @see #setHeight(int)
*
* @attr ref android.R.styleable#TextView_maxHeight
*/
@android.view.RemotableViewMethod
- public void setMaxHeight(int maxHeight) {
- mMaximum = maxHeight;
+ public void setMaxHeight(int maxPixels) {
+ mMaximum = maxPixels;
mMaxMode = PIXELS;
requestLayout();
@@ -3789,10 +3830,14 @@
}
/**
- * @return the maximum height of this TextView expressed in pixels, or -1 if the maximum
- * height was set in number of lines instead using {@link #setMaxLines(int) or #setLines(int)}.
+ * Returns the maximum height of TextView in terms of pixels or -1 if the maximum height was
+ * set using {@link #setMaxLines(int)} or {@link #setLines(int)}.
+ *
+ * @return the maximum height of TextView in terms of pixels or -1 if the maximum height
+ * is not defined in pixels
*
* @see #setMaxHeight(int)
+ * @see #setHeight(int)
*
* @attr ref android.R.styleable#TextView_maxHeight
*/
@@ -3801,10 +3846,16 @@
}
/**
- * Makes the TextView exactly this many lines tall.
+ * Sets the height of the TextView to be exactly {@code lines} tall.
+ * <p>
+ * This value is used for height calculation if LayoutParams does not force TextView to have an
+ * exact height. Setting this value overrides previous minimum/maximum height configurations
+ * such as {@link #setMinLines(int)} or {@link #setMaxLines(int)}. {@link #setSingleLine()} will
+ * set this value to 1.
*
- * Note that setting this value overrides any other (minimum / maximum) number of lines or
- * height setting. A single line TextView will set this value to 1.
+ * @param lines the exact height of the TextView in terms of lines
+ *
+ * @see #setHeight(int)
*
* @attr ref android.R.styleable#TextView_lines
*/
@@ -3818,12 +3869,15 @@
}
/**
- * Makes the TextView exactly this many pixels tall.
- * You could do the same thing by specifying this number in the
- * LayoutParams.
+ * Sets the height of the TextView to be exactly <code>pixels</code> tall.
+ * <p>
+ * This value is used for height calculation if LayoutParams does not force TextView to have an
+ * exact height. Setting this value overrides previous minimum/maximum height configurations
+ * such as {@link #setMinHeight(int)} or {@link #setMaxHeight(int)}.
*
- * Note that setting this value overrides any other (minimum / maximum) number of lines or
- * height setting.
+ * @param pixels the exact height of the TextView in terms of pixels
+ *
+ * @see #setLines(int)
*
* @attr ref android.R.styleable#TextView_height
*/
@@ -3837,13 +3891,22 @@
}
/**
- * Makes the TextView at least this many ems wide
+ * Sets the width of the TextView to be at least {@code minEms} wide.
+ * <p>
+ * This value is used for width calculation if LayoutParams does not force TextView to have an
+ * exact width. Setting this value overrides previous minimum width configurations such as
+ * {@link #setMinWidth(int)} or {@link #setWidth(int)}.
+ *
+ * @param minEms the minimum width of TextView in terms of ems
+ *
+ * @see #getMinEms()
+ * @see #setEms(int)
*
* @attr ref android.R.styleable#TextView_minEms
*/
@android.view.RemotableViewMethod
- public void setMinEms(int minems) {
- mMinWidth = minems;
+ public void setMinEms(int minEms) {
+ mMinWidth = minEms;
mMinWidthMode = EMS;
requestLayout();
@@ -3851,8 +3914,11 @@
}
/**
- * @return the minimum width of the TextView, expressed in ems or -1 if the minimum width
- * was set in pixels instead (using {@link #setMinWidth(int)} or {@link #setWidth(int)}).
+ * Returns the minimum width of TextView in terms of ems or -1 if the minimum width was set
+ * using {@link #setMinWidth(int)} or {@link #setWidth(int)}.
+ *
+ * @return the minimum width of TextView in terms of ems. -1 if the minimum width is not
+ * defined in ems
*
* @see #setMinEms(int)
* @see #setEms(int)
@@ -3864,13 +3930,26 @@
}
/**
- * Makes the TextView at least this many pixels wide
+ * Sets the width of the TextView to be at least {@code minPixels} wide.
+ * <p>
+ * This value is used for width calculation if LayoutParams does not force TextView to have an
+ * exact width. Setting this value overrides previous minimum width configurations such as
+ * {@link #setMinEms(int)} or {@link #setEms(int)}.
+ * <p>
+ * The value given here is different than {@link #setMinimumWidth(int)}. Between
+ * {@code minWidth} and the value set in {@link #setMinimumWidth(int)}, the greater one is used
+ * to decide the final width.
+ *
+ * @param minPixels the minimum width of TextView in terms of pixels
+ *
+ * @see #getMinWidth()
+ * @see #setWidth(int)
*
* @attr ref android.R.styleable#TextView_minWidth
*/
@android.view.RemotableViewMethod
- public void setMinWidth(int minpixels) {
- mMinWidth = minpixels;
+ public void setMinWidth(int minPixels) {
+ mMinWidth = minPixels;
mMinWidthMode = PIXELS;
requestLayout();
@@ -3878,8 +3957,11 @@
}
/**
- * @return the minimum width of the TextView, in pixels or -1 if the minimum width
- * was set in ems instead (using {@link #setMinEms(int)} or {@link #setEms(int)}).
+ * Returns the minimum width of TextView in terms of pixels or -1 if the minimum width was set
+ * using {@link #setMinEms(int)} or {@link #setEms(int)}.
+ *
+ * @return the minimum width of TextView in terms of pixels or -1 if the minimum width is not
+ * defined in pixels
*
* @see #setMinWidth(int)
* @see #setWidth(int)
@@ -3891,13 +3973,22 @@
}
/**
- * Makes the TextView at most this many ems wide
+ * Sets the width of the TextView to be at most {@code maxEms} wide.
+ * <p>
+ * This value is used for width calculation if LayoutParams does not force TextView to have an
+ * exact width. Setting this value overrides previous maximum width configurations such as
+ * {@link #setMaxWidth(int)} or {@link #setWidth(int)}.
+ *
+ * @param maxEms the maximum width of TextView in terms of ems
+ *
+ * @see #getMaxEms()
+ * @see #setEms(int)
*
* @attr ref android.R.styleable#TextView_maxEms
*/
@android.view.RemotableViewMethod
- public void setMaxEms(int maxems) {
- mMaxWidth = maxems;
+ public void setMaxEms(int maxEms) {
+ mMaxWidth = maxEms;
mMaxWidthMode = EMS;
requestLayout();
@@ -3905,8 +3996,11 @@
}
/**
- * @return the maximum width of the TextView, expressed in ems or -1 if the maximum width
- * was set in pixels instead (using {@link #setMaxWidth(int)} or {@link #setWidth(int)}).
+ * Returns the maximum width of TextView in terms of ems or -1 if the maximum width was set
+ * using {@link #setMaxWidth(int)} or {@link #setWidth(int)}.
+ *
+ * @return the maximum width of TextView in terms of ems or -1 if the maximum width is not
+ * defined in ems
*
* @see #setMaxEms(int)
* @see #setEms(int)
@@ -3918,13 +4012,22 @@
}
/**
- * Makes the TextView at most this many pixels wide
+ * Sets the width of the TextView to be at most {@code maxPixels} wide.
+ * <p>
+ * This value is used for width calculation if LayoutParams does not force TextView to have an
+ * exact width. Setting this value overrides previous maximum width configurations such as
+ * {@link #setMaxEms(int)} or {@link #setEms(int)}.
+ *
+ * @param maxPixels the maximum width of TextView in terms of pixels
+ *
+ * @see #getMaxWidth()
+ * @see #setWidth(int)
*
* @attr ref android.R.styleable#TextView_maxWidth
*/
@android.view.RemotableViewMethod
- public void setMaxWidth(int maxpixels) {
- mMaxWidth = maxpixels;
+ public void setMaxWidth(int maxPixels) {
+ mMaxWidth = maxPixels;
mMaxWidthMode = PIXELS;
requestLayout();
@@ -3932,8 +4035,11 @@
}
/**
- * @return the maximum width of the TextView, in pixels or -1 if the maximum width
- * was set in ems instead (using {@link #setMaxEms(int)} or {@link #setEms(int)}).
+ * Returns the maximum width of TextView in terms of pixels or -1 if the maximum width was set
+ * using {@link #setMaxEms(int)} or {@link #setEms(int)}.
+ *
+ * @return the maximum width of TextView in terms of pixels. -1 if the maximum width is not
+ * defined in pixels
*
* @see #setMaxWidth(int)
* @see #setWidth(int)
@@ -3945,12 +4051,15 @@
}
/**
- * Makes the TextView exactly this many ems wide
+ * Sets the width of the TextView to be exactly {@code ems} wide.
*
- * @see #setMaxEms(int)
- * @see #setMinEms(int)
- * @see #getMinEms()
- * @see #getMaxEms()
+ * This value is used for width calculation if LayoutParams does not force TextView to have an
+ * exact width. Setting this value overrides previous minimum/maximum configurations such as
+ * {@link #setMinEms(int)} or {@link #setMaxEms(int)}.
+ *
+ * @param ems the exact width of the TextView in terms of ems
+ *
+ * @see #setWidth(int)
*
* @attr ref android.R.styleable#TextView_ems
*/
@@ -3964,14 +4073,15 @@
}
/**
- * Makes the TextView exactly this many pixels wide.
- * You could do the same thing by specifying this number in the
- * LayoutParams.
+ * Sets the width of the TextView to be exactly {@code pixels} wide.
+ * <p>
+ * This value is used for width calculation if LayoutParams does not force TextView to have an
+ * exact width. Setting this value overrides previous minimum/maximum width configurations
+ * such as {@link #setMinWidth(int)} or {@link #setMaxWidth(int)}.
*
- * @see #setMaxWidth(int)
- * @see #setMinWidth(int)
- * @see #getMinWidth()
- * @see #getMaxWidth()
+ * @param pixels the exact width of the TextView in terms of pixels
+ *
+ * @see #setEms(int)
*
* @attr ref android.R.styleable#TextView_width
*/
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 4efcb09..789e60b 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -100,13 +100,13 @@
*/
public Toast(Context context) {
mContext = context;
- mTN = new TN();
+ mTN = new TN(context.getPackageName());
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
}
-
+
/**
* Show the view for the specified duration.
*/
@@ -133,15 +133,9 @@
* after the appropriate duration.
*/
public void cancel() {
- mTN.hide();
-
- try {
- getService().cancelToast(mContext.getPackageName(), mTN);
- } catch (RemoteException e) {
- // Empty
- }
+ mTN.cancel();
}
-
+
/**
* Set the view to show.
* @see #getView
@@ -328,21 +322,37 @@
}
private static class TN extends ITransientNotification.Stub {
- final Runnable mHide = new Runnable() {
- @Override
- public void run() {
- handleHide();
- // Don't do this in handleHide() because it is also invoked by handleShow()
- mNextView = null;
- }
- };
-
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
+
+ private static final int SHOW = 0;
+ private static final int HIDE = 1;
+ private static final int CANCEL = 2;
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- IBinder token = (IBinder) msg.obj;
- handleShow(token);
+ switch (msg.what) {
+ case SHOW: {
+ IBinder token = (IBinder) msg.obj;
+ handleShow(token);
+ break;
+ }
+ case HIDE: {
+ handleHide();
+ // Don't do this in handleHide() because it is also invoked by handleShow()
+ mNextView = null;
+ break;
+ }
+ case CANCEL: {
+ handleHide();
+ // Don't do this in handleHide() because it is also invoked by handleShow()
+ mNextView = null;
+ try {
+ getService().cancelToast(mPackageName, TN.this);
+ } catch (RemoteException e) {
+ }
+ break;
+ }
+ }
}
};
@@ -358,10 +368,12 @@
WindowManager mWM;
+ String mPackageName;
+
static final long SHORT_DURATION_TIMEOUT = 4000;
static final long LONG_DURATION_TIMEOUT = 7000;
- TN() {
+ TN(String packageName) {
// XXX This should be changed to use a Dialog, with a Theme.Toast
// defined that sets up the layout params appropriately.
final WindowManager.LayoutParams params = mParams;
@@ -374,6 +386,8 @@
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+
+ mPackageName = packageName;
}
/**
@@ -382,7 +396,7 @@
@Override
public void show(IBinder windowToken) {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
- mHandler.obtainMessage(0, windowToken).sendToTarget();
+ mHandler.obtainMessage(SHOW, windowToken).sendToTarget();
}
/**
@@ -391,7 +405,12 @@
@Override
public void hide() {
if (localLOGV) Log.v(TAG, "HIDE: " + this);
- mHandler.post(mHide);
+ mHandler.obtainMessage(HIDE).sendToTarget();
+ }
+
+ public void cancel() {
+ if (localLOGV) Log.v(TAG, "CANCEL: " + this);
+ mHandler.obtainMessage(CANCEL).sendToTarget();
}
public void handleShow(IBinder windowToken) {
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
new file mode 100644
index 0000000..cf1bf62
--- /dev/null
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.hardware;
+
+import com.android.internal.R;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+public class AmbientDisplayConfiguration {
+
+ private final Context mContext;
+
+ public AmbientDisplayConfiguration(Context context) {
+ mContext = context;
+ }
+
+ public boolean enabled(int user) {
+ return pulseOnNotificationEnabled(user)
+ || pulseOnPickupEnabled(user)
+ || pulseOnDoubleTapEnabled(user);
+ }
+
+ public boolean available() {
+ return pulseOnNotificationAvailable() || pulseOnPickupAvailable()
+ || pulseOnDoubleTapAvailable();
+ }
+
+ public boolean pulseOnNotificationEnabled(int user) {
+ return boolSetting(Settings.Secure.DOZE_ENABLED, user) && pulseOnNotificationAvailable();
+ }
+
+ public boolean pulseOnNotificationAvailable() {
+ return ambientDisplayAvailable();
+ }
+
+ public boolean pulseOnPickupEnabled(int user) {
+ return boolSetting(Settings.Secure.DOZE_PULSE_ON_PICK_UP, user)
+ && pulseOnPickupAvailable();
+ }
+
+ public boolean pulseOnPickupAvailable() {
+ return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup)
+ && ambientDisplayAvailable();
+ }
+
+ public boolean pulseOnDoubleTapEnabled(int user) {
+ return boolSetting(Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, user)
+ && pulseOnDoubleTapAvailable();
+ }
+
+ public boolean pulseOnDoubleTapAvailable() {
+ return !TextUtils.isEmpty(doubleTapSensorType()) && ambientDisplayAvailable();
+ }
+
+ public String doubleTapSensorType() {
+ return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType);
+ }
+
+ public String ambientDisplayComponent() {
+ return mContext.getResources().getString(R.string.config_dozeComponent);
+ }
+
+ private boolean ambientDisplayAvailable() {
+ return !TextUtils.isEmpty(ambientDisplayComponent());
+ }
+
+ private boolean boolSetting(String name, int user) {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(), name, 1, user) != 0;
+ }
+
+}
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index 2ed7aa2..11dd0e8 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -16,6 +16,15 @@
package com.android.internal.os;
+import android.net.LocalSocket;
+import android.os.Build;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+
/**
* Startup class for the WebView zygote process.
*
@@ -26,7 +35,48 @@
class WebViewZygoteInit {
public static final String TAG = "WebViewZygoteInit";
+ private static ZygoteServer sServer;
+
+ private static class WebViewZygoteServer extends ZygoteServer {
+ @Override
+ protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
+ throws IOException {
+ return new WebViewZygoteConnection(socket, abiList);
+ }
+ }
+
+ private static class WebViewZygoteConnection extends ZygoteConnection {
+ WebViewZygoteConnection(LocalSocket socket, String abiList) throws IOException {
+ super(socket, abiList);
+ }
+
+ @Override
+ protected boolean handlePreloadPackage(String packagePath, String libsPath) {
+ // TODO: Use preload information to setup the ClassLoader.
+ return false;
+ }
+ }
+
public static void main(String argv[]) {
- throw new RuntimeException("Not implemented yet");
+ sServer = new WebViewZygoteServer();
+
+ // Zygote goes into its own process group.
+ try {
+ Os.setpgid(0, 0);
+ } catch (ErrnoException ex) {
+ throw new RuntimeException("Failed to setpgid(0,0)", ex);
+ }
+
+ try {
+ sServer.registerServerSocket("webview_zygote");
+ sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
+ sServer.closeServerSocket();
+ } catch (Zygote.MethodAndArgsCaller caller) {
+ caller.run();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Fatal exception:", e);
+ }
+
+ System.exit(0);
}
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index b8fa034..7edc938 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -44,6 +44,7 @@
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import libcore.io.IoUtils;
/**
@@ -170,6 +171,11 @@
return handleAbiListQuery();
}
+ if (parsedArgs.preloadPackage != null) {
+ return handlePreloadPackage(parsedArgs.preloadPackage,
+ parsedArgs.preloadPackageLibs);
+ }
+
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
throw new ZygoteSecurityException("Client may not specify capabilities: " +
"permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -271,6 +277,10 @@
}
}
+ protected boolean handlePreloadPackage(String packagePath, String libsPath) {
+ throw new RuntimeException("Zyogte does not support package preloading");
+ }
+
/**
* Closes socket associated with this connection.
*/
@@ -376,6 +386,12 @@
String appDataDir;
/**
+ * Whether to preload a package, with the package path in the remainingArgs.
+ */
+ String preloadPackage;
+ String preloadPackageLibs;
+
+ /**
* Constructs instance and parses args
* @param args zygote command-line args
* @throws IllegalArgumentException
@@ -533,6 +549,9 @@
instructionSet = arg.substring(arg.indexOf('=') + 1);
} else if (arg.startsWith("--app-data-dir=")) {
appDataDir = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.equals("--preload-package")) {
+ preloadPackage = args[++curArg];
+ preloadPackageLibs = args[++curArg];
} else {
break;
}
@@ -542,6 +561,11 @@
if (args.length - curArg > 0) {
throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
}
+ } else if (preloadPackage != null) {
+ if (args.length - curArg > 0) {
+ throw new IllegalArgumentException(
+ "Unexpected arguments after --preload-package.");
+ }
} else {
if (!seenRuntimeArgs) {
throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index ab876410..126d9e7 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -19,6 +19,7 @@
import static android.system.OsConstants.POLLIN;
import android.net.LocalServerSocket;
+import android.net.LocalSocket;
import android.system.Os;
import android.system.ErrnoException;
import android.system.StructPollfd;
@@ -80,13 +81,18 @@
*/
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
- return new ZygoteConnection(mServerSocket.accept(), abiList);
+ return createNewConnection(mServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
+ protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
+ throws IOException {
+ return new ZygoteConnection(socket, abiList);
+ }
+
/**
* Close and clean up zygote sockets. Called on shutdown and on the
* child's exit path.
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
new file mode 100644
index 0000000..793b228
--- /dev/null
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.view.Gravity;
+import android.view.ViewConfiguration;
+import android.widget.Scroller;
+
+import java.util.ArrayList;
+
+/**
+ * Calculates the snap targets and the snap position for the PIP given a position and a velocity.
+ * All bounds are relative to the display top/left.
+ */
+public class PipSnapAlgorithm {
+
+ // Allows snapping to the four corners
+ private static final int SNAP_MODE_CORNERS_ONLY = 0;
+ // Allows snapping to the four corners and the mid-points on the long edge in each orientation
+ private static final int SNAP_MODE_CORNERS_AND_SIDES = 1;
+ // Allows snapping to anywhere along the edge of the screen
+ private static final int SNAP_MODE_EDGE = 2;
+
+ private static final float SCROLL_FRICTION_MULTIPLIER = 8f;
+
+ private final Context mContext;
+
+ private final ArrayList<Integer> mSnapGravities = new ArrayList<>();
+ private final int mSnapMode = SNAP_MODE_CORNERS_ONLY;
+
+ private final Scroller mScroller;
+ private final Rect mDisplayBounds = new Rect();
+ private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+
+ public PipSnapAlgorithm(Context context, int displayId) {
+ final DisplayManager displayManager =
+ (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ final ViewConfiguration viewConfig = ViewConfiguration.get(context);
+ final Point displaySize = new Point();
+ displayManager.getDisplay(displayId).getRealSize(displaySize);
+ mContext = context;
+ mDisplayBounds.set(0, 0, displaySize.x, displaySize.y);
+ mOrientation = context.getResources().getConfiguration().orientation;
+ mScroller = new Scroller(context);
+ mScroller.setFriction(viewConfig.getScrollFriction() * SCROLL_FRICTION_MULTIPLIER);
+ calculateSnapTargets();
+ }
+
+ /**
+ * @return the closest absolute snap stack bounds for the given {@param stackBounds} moving at
+ * the given {@param velocityX} and {@param velocityY}. The {@param movementBounds} should be
+ * those for the given {@param stackBounds}.
+ */
+ public Rect findClosestSnapBounds(Rect movementBounds, Rect stackBounds, float velocityX,
+ float velocityY) {
+ final Rect finalStackBounds = new Rect(stackBounds);
+ mScroller.fling(stackBounds.left, stackBounds.top,
+ (int) velocityX, (int) velocityY,
+ movementBounds.left, movementBounds.right,
+ movementBounds.top, movementBounds.bottom);
+ finalStackBounds.offsetTo(mScroller.getFinalX(), mScroller.getFinalY());
+ mScroller.abortAnimation();
+ return findClosestSnapBounds(movementBounds, finalStackBounds);
+ }
+
+ /**
+ * @return the closest absolute snap stack bounds for the given {@param stackBounds}. The
+ * {@param movementBounds} should be those for the given {@param stackBounds}.
+ */
+ public Rect findClosestSnapBounds(Rect movementBounds, Rect stackBounds) {
+ final Rect pipBounds = new Rect(movementBounds.left, movementBounds.top,
+ movementBounds.right + stackBounds.width(),
+ movementBounds.bottom + stackBounds.height());
+ final Rect newBounds = new Rect(stackBounds);
+ if (mSnapMode == SNAP_MODE_EDGE) {
+ // Find the closest edge to the given stack bounds and snap to it
+ final int fromLeft = stackBounds.left - movementBounds.left;
+ final int fromTop = stackBounds.top - movementBounds.top;
+ final int fromRight = movementBounds.right - stackBounds.left;
+ final int fromBottom = movementBounds.bottom - stackBounds.top;
+ if (fromLeft <= fromTop && fromLeft <= fromRight && fromLeft <= fromBottom) {
+ newBounds.offset(-fromLeft, 0);
+ } else if (fromTop <= fromLeft && fromTop <= fromRight && fromTop <= fromBottom) {
+ newBounds.offset(0, -fromTop);
+ } else if (fromRight < fromLeft && fromRight < fromTop && fromRight < fromBottom) {
+ newBounds.offset(fromRight, 0);
+ } else {
+ newBounds.offset(0, fromBottom);
+ }
+ } else {
+ // Find the closest snap point
+ final Rect tmpBounds = new Rect();
+ final Point[] snapTargets = new Point[mSnapGravities.size()];
+ for (int i = 0; i < mSnapGravities.size(); i++) {
+ Gravity.apply(mSnapGravities.get(i), stackBounds.width(), stackBounds.height(),
+ pipBounds, 0, 0, tmpBounds);
+ snapTargets[i] = new Point(tmpBounds.left, tmpBounds.top);
+ }
+ Point snapTarget = findClosestPoint(stackBounds.left, stackBounds.top, snapTargets);
+ newBounds.offsetTo(snapTarget.x, snapTarget.y);
+ }
+ return newBounds;
+ }
+
+ /**
+ * @return the closest point in {@param points} to the given {@param x} and {@param y}.
+ */
+ private Point findClosestPoint(int x, int y, Point[] points) {
+ Point closestPoint = null;
+ float minDistance = Float.MAX_VALUE;
+ for (Point p : points) {
+ float distance = distanceToPoint(p, x, y);
+ if (distance < minDistance) {
+ closestPoint = p;
+ minDistance = distance;
+ }
+ }
+ return closestPoint;
+ }
+
+ /**
+ * @return the distance between point {@param p} and the given {@param x} and {@param y}.
+ */
+ private float distanceToPoint(Point p, int x, int y) {
+ return PointF.length(p.x - x, p.y - y);
+ }
+
+ /**
+ * Calculate the snap targets for the discrete snap modes.
+ */
+ private void calculateSnapTargets() {
+ mSnapGravities.clear();
+ switch (mSnapMode) {
+ case SNAP_MODE_CORNERS_AND_SIDES:
+ if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ mSnapGravities.add(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
+ mSnapGravities.add(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
+ } else {
+ mSnapGravities.add(Gravity.CENTER_VERTICAL | Gravity.LEFT);
+ mSnapGravities.add(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
+ }
+ // Fall through
+ case SNAP_MODE_CORNERS_ONLY:
+ mSnapGravities.add(Gravity.TOP | Gravity.LEFT);
+ mSnapGravities.add(Gravity.TOP | Gravity.RIGHT);
+ mSnapGravities.add(Gravity.BOTTOM | Gravity.LEFT);
+ mSnapGravities.add(Gravity.BOTTOM | Gravity.RIGHT);
+ break;
+ default:
+ // Skip otherwise
+ break;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/util/TokenBucket.java b/core/java/com/android/internal/util/TokenBucket.java
new file mode 100644
index 0000000..effb82b
--- /dev/null
+++ b/core/java/com/android/internal/util/TokenBucket.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.os.SystemClock;
+
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
+import static com.android.internal.util.Preconditions.checkArgumentPositive;
+
+/**
+ * A class useful for rate-limiting or throttling that stores and distributes tokens.
+ *
+ * A TokenBucket starts with a fixed capacity of tokens, an initial amount of tokens, and
+ * a fixed filling period (in milliseconds).
+ *
+ * For every filling period, the bucket gains one token, up to its maximum capacity from
+ * which point tokens simply overflow and are lost. Tokens can be obtained one by one or n by n.
+ *
+ * The available amount of tokens is computed lazily when the bucket state is inspected.
+ * Therefore it is purely synchronous and does not involve any asynchronous activity.
+ * It is not synchronized in any way and not a thread-safe object.
+ */
+public class TokenBucket {
+
+ private final int mFillDelta; // Time in ms it takes to generate one token.
+ private final int mCapacity; // Maximum number of tokens that can be stored.
+ private long mLastFill; // Last time in ms the bucket generated tokens.
+ private int mAvailable; // Current number of available tokens.
+
+ /**
+ * Create a new TokenBucket.
+ * @param deltaMs the time in milliseconds it takes to generate a new token.
+ * Must be strictly positive.
+ * @param capacity the maximum token capacity. Must be strictly positive.
+ * @param tokens the starting amount of token. Must be positive or zero.
+ */
+ public TokenBucket(int deltaMs, int capacity, int tokens) {
+ mFillDelta = checkArgumentPositive(deltaMs, "deltaMs must be strictly positive");
+ mCapacity = checkArgumentPositive(capacity, "capacity must be strictly positive");
+ mAvailable = Math.min(checkArgumentNonnegative(tokens), mCapacity);
+ mLastFill = scaledTime();
+ }
+
+ /**
+ * Create a new TokenBucket that starts completely filled.
+ * @param deltaMs the time in milliseconds it takes to generate a new token.
+ * Must be strictly positive.
+ * @param capacity the maximum token capacity. Must be strictly positive.
+ */
+ public TokenBucket(int deltaMs, int capacity) {
+ this(deltaMs, capacity, capacity);
+ }
+
+ /** Reset this TokenBucket and set its number of available tokens. */
+ public void reset(int tokens) {
+ checkArgumentNonnegative(tokens);
+ mAvailable = Math.min(tokens, mCapacity);
+ mLastFill = scaledTime();
+ }
+
+ /** Returns this TokenBucket maximum token capacity. */
+ public int capacity() {
+ return mCapacity;
+ }
+
+ /** Returns this TokenBucket currently number of available tokens. */
+ public int available() {
+ fill();
+ return mAvailable;
+ }
+
+ /** Returns true if this TokenBucket as one or more tokens available. */
+ public boolean has() {
+ fill();
+ return mAvailable > 0;
+ }
+
+ /** Consumes a token from this TokenBucket and returns true if a token is available. */
+ public boolean get() {
+ return (get(1) == 1);
+ }
+
+ /**
+ * Try to consume many tokens from this TokenBucket.
+ * @param n the number of tokens to consume.
+ * @return the number of tokens that were actually consumed.
+ */
+ public int get(int n) {
+ fill();
+ if (n <= 0) {
+ return 0;
+ }
+ if (n > mAvailable) {
+ int got = mAvailable;
+ mAvailable = 0;
+ return got;
+ }
+ mAvailable -= n;
+ return n;
+ }
+
+ private void fill() {
+ final long now = scaledTime();
+ final int diff = (int) (now - mLastFill);
+ mAvailable = Math.min(mCapacity, mAvailable + diff);
+ mLastFill = now;
+ }
+
+ private long scaledTime() {
+ return SystemClock.elapsedRealtime() / mFillDelta;
+ }
+}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 0e07bf8..71252fb 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.PasswordMetrics;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
import android.content.ComponentName;
@@ -137,10 +138,6 @@
private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
private static final String IS_TRUST_USUALLY_MANAGED = "lockscreen.istrustusuallymanaged";
- // Maximum allowed number of repeated or ordered characters in a sequence before we'll
- // consider it a complex PIN/password.
- public static final int MAX_ALLOWED_SEQUENCE = 3;
-
public static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
public static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
@@ -593,8 +590,7 @@
setCredentialRequiredToDecrypt(false);
}
- getDevicePolicyManager().setActivePasswordState(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userHandle);
+ getDevicePolicyManager().setActivePasswordState(new PasswordMetrics(), userHandle);
onAfterChangingPassword(userHandle);
}
@@ -665,8 +661,8 @@
setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
- dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
- pattern.size(), 0, 0, 0, 0, 0, 0, userId);
+ dpm.setActivePasswordState(new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern.size()), userId);
onAfterChangingPassword(userId);
} catch (RemoteException re) {
Log.e(TAG, "Couldn't save lock pattern " + re);
@@ -736,96 +732,6 @@
return getDeviceOwnerInfo() != null;
}
- /**
- * Compute the password quality from the given password string.
- */
- static public int computePasswordQuality(String password) {
- boolean hasDigit = false;
- boolean hasNonDigit = false;
- final int len = password.length();
- for (int i = 0; i < len; i++) {
- if (Character.isDigit(password.charAt(i))) {
- hasDigit = true;
- } else {
- hasNonDigit = true;
- }
- }
-
- if (hasNonDigit && hasDigit) {
- return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
- }
- if (hasNonDigit) {
- return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
- }
- if (hasDigit) {
- return maxLengthSequence(password) > MAX_ALLOWED_SEQUENCE
- ? DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
- : DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
- }
- return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
- }
-
- private static int categoryChar(char c) {
- if ('a' <= c && c <= 'z') return 0;
- if ('A' <= c && c <= 'Z') return 1;
- if ('0' <= c && c <= '9') return 2;
- return 3;
- }
-
- private static int maxDiffCategory(int category) {
- if (category == 0 || category == 1) return 1;
- else if (category == 2) return 10;
- return 0;
- }
-
- /*
- * Returns the maximum length of a sequential characters. A sequence is defined as
- * monotonically increasing characters with a constant interval or the same character repeated.
- *
- * For example:
- * maxLengthSequence("1234") == 4
- * maxLengthSequence("1234abc") == 4
- * maxLengthSequence("aabc") == 3
- * maxLengthSequence("qwertyuio") == 1
- * maxLengthSequence("@ABC") == 3
- * maxLengthSequence(";;;;") == 4 (anything that repeats)
- * maxLengthSequence(":;<=>") == 1 (ordered, but not composed of alphas or digits)
- *
- * @param string the pass
- * @return the number of sequential letters or digits
- */
- public static int maxLengthSequence(String string) {
- if (string.length() == 0) return 0;
- char previousChar = string.charAt(0);
- int category = categoryChar(previousChar); //current category of the sequence
- int diff = 0; //difference between two consecutive characters
- boolean hasDiff = false; //if we are currently targeting a sequence
- int maxLength = 0; //maximum length of a sequence already found
- int startSequence = 0; //where the current sequence started
- for (int current = 1; current < string.length(); current++) {
- char currentChar = string.charAt(current);
- int categoryCurrent = categoryChar(currentChar);
- int currentDiff = (int) currentChar - (int) previousChar;
- if (categoryCurrent != category || Math.abs(currentDiff) > maxDiffCategory(category)) {
- maxLength = Math.max(maxLength, current - startSequence);
- startSequence = current;
- hasDiff = false;
- category = categoryCurrent;
- }
- else {
- if(hasDiff && currentDiff != diff) {
- maxLength = Math.max(maxLength, current - startSequence);
- startSequence = current - 1;
- }
- diff = currentDiff;
- hasDiff = true;
- }
- previousChar = currentChar;
- }
- maxLength = Math.max(maxLength, string.length() - startSequence);
- return maxLength;
- }
-
/** Update the encryption password if it is enabled **/
private void updateEncryptionPassword(final int type, final String password) {
if (!isDeviceEncryptionEnabled()) {
@@ -871,7 +777,8 @@
getLockSettings().setLockPassword(password, savedPassword, userHandle);
getLockSettings().setSeparateProfileChallengeEnabled(userHandle, true, null);
- int computedQuality = computePasswordQuality(password);
+ final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
+ final int computedQuality = metrics.quality;
// Update the device encryption password.
if (userHandle == UserHandle.USER_SYSTEM
@@ -891,36 +798,11 @@
setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
- int letters = 0;
- int uppercase = 0;
- int lowercase = 0;
- int numbers = 0;
- int symbols = 0;
- int nonletter = 0;
- for (int i = 0; i < password.length(); i++) {
- char c = password.charAt(i);
- if (c >= 'A' && c <= 'Z') {
- letters++;
- uppercase++;
- } else if (c >= 'a' && c <= 'z') {
- letters++;
- lowercase++;
- } else if (c >= '0' && c <= '9') {
- numbers++;
- nonletter++;
- } else {
- symbols++;
- nonletter++;
- }
- }
- dpm.setActivePasswordState(Math.max(quality, computedQuality),
- password.length(), letters, uppercase, lowercase,
- numbers, symbols, nonletter, userHandle);
+ metrics.quality = Math.max(quality, metrics.quality);
+ dpm.setActivePasswordState(metrics, userHandle);
} else {
// The password is not anything.
- dpm.setActivePasswordState(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
- 0, 0, 0, 0, 0, 0, 0, userHandle);
+ dpm.setActivePasswordState(new PasswordMetrics(), userHandle);
}
// Add the password to the password history. We assume all
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 7950685..506a284 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -119,12 +119,15 @@
android/graphics/DrawFilter.cpp \
android/graphics/FontFamily.cpp \
android/graphics/CreateJavaOutputStreamAdaptor.cpp \
+ android/graphics/GIFMovie.cpp \
android/graphics/Graphics.cpp \
android/graphics/HarfBuzzNGFaceSkia.cpp \
android/graphics/Interpolator.cpp \
android/graphics/MaskFilter.cpp \
android/graphics/Matrix.cpp \
android/graphics/Movie.cpp \
+ android/graphics/MovieImpl.cpp \
+ android/graphics/Movie_FactoryDefault.cpp \
android/graphics/NinePatch.cpp \
android/graphics/NinePatchPeeker.cpp \
android/graphics/Paint.cpp \
@@ -200,6 +203,7 @@
$(TOP)/system/core/include \
$(TOP)/system/media/camera/include \
$(TOP)/system/netd/include \
+ external/giflib \
external/pdfium/core/include/fpdfapi \
external/pdfium/fpdfsdk/include \
external/pdfium/public \
@@ -219,6 +223,9 @@
external/freetype/include
# TODO: clean up Minikin so it doesn't need the freetype include
+LOCAL_STATIC_LIBRARIES := \
+ libgif \
+
LOCAL_SHARED_LIBRARIES := \
libmemtrack \
libandroidfw \
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 9ed1588..467ec37 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -17,7 +17,7 @@
#include "android_nio_utils.h"
#include "CreateJavaOutputStreamAdaptor.h"
#include <hwui/Paint.h>
-#include <hwui/PixelRef.h>
+#include <hwui/Bitmap.h>
#include <renderthread/RenderProxy.h>
#include "core_jni_helpers.h"
@@ -37,25 +37,28 @@
namespace android {
-class Bitmap {
+class BitmapWrapper {
public:
- Bitmap(PixelRef* pixelRef)
- : mPixelRef(pixelRef) { }
+ BitmapWrapper(Bitmap* bitmap)
+ : mBitmap(bitmap) { }
void freePixels() {
- mInfo = mPixelRef->info();
- mHasHardwareMipMap = mPixelRef->hasHardwareMipMap();
- mAllocationSize = mPixelRef->getAllocationByteCount();
- mRowBytes = mPixelRef->rowBytes();
- mGenerationId = mPixelRef->getGenerationID();
- mPixelRef.reset();
+ mInfo = mBitmap->info();
+ mHasHardwareMipMap = mBitmap->hasHardwareMipMap();
+ mAllocationSize = mBitmap->getAllocationByteCount();
+ mRowBytes = mBitmap->rowBytes();
+ mGenerationId = mBitmap->getGenerationID();
+ mBitmap.reset();
}
bool valid() {
- return !!mPixelRef;
+ return mBitmap;
}
- PixelRef* pixelRef() { return mPixelRef.get(); }
+ Bitmap& bitmap() {
+ assertValid();
+ return *mBitmap;
+ }
void assertValid() {
LOG_ALWAYS_FATAL_IF(!valid(), "Error, cannot access an invalid/free'd bitmap here!");
@@ -63,58 +66,58 @@
void getSkBitmap(SkBitmap* outBitmap) {
assertValid();
- mPixelRef->getSkBitmap(outBitmap);
+ mBitmap->getSkBitmap(outBitmap);
}
bool hasHardwareMipMap() {
- if (mPixelRef) {
- return mPixelRef->hasHardwareMipMap();
+ if (mBitmap) {
+ return mBitmap->hasHardwareMipMap();
}
return mHasHardwareMipMap;
}
void setHasHardwareMipMap(bool hasMipMap) {
assertValid();
- mPixelRef->setHasHardwareMipMap(hasMipMap);
+ mBitmap->setHasHardwareMipMap(hasMipMap);
}
void setAlphaType(SkAlphaType alphaType) {
assertValid();
- mPixelRef->setAlphaType(alphaType);
+ mBitmap->setAlphaType(alphaType);
}
const SkImageInfo& info() {
- if (mPixelRef) {
- return mPixelRef->info();
+ if (mBitmap) {
+ return mBitmap->info();
}
return mInfo;
}
size_t getAllocationByteCount() const {
- if (mPixelRef) {
- return mPixelRef->getAllocationByteCount();
+ if (mBitmap) {
+ return mBitmap->getAllocationByteCount();
}
return mAllocationSize;
}
size_t rowBytes() const {
- if (mPixelRef) {
- return mPixelRef->rowBytes();
+ if (mBitmap) {
+ return mBitmap->rowBytes();
}
return mRowBytes;
}
uint32_t getGenerationID() const {
- if (mPixelRef) {
- return mPixelRef->getGenerationID();
+ if (mBitmap) {
+ return mBitmap->getGenerationID();
}
return mGenerationId;
}
- ~Bitmap() { }
+ ~BitmapWrapper() { }
private:
- sk_sp<PixelRef> mPixelRef;
+ sk_sp<Bitmap> mBitmap;
SkImageInfo mInfo;
bool mHasHardwareMipMap;
size_t mAllocationSize;
@@ -127,22 +130,22 @@
class LocalScopedBitmap {
public:
explicit LocalScopedBitmap(jlong bitmapHandle)
- : mBitmap(reinterpret_cast<Bitmap*>(bitmapHandle)) {}
+ : mBitmapWrapper(reinterpret_cast<BitmapWrapper*>(bitmapHandle)) {}
- Bitmap* operator->() {
- return mBitmap;
+ BitmapWrapper* operator->() {
+ return mBitmapWrapper;
}
void* pixels() {
- return mBitmap->pixelRef()->pixels();
+ return mBitmapWrapper->bitmap().pixels();
}
bool valid() {
- return mBitmap && mBitmap->valid();
+ return mBitmapWrapper && mBitmapWrapper->valid();
}
private:
- Bitmap* mBitmap;
+ BitmapWrapper* mBitmapWrapper;
};
namespace bitmap {
@@ -175,18 +178,18 @@
return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID);
}
-jobject createBitmap(JNIEnv* env, PixelRef* pixelRef,
+jobject createBitmap(JNIEnv* env, Bitmap* bitmap,
int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets,
int density) {
bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable;
bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied;
// The caller needs to have already set the alpha type properly, so the
// native SkBitmap stays in sync with the Java Bitmap.
- assert_premultiplied(pixelRef->info(), isPremultiplied);
- Bitmap* bitmap = new Bitmap(pixelRef);
+ assert_premultiplied(bitmap->info(), isPremultiplied);
+ BitmapWrapper* bitmapWrapper = new BitmapWrapper(bitmap);
jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
- reinterpret_cast<jlong>(bitmap), pixelRef->width(), pixelRef->height(), density, isMutable,
- isPremultiplied, ninePatchChunk, ninePatchInsets);
+ reinterpret_cast<jlong>(bitmapWrapper), bitmap->width(), bitmap->height(), density,
+ isMutable, isPremultiplied, ninePatchChunk, ninePatchInsets);
if (env->ExceptionCheck() != 0) {
ALOGE("*** Uncaught exception returned from Java call!\n");
@@ -200,14 +203,19 @@
bitmap->getSkBitmap(outBitmap);
}
-PixelRef* toPixelRef(JNIEnv* env, jobject bitmap) {
+Bitmap& toBitmap(JNIEnv* env, jobject bitmap) {
SkASSERT(env);
SkASSERT(bitmap);
SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
LocalScopedBitmap localBitmap(bitmapHandle);
- localBitmap->assertValid();
- return localBitmap->pixelRef();
+ return localBitmap->bitmap();
+}
+
+Bitmap& toBitmap(JNIEnv* env, jlong bitmapHandle) {
+ SkASSERT(env);
+ LocalScopedBitmap localBitmap(bitmapHandle);
+ return localBitmap->bitmap();
}
} // namespace bitmap
@@ -548,7 +556,7 @@
bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType,
GraphicsJNI::defaultColorSpace()));
- PixelRef* nativeBitmap = GraphicsJNI::allocateHeapPixelRef(&bitmap, NULL);
+ sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap, NULL);
if (!nativeBitmap) {
return NULL;
}
@@ -558,14 +566,13 @@
0, 0, width, height, bitmap);
}
- return createBitmap(env, nativeBitmap,
- getPremulBitmapCreateFlags(isMutable));
+ return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable));
}
static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
jint dstConfigHandle, jboolean isMutable) {
SkBitmap src;
- reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
+ reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src);
SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
SkBitmap result;
HeapAllocator allocator;
@@ -573,41 +580,41 @@
if (!src.copyTo(&result, dstCT, &allocator)) {
return NULL;
}
- auto pixelRef = allocator.getStorageObjAndReset();
- return createBitmap(env, pixelRef, getPremulBitmapCreateFlags(isMutable));
+ auto bitmap = allocator.getStorageObjAndReset();
+ return createBitmap(env, bitmap, getPremulBitmapCreateFlags(isMutable));
}
-static PixelRef* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& dstCT) {
+static Bitmap* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& dstCT) {
SkBitmap result;
AshmemPixelAllocator allocator(env);
if (!src.copyTo(&result, dstCT, &allocator)) {
return NULL;
}
- auto pixelRef = allocator.getStorageObjAndReset();
- pixelRef->setImmutable();
- return pixelRef;
+ auto bitmap = allocator.getStorageObjAndReset();
+ bitmap->setImmutable();
+ return bitmap;
}
static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) {
SkBitmap src;
- reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
+ reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src);
SkColorType dstCT = src.colorType();
- auto pixelRef = Bitmap_copyAshmemImpl(env, src, dstCT);
- jobject ret = createBitmap(env, pixelRef, getPremulBitmapCreateFlags(false));
+ auto bitmap = Bitmap_copyAshmemImpl(env, src, dstCT);
+ jobject ret = createBitmap(env, bitmap, getPremulBitmapCreateFlags(false));
return ret;
}
static jobject Bitmap_copyAshmemConfig(JNIEnv* env, jobject, jlong srcHandle, jint dstConfigHandle) {
SkBitmap src;
- reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
+ reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src);
SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
- auto pixelRef = Bitmap_copyAshmemImpl(env, src, dstCT);
- jobject ret = createBitmap(env, pixelRef, getPremulBitmapCreateFlags(false));
+ auto bitmap = Bitmap_copyAshmemImpl(env, src, dstCT);
+ jobject ret = createBitmap(env, bitmap, getPremulBitmapCreateFlags(false));
return ret;
}
-static void Bitmap_destruct(Bitmap* bitmap) {
+static void Bitmap_destruct(BitmapWrapper* bitmap) {
delete bitmap;
}
@@ -647,7 +654,7 @@
// Otherwise respect the premultiplied request.
alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
}
- bitmap->pixelRef()->reconfigure(SkImageInfo::Make(width, height, colorType, alphaType,
+ bitmap->bitmap().reconfigure(SkImageInfo::Make(width, height, colorType, alphaType,
sk_sp<SkColorSpace>(bitmap->info().colorSpace())));
}
@@ -832,7 +839,7 @@
}
// Map the bitmap in place from the ashmem region if possible otherwise copy.
- PixelRef* nativeBitmap;
+ sk_sp<Bitmap> nativeBitmap;
if (blob.fd() >= 0 && (blob.isMutable() || !isMutable) && (size >= ASHMEM_BITMAP_MIN_SIZE)) {
#if DEBUG_PARCEL
ALOGD("Bitmap.createFromParcel: mapped contents of %s bitmap from %s blob "
@@ -853,8 +860,8 @@
}
// Map the pixels in place and take ownership of the ashmem region.
- nativeBitmap = GraphicsJNI::mapAshmemPixelRef(env, bitmap.get(),
- ctable, dupFd, const_cast<void*>(blob.data()), size, !isMutable);
+ nativeBitmap = sk_sp<Bitmap>(GraphicsJNI::mapAshmemBitmap(env, bitmap.get(),
+ ctable, dupFd, const_cast<void*>(blob.data()), size, !isMutable));
SkSafeUnref(ctable);
if (!nativeBitmap) {
close(dupFd);
@@ -880,7 +887,7 @@
#endif
// Copy the pixels into a new buffer.
- nativeBitmap = GraphicsJNI::allocateHeapPixelRef(bitmap.get(), ctable);
+ nativeBitmap = Bitmap::allocateHeapBitmap(bitmap.get(), ctable);
SkSafeUnref(ctable);
if (!nativeBitmap) {
blob.release();
@@ -895,7 +902,7 @@
blob.release();
}
- return createBitmap(env, nativeBitmap,
+ return createBitmap(env, nativeBitmap.release(),
getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);
}
@@ -911,8 +918,8 @@
android::Parcel* p = android::parcelForJavaObject(env, parcel);
SkBitmap bitmap;
- auto androidBitmap = reinterpret_cast<Bitmap*>(bitmapHandle);
- androidBitmap->getSkBitmap(&bitmap);
+ auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle);
+ bitmapWrapper->getSkBitmap(&bitmap);
sk_sp<SkColorSpace> sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
bool isSRGB = bitmap.colorSpace() == sRGB.get();
@@ -942,7 +949,7 @@
// Transfer the underlying ashmem region if we have one and it's immutable.
android::status_t status;
- int fd = androidBitmap->pixelRef()->getAshmemFd();
+ int fd = bitmapWrapper->bitmap().getAshmemFd();
if (fd >= 0 && !isMutable && p->allowFds()) {
#if DEBUG_PARCEL
ALOGD("Bitmap.writeToParcel: transferring immutable bitmap's ashmem fd as "
@@ -992,7 +999,7 @@
jlong srcHandle, jlong paintHandle,
jintArray offsetXY) {
SkBitmap src;
- reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
+ reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src);
const android::Paint* paint = reinterpret_cast<android::Paint*>(paintHandle);
SkIPoint offset;
SkBitmap dst;
@@ -1021,7 +1028,7 @@
static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
jint x, jint y) {
SkBitmap bitmap;
- reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+ reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
SkAutoLockPixels alp(bitmap);
ToColorProc proc = ChooseToColorProc(bitmap);
@@ -1042,7 +1049,7 @@
jintArray pixelArray, jint offset, jint stride,
jint x, jint y, jint width, jint height) {
SkBitmap bitmap;
- reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+ reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
SkAutoLockPixels alp(bitmap);
ToColorProc proc = ChooseToColorProc(bitmap);
@@ -1070,7 +1077,7 @@
static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
jint x, jint y, jint colorHandle) {
SkBitmap bitmap;
- reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+ reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
SkColor color = static_cast<SkColor>(colorHandle);
SkAutoLockPixels alp(bitmap);
if (NULL == bitmap.getPixels()) {
@@ -1090,7 +1097,7 @@
jintArray pixelArray, jint offset, jint stride,
jint x, jint y, jint width, jint height) {
SkBitmap bitmap;
- reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+ reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
x, y, width, height, bitmap);
}
@@ -1098,7 +1105,7 @@
static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
jlong bitmapHandle, jobject jbuffer) {
SkBitmap bitmap;
- reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+ reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
SkAutoLockPixels alp(bitmap);
const void* src = bitmap.getPixels();
@@ -1113,7 +1120,7 @@
static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
jlong bitmapHandle, jobject jbuffer) {
SkBitmap bitmap;
- reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+ reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
SkAutoLockPixels alp(bitmap);
void* dst = bitmap.getPixels();
@@ -1129,8 +1136,8 @@
jlong bm1Handle) {
SkBitmap bm0;
SkBitmap bm1;
- reinterpret_cast<Bitmap*>(bm0Handle)->getSkBitmap(&bm0);
- reinterpret_cast<Bitmap*>(bm1Handle)->getSkBitmap(&bm1);
+ reinterpret_cast<BitmapWrapper*>(bm0Handle)->getSkBitmap(&bm0);
+ reinterpret_cast<BitmapWrapper*>(bm1Handle)->getSkBitmap(&bm1);
if (bm0.width() != bm1.width() ||
bm0.height() != bm1.height() ||
bm0.colorType() != bm1.colorType() ||
@@ -1190,17 +1197,15 @@
static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) {
LocalScopedBitmap bitmap(bitmapHandle);
- SkPixelRef* pixelRef = bitmap->pixelRef();
- SkSafeRef(pixelRef);
- return reinterpret_cast<jlong>(pixelRef);
+ SkPixelRef& pixelRef = bitmap->bitmap();
+ pixelRef.ref();
+ return reinterpret_cast<jlong>(&pixelRef);
}
static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) {
LocalScopedBitmap bitmapHandle(bitmapPtr);
if (!bitmapHandle.valid()) return;
- SkBitmap bitmap;
- bitmapHandle->getSkBitmap(&bitmap);
- android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmap);
+ android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmapHandle->bitmap());
}
static jint Bitmap_getAllocationByteCount(JNIEnv* env, jobject, jlong bitmapPtr) {
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index 588a99c..387a128 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -24,7 +24,7 @@
namespace android {
-class PixelRef;
+class Bitmap;
namespace bitmap {
@@ -34,14 +34,15 @@
kBitmapCreateFlag_Premultiplied = 0x2,
};
-jobject createBitmap(JNIEnv* env, PixelRef* bitmap,
+jobject createBitmap(JNIEnv* env, Bitmap* bitmap,
int bitmapCreateFlags, jbyteArray ninePatchChunk = NULL,
jobject ninePatchInsets = NULL, int density = -1);
void toSkBitmap(jlong bitmapHandle, SkBitmap* outBitmap);
-PixelRef* toPixelRef(JNIEnv* env, jobject bitmap);
+Bitmap& toBitmap(JNIEnv* env, jobject bitmap);
+Bitmap& toBitmap(JNIEnv* env, jlong bitmapHandle);
/** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in
sync with isPremultiplied
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 5c24585..bc2da91 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -162,7 +162,7 @@
class RecyclingPixelAllocator : public SkBitmap::Allocator {
public:
- RecyclingPixelAllocator(android::PixelRef* bitmap, unsigned int size)
+ RecyclingPixelAllocator(android::Bitmap* bitmap, unsigned int size)
: mBitmap(bitmap), mSize(size) {
}
@@ -200,7 +200,7 @@
}
private:
- android::PixelRef* const mBitmap;
+ android::Bitmap* const mBitmap;
const unsigned int mSize;
};
@@ -327,10 +327,10 @@
scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f);
}
- android::PixelRef* reuseBitmap = nullptr;
+ android::Bitmap* reuseBitmap = nullptr;
unsigned int existingBufferSize = 0;
if (javaBitmap != NULL) {
- reuseBitmap = bitmap::toPixelRef(env, javaBitmap);
+ reuseBitmap = &bitmap::toBitmap(env, javaBitmap);
if (reuseBitmap->isImmutable()) {
ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
javaBitmap = NULL;
@@ -497,7 +497,7 @@
// kSrc_Mode instructs us to overwrite the uninitialized pixels in
// outputBitmap. Otherwise we would blend by default, which is not
// what we want.
- paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ paint.setBlendMode(SkBlendMode::kSrc);
paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering
SkCanvas canvas(outputBitmap);
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 7d0915b..45bf702 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -148,10 +148,10 @@
}
// Recycle a bitmap if possible.
- android::PixelRef* recycledBitmap = nullptr;
+ android::Bitmap* recycledBitmap = nullptr;
size_t recycledBytes = 0;
if (javaBitmap) {
- recycledBitmap = bitmap::toPixelRef(env, javaBitmap);
+ recycledBitmap = &bitmap::toBitmap(env, javaBitmap);
if (recycledBitmap->isImmutable()) {
ALOGW("Warning: Reusing an immutable bitmap as an image decoder target.");
}
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index 1a86e5f..79439e2 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -21,7 +21,6 @@
#include "SkColorFilter.h"
#include "SkColorMatrixFilter.h"
-#include "SkXfermode.h"
#include <Caches.h>
@@ -33,16 +32,16 @@
public:
static void finalizer(JNIEnv* env, jobject clazz, jlong skFilterHandle) {
SkColorFilter* filter = reinterpret_cast<SkColorFilter *>(skFilterHandle);
- if (filter) SkSafeUnref(filter);
+ SkSafeUnref(filter);
}
static jlong CreatePorterDuffFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) {
- SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(modeHandle);
- return reinterpret_cast<jlong>(SkColorFilter::CreateModeFilter(srcColor, mode));
+ SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
+ return reinterpret_cast<jlong>(SkColorFilter::MakeModeFilter(srcColor, mode).release());
}
static jlong CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) {
- return reinterpret_cast<jlong>(SkColorMatrixFilter::CreateLightingFilter(mul, add));
+ return reinterpret_cast<jlong>(SkColorMatrixFilter::MakeLightingFilter(mul, add).release());
}
static jlong CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) {
@@ -50,7 +49,7 @@
const float* src = autoArray.ptr();
#ifdef SK_SCALAR_IS_FLOAT
- return reinterpret_cast<jlong>(SkColorMatrixFilter::Create(src));
+ return reinterpret_cast<jlong>(SkColorFilter::MakeMatrixFilterRowMajor255(src).release());
#else
SkASSERT(false);
#endif
diff --git a/core/jni/android/graphics/GIFMovie.cpp b/core/jni/android/graphics/GIFMovie.cpp
new file mode 100644
index 0000000..035417e
--- /dev/null
+++ b/core/jni/android/graphics/GIFMovie.cpp
@@ -0,0 +1,451 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "Movie.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+#include "gif_lib.h"
+
+#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
+#define DGifCloseFile(a, b) DGifCloseFile(a)
+#endif
+
+class GIFMovie : public Movie {
+public:
+ GIFMovie(SkStream* stream);
+ virtual ~GIFMovie();
+
+protected:
+ virtual bool onGetInfo(Info*);
+ virtual bool onSetTime(SkMSec);
+ virtual bool onGetBitmap(SkBitmap*);
+
+private:
+ GifFileType* fGIF;
+ int fCurrIndex;
+ int fLastDrawIndex;
+ SkBitmap fBackup;
+ SkColor fPaintingColor;
+};
+
+static int Decode(GifFileType* fileType, GifByteType* out, int size) {
+ SkStream* stream = (SkStream*) fileType->UserData;
+ return (int) stream->read(out, size);
+}
+
+GIFMovie::GIFMovie(SkStream* stream)
+{
+#if GIFLIB_MAJOR < 5
+ fGIF = DGifOpen( stream, Decode );
+#else
+ fGIF = DGifOpen( stream, Decode, nullptr );
+#endif
+ if (nullptr == fGIF)
+ return;
+
+ if (DGifSlurp(fGIF) != GIF_OK)
+ {
+ DGifCloseFile(fGIF, nullptr);
+ fGIF = nullptr;
+ }
+ fCurrIndex = -1;
+ fLastDrawIndex = -1;
+ fPaintingColor = SkPackARGB32(0, 0, 0, 0);
+}
+
+GIFMovie::~GIFMovie()
+{
+ if (fGIF)
+ DGifCloseFile(fGIF, nullptr);
+}
+
+static SkMSec savedimage_duration(const SavedImage* image)
+{
+ for (int j = 0; j < image->ExtensionBlockCount; j++)
+ {
+ if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
+ {
+ SkASSERT(image->ExtensionBlocks[j].ByteCount >= 4);
+ const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
+ return ((b[2] << 8) | b[1]) * 10;
+ }
+ }
+ return 0;
+}
+
+bool GIFMovie::onGetInfo(Info* info)
+{
+ if (nullptr == fGIF)
+ return false;
+
+ SkMSec dur = 0;
+ for (int i = 0; i < fGIF->ImageCount; i++)
+ dur += savedimage_duration(&fGIF->SavedImages[i]);
+
+ info->fDuration = dur;
+ info->fWidth = fGIF->SWidth;
+ info->fHeight = fGIF->SHeight;
+ info->fIsOpaque = false; // how to compute?
+ return true;
+}
+
+bool GIFMovie::onSetTime(SkMSec time)
+{
+ if (nullptr == fGIF)
+ return false;
+
+ SkMSec dur = 0;
+ for (int i = 0; i < fGIF->ImageCount; i++)
+ {
+ dur += savedimage_duration(&fGIF->SavedImages[i]);
+ if (dur >= time)
+ {
+ fCurrIndex = i;
+ return fLastDrawIndex != fCurrIndex;
+ }
+ }
+ fCurrIndex = fGIF->ImageCount - 1;
+ return true;
+}
+
+static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
+ int transparent, int width)
+{
+ for (; width > 0; width--, src++, dst++) {
+ if (*src != transparent) {
+ const GifColorType& col = cmap->Colors[*src];
+ *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
+ }
+ }
+}
+
+#if GIFLIB_MAJOR < 5
+static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
+ const ColorMapObject* cmap, int transparent, int copyWidth,
+ int copyHeight, const GifImageDesc& imageDesc, int rowStep,
+ int startRow)
+{
+ int row;
+ // every 'rowStep'th row, starting with row 'startRow'
+ for (row = startRow; row < copyHeight; row += rowStep) {
+ uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
+ copyLine(dst, src, cmap, transparent, copyWidth);
+ src += imageDesc.Width;
+ }
+
+ // pad for rest height
+ src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
+}
+
+static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
+ int transparent)
+{
+ int width = bm->width();
+ int height = bm->height();
+ GifWord copyWidth = frame->ImageDesc.Width;
+ if (frame->ImageDesc.Left + copyWidth > width) {
+ copyWidth = width - frame->ImageDesc.Left;
+ }
+
+ GifWord copyHeight = frame->ImageDesc.Height;
+ if (frame->ImageDesc.Top + copyHeight > height) {
+ copyHeight = height - frame->ImageDesc.Top;
+ }
+
+ // deinterlace
+ const unsigned char* src = (unsigned char*)frame->RasterBits;
+
+ // group 1 - every 8th row, starting with row 0
+ copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);
+
+ // group 2 - every 8th row, starting with row 4
+ copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);
+
+ // group 3 - every 4th row, starting with row 2
+ copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);
+
+ copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
+}
+#endif
+
+static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
+ int transparent)
+{
+ int width = bm->width();
+ int height = bm->height();
+ const unsigned char* src = (unsigned char*)frame->RasterBits;
+ uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
+ GifWord copyWidth = frame->ImageDesc.Width;
+ if (frame->ImageDesc.Left + copyWidth > width) {
+ copyWidth = width - frame->ImageDesc.Left;
+ }
+
+ GifWord copyHeight = frame->ImageDesc.Height;
+ if (frame->ImageDesc.Top + copyHeight > height) {
+ copyHeight = height - frame->ImageDesc.Top;
+ }
+
+ for (; copyHeight > 0; copyHeight--) {
+ copyLine(dst, src, cmap, transparent, copyWidth);
+ src += frame->ImageDesc.Width;
+ dst += width;
+ }
+}
+
+static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
+ uint32_t col)
+{
+ int bmWidth = bm->width();
+ int bmHeight = bm->height();
+ uint32_t* dst = bm->getAddr32(left, top);
+ GifWord copyWidth = width;
+ if (left + copyWidth > bmWidth) {
+ copyWidth = bmWidth - left;
+ }
+
+ GifWord copyHeight = height;
+ if (top + copyHeight > bmHeight) {
+ copyHeight = bmHeight - top;
+ }
+
+ for (; copyHeight > 0; copyHeight--) {
+ sk_memset32(dst, col, copyWidth);
+ dst += bmWidth;
+ }
+}
+
+static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
+{
+ int transparent = -1;
+
+ for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
+ ExtensionBlock* eb = frame->ExtensionBlocks + i;
+ if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
+ eb->ByteCount == 4) {
+ bool has_transparency = ((eb->Bytes[0] & 1) == 1);
+ if (has_transparency) {
+ transparent = (unsigned char)eb->Bytes[3];
+ }
+ }
+ }
+
+ if (frame->ImageDesc.ColorMap != nullptr) {
+ // use local color table
+ cmap = frame->ImageDesc.ColorMap;
+ }
+
+ if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
+ SkDEBUGFAIL("bad colortable setup");
+ return;
+ }
+
+#if GIFLIB_MAJOR < 5
+ // before GIFLIB 5, de-interlacing wasn't done by library at load time
+ if (frame->ImageDesc.Interlace) {
+ blitInterlace(bm, frame, cmap, transparent);
+ return;
+ }
+#endif
+
+ blitNormal(bm, frame, cmap, transparent);
+}
+
+static bool checkIfWillBeCleared(const SavedImage* frame)
+{
+ for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
+ ExtensionBlock* eb = frame->ExtensionBlocks + i;
+ if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
+ eb->ByteCount == 4) {
+ // check disposal method
+ int disposal = ((eb->Bytes[0] >> 2) & 7);
+ if (disposal == 2 || disposal == 3) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
+{
+ *trans = false;
+ *disposal = 0;
+ for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
+ ExtensionBlock* eb = frame->ExtensionBlocks + i;
+ if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
+ eb->ByteCount == 4) {
+ *trans = ((eb->Bytes[0] & 1) == 1);
+ *disposal = ((eb->Bytes[0] >> 2) & 7);
+ }
+ }
+}
+
+// return true if area of 'target' is completely covers area of 'covered'
+static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
+{
+ if (target->ImageDesc.Left <= covered->ImageDesc.Left
+ && covered->ImageDesc.Left + covered->ImageDesc.Width <=
+ target->ImageDesc.Left + target->ImageDesc.Width
+ && target->ImageDesc.Top <= covered->ImageDesc.Top
+ && covered->ImageDesc.Top + covered->ImageDesc.Height <=
+ target->ImageDesc.Top + target->ImageDesc.Height) {
+ return true;
+ }
+ return false;
+}
+
+static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
+ SkBitmap* backup, SkColor color)
+{
+ // We can skip disposal process if next frame is not transparent
+ // and completely covers current area
+ bool curTrans;
+ int curDisposal;
+ getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
+ bool nextTrans;
+ int nextDisposal;
+ getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
+ if ((curDisposal == 2 || curDisposal == 3)
+ && (nextTrans || !checkIfCover(next, cur))) {
+ switch (curDisposal) {
+ // restore to background color
+ // -> 'background' means background under this image.
+ case 2:
+ fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
+ cur->ImageDesc.Width, cur->ImageDesc.Height,
+ color);
+ break;
+
+ // restore to previous
+ case 3:
+ bm->swap(*backup);
+ break;
+ }
+ }
+
+ // Save current image if next frame's disposal method == 3
+ if (nextDisposal == 3) {
+ const uint32_t* src = bm->getAddr32(0, 0);
+ uint32_t* dst = backup->getAddr32(0, 0);
+ int cnt = bm->width() * bm->height();
+ memcpy(dst, src, cnt*sizeof(uint32_t));
+ }
+}
+
+bool GIFMovie::onGetBitmap(SkBitmap* bm)
+{
+ const GifFileType* gif = fGIF;
+ if (nullptr == gif)
+ return false;
+
+ if (gif->ImageCount < 1) {
+ return false;
+ }
+
+ const int width = gif->SWidth;
+ const int height = gif->SHeight;
+ if (width <= 0 || height <= 0) {
+ return false;
+ }
+
+ // no need to draw
+ if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
+ return true;
+ }
+
+ int startIndex = fLastDrawIndex + 1;
+ if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
+ // first time
+
+ startIndex = 0;
+
+ // create bitmap
+ if (!bm->tryAllocN32Pixels(width, height)) {
+ return false;
+ }
+ // create bitmap for backup
+ if (!fBackup.tryAllocN32Pixels(width, height)) {
+ return false;
+ }
+ } else if (startIndex > fCurrIndex) {
+ // rewind to 1st frame for repeat
+ startIndex = 0;
+ }
+
+ int lastIndex = fCurrIndex;
+ if (lastIndex < 0) {
+ // first time
+ lastIndex = 0;
+ } else if (lastIndex > fGIF->ImageCount - 1) {
+ // this block must not be reached.
+ lastIndex = fGIF->ImageCount - 1;
+ }
+
+ SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
+ if (gif->SColorMap != nullptr) {
+ const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor];
+ bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
+ }
+
+ // draw each frames - not intelligent way
+ for (int i = startIndex; i <= lastIndex; i++) {
+ const SavedImage* cur = &fGIF->SavedImages[i];
+ if (i == 0) {
+ bool trans;
+ int disposal;
+ getTransparencyAndDisposalMethod(cur, &trans, &disposal);
+ if (!trans && gif->SColorMap != nullptr) {
+ fPaintingColor = bgColor;
+ } else {
+ fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
+ }
+
+ bm->eraseColor(fPaintingColor);
+ fBackup.eraseColor(fPaintingColor);
+ } else {
+ // Dispose previous frame before move to next frame.
+ const SavedImage* prev = &fGIF->SavedImages[i-1];
+ disposeFrameIfNeeded(bm, prev, cur, &fBackup, fPaintingColor);
+ }
+
+ // Draw frame
+ // We can skip this process if this index is not last and disposal
+ // method == 2 or method == 3
+ if (i == lastIndex || !checkIfWillBeCleared(cur)) {
+ drawFrame(bm, cur, gif->SColorMap);
+ }
+ }
+
+ // save index
+ fLastDrawIndex = lastIndex;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+Movie* Factory(SkStreamRewindable* stream) {
+ char buf[GIF_STAMP_LEN];
+ if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
+ if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+ memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+ memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
+ // must rewind here, since our construct wants to re-read the data
+ stream->rewind();
+ return new GIFMovie(stream);
+ }
+ }
+ return nullptr;
+}
+
+static SkTRegistry<Movie*(*)(SkStreamRewindable*)> gReg(Factory);
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 82b70fc..dffb63c 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -337,13 +337,13 @@
}
void GraphicsJNI::getSkBitmap(JNIEnv* env, jobject bitmap, SkBitmap* outBitmap) {
- android::bitmap::toPixelRef(env, bitmap)->getSkBitmap(outBitmap);
+ android::bitmap::toBitmap(env, bitmap).getSkBitmap(outBitmap);
}
-SkPixelRef* GraphicsJNI::refSkPixelRef(JNIEnv* env, jobject bitmap) {
- SkPixelRef* pixelRef = android::bitmap::toPixelRef(env, bitmap);
- pixelRef->ref();
- return pixelRef;
+SkPixelRef* GraphicsJNI::refSkPixelRef(JNIEnv* env, jobject jbitmap) {
+ android::Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap);
+ bitmap.ref();
+ return &bitmap;
}
SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) {
SkASSERT(env);
@@ -399,161 +399,9 @@
return obj;
}
-static JNIEnv* vm2env(JavaVM* vm)
-{
- JNIEnv* env = NULL;
- if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK || NULL == env)
- {
- SkDebugf("------- [%p] vm->GetEnv() failed\n", vm);
- sk_throw();
- }
- return env;
-}
-
///////////////////////////////////////////////////////////////////////////////
-static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) {
- int32_t rowBytes32 = SkToS32(bitmap.rowBytes());
- int64_t bigSize = (int64_t)bitmap.height() * rowBytes32;
- if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
- return false; // allocation will be too large
- }
-
- *size = sk_64_asS32(bigSize);
- return true;
-}
-
-android::PixelRef* GraphicsJNI::allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
- const SkImageInfo& info = bitmap->info();
- if (info.colorType() == kUnknown_SkColorType) {
- LOG_ALWAYS_FATAL("unknown bitmap configuration");
- return nullptr;
- }
-
- size_t size;
- if (!computeAllocationSize(*bitmap, &size)) {
- return nullptr;
- }
-
- // we must respect the rowBytes value already set on the bitmap instead of
- // attempting to compute our own.
- const size_t rowBytes = bitmap->rowBytes();
-
- void* addr = calloc(size, 1);
- if (!addr) {
- return nullptr;
- }
-
- auto wrapper = new android::PixelRef(addr, size, info, rowBytes, ctable);
- wrapper->getSkBitmap(bitmap);
- // since we're already allocated, we lockPixels right away
- // HeapAllocator behaves this way too
- bitmap->lockPixels();
-
- return wrapper;
-}
-
-struct AndroidPixelRefContext {
- int32_t stableID;
-};
-
-static void allocatePixelsReleaseProc(void* ptr, void* ctx) {
- AndroidPixelRefContext* context = (AndroidPixelRefContext*)ctx;
- if (android::uirenderer::Caches::hasInstance()) {
- android::uirenderer::Caches::getInstance().textureCache.releaseTexture(context->stableID);
- }
-
- sk_free(ptr);
- delete context;
-}
-
-bool GraphicsJNI::allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) {
- const SkImageInfo& info = bitmap->info();
- if (info.colorType() == kUnknown_SkColorType) {
- doThrowIAE(env, "unknown bitmap configuration");
- return NULL;
- }
-
- size_t size;
- if (!computeAllocationSize(*bitmap, &size)) {
- return false;
- }
-
- // we must respect the rowBytes value already set on the bitmap instead of
- // attempting to compute our own.
- const size_t rowBytes = bitmap->rowBytes();
-
- void* addr = sk_malloc_flags(size, 0);
- if (NULL == addr) {
- return false;
- }
-
- AndroidPixelRefContext* context = new AndroidPixelRefContext;
- SkMallocPixelRef* pr = SkMallocPixelRef::NewWithProc(info, rowBytes, ctable, addr,
- &allocatePixelsReleaseProc, context);
- if (!pr) {
- delete context;
- return false;
- }
-
- // set the stableID in the context so that it can be used later in
- // allocatePixelsReleaseProc to remove the texture from the cache.
- context->stableID = pr->getStableID();
-
- bitmap->setPixelRef(pr)->unref();
- // since we're already allocated, we can lockPixels right away
- bitmap->lockPixels();
-
- return true;
-}
-
-android::PixelRef* GraphicsJNI::allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
- SkColorTable* ctable) {
- int fd;
-
- const SkImageInfo& info = bitmap->info();
- if (info.colorType() == kUnknown_SkColorType) {
- doThrowIAE(env, "unknown bitmap configuration");
- return nullptr;
- }
-
- size_t size;
- if (!computeAllocationSize(*bitmap, &size)) {
- return nullptr;
- }
-
- // we must respect the rowBytes value already set on the bitmap instead of
- // attempting to compute our own.
- const size_t rowBytes = bitmap->rowBytes();
-
- // Create new ashmem region with read/write priv
- fd = ashmem_create_region("bitmap", size);
- if (fd < 0) {
- return nullptr;
- }
-
- void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (addr == MAP_FAILED) {
- close(fd);
- return nullptr;
- }
-
- if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
- munmap(addr, size);
- close(fd);
- return nullptr;
- }
-
- auto wrapper = new android::PixelRef(addr, fd, size, info, rowBytes, ctable);
- wrapper->getSkBitmap(bitmap);
- // since we're already allocated, we lockPixels right away
- // HeapAllocator behaves this way too
- bitmap->lockPixels();
-
- return wrapper;
-}
-
-android::PixelRef* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
+android::Bitmap* GraphicsJNI::mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable, int fd, void* addr, size_t size, bool readOnly) {
const SkImageInfo& info = bitmap->info();
if (info.colorType() == kUnknown_SkColorType) {
@@ -575,7 +423,7 @@
// attempting to compute our own.
const size_t rowBytes = bitmap->rowBytes();
- auto wrapper = new android::PixelRef(addr, fd, size, info, rowBytes, ctable);
+ auto wrapper = new android::Bitmap(addr, fd, size, info, rowBytes, ctable);
wrapper->getSkBitmap(bitmap);
if (readOnly) {
bitmap->pixelRef()->setImmutable();
@@ -597,14 +445,14 @@
///////////////////////////////////////////////////////////////////////////////
bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
- mStorage.reset(GraphicsJNI::allocateHeapPixelRef(bitmap, ctable));
+ mStorage = android::Bitmap::allocateHeapBitmap(bitmap, ctable);
return !!mStorage;
}
////////////////////////////////////////////////////////////////////////////////
RecyclingClippingPixelAllocator::RecyclingClippingPixelAllocator(
- android::PixelRef* recycledBitmap, size_t recycledBytes)
+ android::Bitmap* recycledBitmap, size_t recycledBytes)
: mRecycledBitmap(recycledBitmap)
, mRecycledBytes(recycledBytes)
, mSkiaBitmap(nullptr)
@@ -702,8 +550,7 @@
}
bool AshmemPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
- JNIEnv* env = vm2env(mJavaVM);
- mStorage.reset(GraphicsJNI::allocateAshmemPixelRef(env, bitmap, ctable));
+ mStorage = android::Bitmap::allocateAshmemBitmap(bitmap, ctable);
return !!mStorage;
}
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index e5af525..aaa8db9 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -13,7 +13,7 @@
#include "SkColorSpace.h"
#include <jni.h>
#include <hwui/Canvas.h>
-#include <hwui/PixelRef.h>
+#include <hwui/Bitmap.h>
class SkBitmapRegionDecoder;
class SkCanvas;
@@ -53,7 +53,7 @@
// Given the 'native' long held by the Rasterizer.java object, return a
// ref to its SkRasterizer* (or NULL).
- static SkRasterizer* refNativeRasterizer(jlong rasterizerHandle);
+ static sk_sp<SkRasterizer> refNativeRasterizer(jlong rasterizerHandle);
/*
* LegacyBitmapConfig is the old enum in Skia that matched the enum int values
@@ -72,12 +72,7 @@
static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
- static android::PixelRef* allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
-
- static android::PixelRef* allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
- SkColorTable* ctable);
-
- static android::PixelRef* mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
+ static android::Bitmap* mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable, int fd, void* addr, size_t size, bool readOnly);
/**
@@ -109,13 +104,13 @@
/**
* Fetches the backing allocation object. Must be called!
*/
- android::PixelRef* getStorageObjAndReset() {
+ android::Bitmap* getStorageObjAndReset() {
return mStorage.release();
};
SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kYes_ZeroInitialized; }
private:
- sk_sp<android::PixelRef> mStorage;
+ sk_sp<android::Bitmap> mStorage;
};
/**
@@ -148,7 +143,7 @@
class RecyclingClippingPixelAllocator : public SkBRDAllocator {
public:
- RecyclingClippingPixelAllocator(android::PixelRef* recycledBitmap,
+ RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap,
size_t recycledBytes);
~RecyclingClippingPixelAllocator();
@@ -173,7 +168,7 @@
SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kNo_ZeroInitialized; }
private:
- android::PixelRef* mRecycledBitmap;
+ android::Bitmap* mRecycledBitmap;
const size_t mRecycledBytes;
SkBitmap* mSkiaBitmap;
bool mNeedsCopy;
@@ -184,13 +179,13 @@
explicit AshmemPixelAllocator(JNIEnv* env);
~AshmemPixelAllocator() { };
virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
- android::PixelRef* getStorageObjAndReset() {
+ android::Bitmap* getStorageObjAndReset() {
return mStorage.release();
};
private:
JavaVM* mJavaVM;
- sk_sp<android::PixelRef> mStorage;
+ sk_sp<android::Bitmap> mStorage;
};
diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp
index 2b4a1ab..3e3de5a 100644
--- a/core/jni/android/graphics/MaskFilter.cpp
+++ b/core/jni/android/graphics/MaskFilter.cpp
@@ -23,7 +23,7 @@
static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) {
SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
- SkMaskFilter* filter = SkBlurMaskFilter::Create((SkBlurStyle)blurStyle, sigma);
+ SkMaskFilter* filter = SkBlurMaskFilter::Make((SkBlurStyle)blurStyle, sigma).release();
ThrowIAE_IfNull(env, filter);
return reinterpret_cast<jlong>(filter);
}
@@ -38,8 +38,8 @@
}
SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
- SkMaskFilter* filter = SkBlurMaskFilter::CreateEmboss(sigma,
- direction, ambient, specular);
+ SkMaskFilter* filter = SkBlurMaskFilter::MakeEmboss(sigma,
+ direction, ambient, specular).release();
ThrowIAE_IfNull(env, filter);
return reinterpret_cast<jlong>(filter);
}
diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp
index 71988f9c..776ed66 100644
--- a/core/jni/android/graphics/Movie.cpp
+++ b/core/jni/android/graphics/Movie.cpp
@@ -2,7 +2,7 @@
#include "GraphicsJNI.h"
#include "ScopedLocalRef.h"
#include "SkFrontBufferedStream.h"
-#include "SkMovie.h"
+#include "Movie.h"
#include "SkStream.h"
#include "SkUtils.h"
#include "Utils.h"
@@ -19,7 +19,7 @@
static jmethodID gMovie_constructorMethodID;
static jfieldID gMovie_nativeInstanceID;
-jobject create_jmovie(JNIEnv* env, SkMovie* moov) {
+jobject create_jmovie(JNIEnv* env, Movie* moov) {
if (NULL == moov) {
return NULL;
}
@@ -27,11 +27,11 @@
static_cast<jlong>(reinterpret_cast<uintptr_t>(moov)));
}
-static SkMovie* J2Movie(JNIEnv* env, jobject movie) {
+static Movie* J2Movie(JNIEnv* env, jobject movie) {
SkASSERT(env);
SkASSERT(movie);
SkASSERT(env->IsInstanceOf(movie, gMovie_class));
- SkMovie* m = (SkMovie*)env->GetLongField(movie, gMovie_nativeInstanceID);
+ Movie* m = (Movie*)env->GetLongField(movie, gMovie_nativeInstanceID);
SkASSERT(m);
return m;
}
@@ -74,17 +74,17 @@
// therefore may be NULL.
SkASSERT(c != NULL);
- SkMovie* m = J2Movie(env, movie);
+ Movie* m = J2Movie(env, movie);
const SkBitmap& b = m->bitmap();
-
- c->drawBitmap(b, fx, fy, p);
+ sk_sp<android::Bitmap> wrapper = android::Bitmap::createFrom(b.info(), *b.pixelRef());
+ c->drawBitmap(*wrapper, fx, fy, p);
}
static jobject movie_decodeAsset(JNIEnv* env, jobject clazz, jlong native_asset) {
android::Asset* asset = reinterpret_cast<android::Asset*>(native_asset);
if (asset == NULL) return NULL;
android::AssetStreamAdaptor stream(asset);
- SkMovie* moov = SkMovie::DecodeStream(&stream);
+ Movie* moov = Movie::DecodeStream(&stream);
return create_jmovie(env, moov);
}
@@ -107,7 +107,7 @@
std::unique_ptr<SkStreamRewindable> bufferedStream(SkFrontBufferedStream::Create(strm, 6));
SkASSERT(bufferedStream.get() != NULL);
- SkMovie* moov = SkMovie::DecodeStream(bufferedStream.get());
+ Movie* moov = Movie::DecodeStream(bufferedStream.get());
return create_jmovie(env, moov);
}
@@ -124,12 +124,12 @@
}
AutoJavaByteArray ar(env, byteArray);
- SkMovie* moov = SkMovie::DecodeMemory(ar.ptr() + offset, length);
+ Movie* moov = Movie::DecodeMemory(ar.ptr() + offset, length);
return create_jmovie(env, moov);
}
static void movie_destructor(JNIEnv* env, jobject, jlong movieHandle) {
- SkMovie* movie = (SkMovie*) movieHandle;
+ Movie* movie = (Movie*) movieHandle;
delete movie;
}
diff --git a/core/jni/android/graphics/Movie.h b/core/jni/android/graphics/Movie.h
new file mode 100644
index 0000000..c0fbe4f
--- /dev/null
+++ b/core/jni/android/graphics/Movie.h
@@ -0,0 +1,78 @@
+
+/*
+ * Copyright 2008 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef Movie_DEFINED
+#define Movie_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkCanvas.h"
+
+class SkStreamRewindable;
+
+class Movie : public SkRefCnt {
+public:
+ /** Try to create a movie from the stream. If the stream format is not
+ supported, return NULL.
+ */
+ static Movie* DecodeStream(SkStreamRewindable*);
+ /** Try to create a movie from the specified file path. If the file is not
+ found, or the format is not supported, return NULL. If a movie is
+ returned, the stream may be retained by the movie (via ref()) until
+ the movie is finished with it (by calling unref()).
+ */
+ static Movie* DecodeFile(const char path[]);
+ /** Try to create a movie from the specified memory.
+ If the format is not supported, return NULL. If a movie is returned,
+ the data will have been read or copied, and so the caller may free
+ it.
+ */
+ static Movie* DecodeMemory(const void* data, size_t length);
+
+ SkMSec duration();
+ int width();
+ int height();
+ int isOpaque();
+
+ /** Specify the time code (between 0...duration) to sample a bitmap
+ from the movie. Returns true if this time code generated a different
+ bitmap/frame from the previous state (i.e. true means you need to
+ redraw).
+ */
+ bool setTime(SkMSec);
+
+ // return the right bitmap for the current time code
+ const SkBitmap& bitmap();
+
+protected:
+ struct Info {
+ SkMSec fDuration;
+ int fWidth;
+ int fHeight;
+ bool fIsOpaque;
+ };
+
+ virtual bool onGetInfo(Info*) = 0;
+ virtual bool onSetTime(SkMSec) = 0;
+ virtual bool onGetBitmap(SkBitmap*) = 0;
+
+ // visible for subclasses
+ Movie();
+
+private:
+ Info fInfo;
+ SkMSec fCurrTime;
+ SkBitmap fBitmap;
+ bool fNeedBitmap;
+
+ void ensureInfo();
+
+ typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/core/jni/android/graphics/MovieImpl.cpp b/core/jni/android/graphics/MovieImpl.cpp
new file mode 100644
index 0000000..ae9e04e
--- /dev/null
+++ b/core/jni/android/graphics/MovieImpl.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Movie.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+// We should never see this in normal operation since our time values are
+// 0-based. So we use it as a sentinal.
+#define UNINITIALIZED_MSEC ((SkMSec)-1)
+
+Movie::Movie()
+{
+ fInfo.fDuration = UNINITIALIZED_MSEC; // uninitialized
+ fCurrTime = UNINITIALIZED_MSEC; // uninitialized
+ fNeedBitmap = true;
+}
+
+void Movie::ensureInfo()
+{
+ if (fInfo.fDuration == UNINITIALIZED_MSEC && !this->onGetInfo(&fInfo))
+ memset(&fInfo, 0, sizeof(fInfo)); // failure
+}
+
+SkMSec Movie::duration()
+{
+ this->ensureInfo();
+ return fInfo.fDuration;
+}
+
+int Movie::width()
+{
+ this->ensureInfo();
+ return fInfo.fWidth;
+}
+
+int Movie::height()
+{
+ this->ensureInfo();
+ return fInfo.fHeight;
+}
+
+int Movie::isOpaque()
+{
+ this->ensureInfo();
+ return fInfo.fIsOpaque;
+}
+
+bool Movie::setTime(SkMSec time)
+{
+ SkMSec dur = this->duration();
+ if (time > dur)
+ time = dur;
+
+ bool changed = false;
+ if (time != fCurrTime)
+ {
+ fCurrTime = time;
+ changed = this->onSetTime(time);
+ fNeedBitmap |= changed;
+ }
+ return changed;
+}
+
+const SkBitmap& Movie::bitmap()
+{
+ if (fCurrTime == UNINITIALIZED_MSEC) // uninitialized
+ this->setTime(0);
+
+ if (fNeedBitmap)
+ {
+ if (!this->onGetBitmap(&fBitmap)) // failure
+ fBitmap.reset();
+ fNeedBitmap = false;
+ }
+ return fBitmap;
+}
+
+////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+Movie* Movie::DecodeMemory(const void* data, size_t length) {
+ SkMemoryStream stream(data, length, false);
+ return Movie::DecodeStream(&stream);
+}
+
+Movie* Movie::DecodeFile(const char path[]) {
+ std::unique_ptr<SkStreamRewindable> stream = SkStream::MakeFromFile(path);
+ return stream ? Movie::DecodeStream(stream.get()) : nullptr;
+}
diff --git a/core/jni/android/graphics/Movie_FactoryDefault.cpp b/core/jni/android/graphics/Movie_FactoryDefault.cpp
new file mode 100644
index 0000000..175e0a6
--- /dev/null
+++ b/core/jni/android/graphics/Movie_FactoryDefault.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Movie.h"
+#include "SkStream.h"
+
+typedef SkTRegistry<Movie*(*)(SkStreamRewindable*)> MovieReg;
+
+Movie* Movie::DecodeStream(SkStreamRewindable* stream) {
+ const MovieReg* curr = MovieReg::Head();
+ while (curr) {
+ Movie* movie = curr->factory()(stream);
+ if (movie) {
+ return movie;
+ }
+ // we must rewind only if we got nullptr, since we gave the stream to the
+ // movie, who may have already started reading from it
+ stream->rewind();
+ curr = curr->next();
+ }
+ return nullptr;
+}
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 4f2f389..91e5caf 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -26,10 +26,10 @@
#include <ResourceCache.h>
#include "SkCanvas.h"
+#include "SkLatticeIter.h"
#include "SkRegion.h"
#include "GraphicsJNI.h"
-
-#include "utils/NinePatch.h"
+#include "NinePatchUtils.h"
#include "JNIHelp.h"
#include "core_jni_helpers.h"
@@ -88,18 +88,40 @@
}
static jlong getTransparentRegion(JNIEnv* env, jobject, jobject jbitmap,
- jlong chunkHandle, jobject boundsRect) {
+ jlong chunkHandle, jobject dstRect) {
Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle);
SkASSERT(chunk);
SkASSERT(boundsRect);
SkBitmap bitmap;
GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
- SkRect bounds;
- GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
+ SkRect dst;
+ GraphicsJNI::jrect_to_rect(env, dstRect, &dst);
- SkRegion* region = NULL;
- NinePatch::Draw(NULL, bounds, bitmap, *chunk, NULL, ®ion);
+ SkCanvas::Lattice lattice;
+ SkIRect src = SkIRect::MakeWH(bitmap.width(), bitmap.height());
+ lattice.fBounds = &src;
+ NinePatchUtils::SetLatticeDivs(&lattice, *chunk, bitmap.width(), bitmap.height());
+ lattice.fFlags = nullptr;
+
+ SkRegion* region = nullptr;
+ if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) {
+ SkLatticeIter iter(lattice, dst);
+ if (iter.numRectsToDraw() == chunk->numColors) {
+ SkRect dummy;
+ SkRect iterDst;
+ int index = 0;
+ while (iter.next(&dummy, &iterDst)) {
+ if (0 == chunk->getColors()[index++] && !iterDst.isEmpty()) {
+ if (!region) {
+ region = new SkRegion();
+ }
+
+ region->op(iterDst.round(), SkRegion::kUnion_Op);
+ }
+ }
+ }
+ }
return reinterpret_cast<jlong>(region);
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 4b1530a..38452bb 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -29,6 +29,7 @@
#include "SkColorFilter.h"
#include "SkMaskFilter.h"
#include "SkPath.h"
+#include "SkPathEffect.h"
#include "SkRasterizer.h"
#include "SkShader.h"
#include "SkXfermode.h"
@@ -791,51 +792,55 @@
static jlong setShader(jlong objHandle, jlong shaderHandle) {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
- return reinterpret_cast<jlong>(obj->setShader(shader));
+ obj->setShader(sk_ref_sp(shader));
+ return reinterpret_cast<jlong>(obj->getShader());
}
static jlong setColorFilter(jlong objHandle, jlong filterHandle) {
Paint* obj = reinterpret_cast<Paint *>(objHandle);
SkColorFilter* filter = reinterpret_cast<SkColorFilter *>(filterHandle);
- return reinterpret_cast<jlong>(obj->setColorFilter(filter));
+ obj->setColorFilter(sk_ref_sp(filter));
+ return reinterpret_cast<jlong>(obj->getColorFilter());
}
static void setXfermode(jlong paintHandle, jint xfermodeHandle) {
// validate that the Java enum values match our expectations
- static_assert(0 == SkXfermode::kClear_Mode, "xfermode_mismatch");
- static_assert(1 == SkXfermode::kSrc_Mode, "xfermode_mismatch");
- static_assert(2 == SkXfermode::kDst_Mode, "xfermode_mismatch");
- static_assert(3 == SkXfermode::kSrcOver_Mode, "xfermode_mismatch");
- static_assert(4 == SkXfermode::kDstOver_Mode, "xfermode_mismatch");
- static_assert(5 == SkXfermode::kSrcIn_Mode, "xfermode_mismatch");
- static_assert(6 == SkXfermode::kDstIn_Mode, "xfermode_mismatch");
- static_assert(7 == SkXfermode::kSrcOut_Mode, "xfermode_mismatch");
- static_assert(8 == SkXfermode::kDstOut_Mode, "xfermode_mismatch");
- static_assert(9 == SkXfermode::kSrcATop_Mode, "xfermode_mismatch");
- static_assert(10 == SkXfermode::kDstATop_Mode, "xfermode_mismatch");
- static_assert(11 == SkXfermode::kXor_Mode, "xfermode_mismatch");
- static_assert(16 == SkXfermode::kDarken_Mode, "xfermode_mismatch");
- static_assert(17 == SkXfermode::kLighten_Mode, "xfermode_mismatch");
- static_assert(13 == SkXfermode::kModulate_Mode, "xfermode_mismatch");
- static_assert(14 == SkXfermode::kScreen_Mode, "xfermode_mismatch");
- static_assert(12 == SkXfermode::kPlus_Mode, "xfermode_mismatch");
- static_assert(15 == SkXfermode::kOverlay_Mode, "xfermode_mismatch");
+ static_assert(0 == static_cast<int>(SkBlendMode::kClear), "xfermode_mismatch");
+ static_assert(1 == static_cast<int>(SkBlendMode::kSrc), "xfermode_mismatch");
+ static_assert(2 == static_cast<int>(SkBlendMode::kDst), "xfermode_mismatch");
+ static_assert(3 == static_cast<int>(SkBlendMode::kSrcOver), "xfermode_mismatch");
+ static_assert(4 == static_cast<int>(SkBlendMode::kDstOver), "xfermode_mismatch");
+ static_assert(5 == static_cast<int>(SkBlendMode::kSrcIn), "xfermode_mismatch");
+ static_assert(6 == static_cast<int>(SkBlendMode::kDstIn), "xfermode_mismatch");
+ static_assert(7 == static_cast<int>(SkBlendMode::kSrcOut), "xfermode_mismatch");
+ static_assert(8 == static_cast<int>(SkBlendMode::kDstOut), "xfermode_mismatch");
+ static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "xfermode_mismatch");
+ static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "xfermode_mismatch");
+ static_assert(11 == static_cast<int>(SkBlendMode::kXor), "xfermode_mismatch");
+ static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "xfermode_mismatch");
+ static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "xfermode_mismatch");
+ static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "xfermode_mismatch");
+ static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "xfermode_mismatch");
+ static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "xfermode_mismatch");
+ static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "xfermode_mismatch");
- SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeHandle);
+ SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- paint->setXfermodeMode(mode);
+ paint->setBlendMode(mode);
}
static jlong setPathEffect(jlong objHandle, jlong effectHandle) {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkPathEffect* effect = reinterpret_cast<SkPathEffect*>(effectHandle);
- return reinterpret_cast<jlong>(obj->setPathEffect(effect));
+ obj->setPathEffect(sk_ref_sp(effect));
+ return reinterpret_cast<jlong>(obj->getPathEffect());
}
static jlong setMaskFilter(jlong objHandle, jlong maskfilterHandle) {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkMaskFilter* maskfilter = reinterpret_cast<SkMaskFilter*>(maskfilterHandle);
- return reinterpret_cast<jlong>(obj->setMaskFilter(maskfilter));
+ obj->setMaskFilter(sk_ref_sp(maskfilter));
+ return reinterpret_cast<jlong>(obj->getMaskFilter());
}
static jlong setTypeface(jlong objHandle, jlong typefaceHandle) {
@@ -845,8 +850,8 @@
static jlong setRasterizer(jlong objHandle, jlong rasterizerHandle) {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
- SkAutoTUnref<SkRasterizer> rasterizer(GraphicsJNI::refNativeRasterizer(rasterizerHandle));
- return reinterpret_cast<jlong>(obj->setRasterizer(rasterizer));
+ obj->setRasterizer(GraphicsJNI::refNativeRasterizer(rasterizerHandle));
+ return reinterpret_cast<jlong>(obj->getRasterizer());
}
static jint getTextAlign(jlong objHandle) {
@@ -940,7 +945,7 @@
}
else {
SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
- paint->setLooper(SkBlurDrawLooper::Create((SkColor)color, sigma, dx, dy))->unref();
+ paint->setLooper(SkBlurDrawLooper::Make((SkColor)color, sigma, dx, dy));
}
}
diff --git a/core/jni/android/graphics/PathEffect.cpp b/core/jni/android/graphics/PathEffect.cpp
index b289b21..e801da3 100644
--- a/core/jni/android/graphics/PathEffect.cpp
+++ b/core/jni/android/graphics/PathEffect.cpp
@@ -20,7 +20,8 @@
jlong outerHandle, jlong innerHandle) {
SkPathEffect* outer = reinterpret_cast<SkPathEffect*>(outerHandle);
SkPathEffect* inner = reinterpret_cast<SkPathEffect*>(innerHandle);
- SkPathEffect* effect = SkComposePathEffect::Create(outer, inner);
+ SkPathEffect* effect = SkComposePathEffect::Make(sk_ref_sp(outer),
+ sk_ref_sp(inner)).release();
return reinterpret_cast<jlong>(effect);
}
@@ -28,7 +29,8 @@
jlong firstHandle, jlong secondHandle) {
SkPathEffect* first = reinterpret_cast<SkPathEffect*>(firstHandle);
SkPathEffect* second = reinterpret_cast<SkPathEffect*>(secondHandle);
- SkPathEffect* effect = SkSumPathEffect::Create(first, second);
+ SkPathEffect* effect = SkSumPathEffect::Make(sk_ref_sp(first),
+ sk_ref_sp(second)).release();
return reinterpret_cast<jlong>(effect);
}
@@ -41,7 +43,7 @@
#else
#error Need to convert float array to SkScalar array before calling the following function.
#endif
- SkPathEffect* effect = SkDashPathEffect::Create(intervals, count, phase);
+ SkPathEffect* effect = SkDashPathEffect::Make(intervals, count, phase).release();
return reinterpret_cast<jlong>(effect);
}
@@ -49,19 +51,19 @@
jlong shapeHandle, jfloat advance, jfloat phase, jint style) {
const SkPath* shape = reinterpret_cast<SkPath*>(shapeHandle);
SkASSERT(shape != NULL);
- SkPathEffect* effect = SkPath1DPathEffect::Create(*shape, advance, phase,
- (SkPath1DPathEffect::Style)style);
+ SkPathEffect* effect = SkPath1DPathEffect::Make(*shape, advance, phase,
+ (SkPath1DPathEffect::Style)style).release();
return reinterpret_cast<jlong>(effect);
}
static jlong Corner_constructor(JNIEnv* env, jobject, jfloat radius){
- SkPathEffect* effect = SkCornerPathEffect::Create(radius);
+ SkPathEffect* effect = SkCornerPathEffect::Make(radius).release();
return reinterpret_cast<jlong>(effect);
}
static jlong Discrete_constructor(JNIEnv* env, jobject,
jfloat length, jfloat deviation) {
- SkPathEffect* effect = SkDiscretePathEffect::Create(length, deviation);
+ SkPathEffect* effect = SkDiscretePathEffect::Make(length, deviation).release();
return reinterpret_cast<jlong>(effect);
}
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index 07e14a2..d1ddfab 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -27,9 +27,9 @@
mWidth = src->width();
mHeight = src->height();
if (NULL != src->mPicture.get()) {
- mPicture.reset(SkRef(src->mPicture.get()));
+ mPicture = src->mPicture;
} else if (NULL != src->mRecorder.get()) {
- mPicture.reset(src->makePartialCopy());
+ mPicture = src->makePartialCopy();
}
validate();
} else {
@@ -49,7 +49,7 @@
void Picture::endRecording() {
if (NULL != mRecorder.get()) {
- mPicture.reset(mRecorder->endRecording());
+ mPicture = mRecorder->finishRecordingAsPicture();
validate();
mRecorder.reset(NULL);
}
@@ -68,9 +68,9 @@
Picture* Picture::CreateFromStream(SkStream* stream) {
Picture* newPict = new Picture;
- SkPicture* skPicture = SkPicture::CreateFromStream(stream);
+ sk_sp<SkPicture> skPicture = SkPicture::MakeFromStream(stream);
if (NULL != skPicture) {
- newPict->mPicture.reset(skPicture);
+ newPict->mPicture = skPicture;
const SkIRect cullRect = skPicture->cullRect().roundOut();
newPict->mWidth = cullRect.width();
@@ -82,16 +82,15 @@
void Picture::serialize(SkWStream* stream) const {
if (NULL != mRecorder.get()) {
- std::unique_ptr<SkPicture> tempPict(this->makePartialCopy());
- tempPict->serialize(stream);
+ this->makePartialCopy()->serialize(stream);
} else if (NULL != mPicture.get()) {
validate();
mPicture->serialize(stream);
} else {
+ // serialize "empty" picture
SkPictureRecorder recorder;
recorder.beginRecording(0, 0);
- std::unique_ptr<SkPicture> empty(recorder.endRecording());
- empty->serialize(stream);
+ recorder.finishRecordingAsPicture()->serialize(stream);
}
}
@@ -102,18 +101,18 @@
}
validate();
if (NULL != mPicture.get()) {
- mPicture.get()->playback(canvas->asSkCanvas());
+ mPicture->playback(canvas->asSkCanvas());
}
}
-SkPicture* Picture::makePartialCopy() const {
+sk_sp<SkPicture> Picture::makePartialCopy() const {
SkASSERT(NULL != mRecorder.get());
SkPictureRecorder reRecorder;
SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0);
mRecorder->partialReplay(canvas);
- return reRecorder.endRecording();
+ return reRecorder.finishRecordingAsPicture();
}
void Picture::validate() const {
diff --git a/core/jni/android/graphics/Picture.h b/core/jni/android/graphics/Picture.h
index 26a4f6a..b73b375 100644
--- a/core/jni/android/graphics/Picture.h
+++ b/core/jni/android/graphics/Picture.h
@@ -55,12 +55,12 @@
private:
int mWidth;
int mHeight;
- SkAutoTUnref<const SkPicture> mPicture;
+ sk_sp<SkPicture> mPicture;
std::unique_ptr<SkPictureRecorder> mRecorder;
// Make a copy of a picture that is in the midst of being recorded. The
// resulting picture will have balanced saves and restores.
- SkPicture* makePartialCopy() const;
+ sk_sp<SkPicture> makePartialCopy() const;
void validate() const;
};
diff --git a/core/jni/android/graphics/Rasterizer.cpp b/core/jni/android/graphics/Rasterizer.cpp
index 3784f0d..f409498 100644
--- a/core/jni/android/graphics/Rasterizer.cpp
+++ b/core/jni/android/graphics/Rasterizer.cpp
@@ -34,19 +34,19 @@
virtual ~NativeRasterizer() {}
// Can return NULL, or a ref to the skia rasterizer.
- virtual SkRasterizer* refRasterizer() { return NULL; }
+ virtual sk_sp<SkRasterizer> refRasterizer() { return NULL; }
};
class NativeLayerRasterizer : public NativeRasterizer {
public:
SkLayerRasterizer::Builder fBuilder;
- virtual SkRasterizer* refRasterizer() {
- return fBuilder.snapshotRasterizer();
+ virtual sk_sp<SkRasterizer> refRasterizer() {
+ return fBuilder.snapshot();
}
};
-SkRasterizer* GraphicsJNI::refNativeRasterizer(jlong rasterizerHandle) {
+sk_sp<SkRasterizer> GraphicsJNI::refNativeRasterizer(jlong rasterizerHandle) {
NativeRasterizer* nr = reinterpret_cast<NativeRasterizer*>(rasterizerHandle);
return nr ? nr->refRasterizer() : NULL;
}
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 03462a6..a2416df 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -79,9 +79,9 @@
if (proxyMatrix == *matrix) {
return reinterpret_cast<jlong>(currentShader.detach());
}
- return reinterpret_cast<jlong>(baseShader->newWithLocalMatrix(*matrix));
+ return reinterpret_cast<jlong>(baseShader->makeWithLocalMatrix(*matrix).release());
}
- return reinterpret_cast<jlong>(currentShader->newWithLocalMatrix(*matrix));
+ return reinterpret_cast<jlong>(currentShader->makeWithLocalMatrix(*matrix).release());
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -126,9 +126,9 @@
#error Need to convert float array to SkScalar array before calling the following function.
#endif
- SkShader* shader = SkGradientShader::CreateLinear(pts,
+ SkShader* shader = SkGradientShader::MakeLinear(pts,
reinterpret_cast<const SkColor*>(colorValues), pos, count,
- static_cast<SkShader::TileMode>(tileMode));
+ static_cast<SkShader::TileMode>(tileMode)).release();
env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
ThrowIAE_IfNull(env, shader);
@@ -147,7 +147,7 @@
colors[0] = color0;
colors[1] = color1;
- SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode);
+ SkShader* s = SkGradientShader::MakeLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode).release();
ThrowIAE_IfNull(env, s);
return reinterpret_cast<jlong>(s);
@@ -170,9 +170,9 @@
#error Need to convert float array to SkScalar array before calling the following function.
#endif
- SkShader* shader = SkGradientShader::CreateRadial(center, radius,
+ SkShader* shader = SkGradientShader::MakeRadial(center, radius,
reinterpret_cast<const SkColor*>(colorValues), pos, count,
- static_cast<SkShader::TileMode>(tileMode));
+ static_cast<SkShader::TileMode>(tileMode)).release();
env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
JNI_ABORT);
@@ -189,8 +189,8 @@
colors[0] = color0;
colors[1] = color1;
- SkShader* s = SkGradientShader::CreateRadial(center, radius, colors, NULL, 2,
- (SkShader::TileMode)tileMode);
+ SkShader* s = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2,
+ (SkShader::TileMode)tileMode).release();
ThrowIAE_IfNull(env, s);
return reinterpret_cast<jlong>(s);
}
@@ -209,8 +209,8 @@
#error Need to convert float array to SkScalar array before calling the following function.
#endif
- SkShader* shader = SkGradientShader::CreateSweep(x, y,
- reinterpret_cast<const SkColor*>(colors), pos, count);
+ SkShader* shader = SkGradientShader::MakeSweep(x, y,
+ reinterpret_cast<const SkColor*>(colors), pos, count).release();
env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors),
JNI_ABORT);
ThrowIAE_IfNull(env, shader);
@@ -222,7 +222,7 @@
SkColor colors[2];
colors[0] = color0;
colors[1] = color1;
- SkShader* s = SkGradientShader::CreateSweep(x, y, colors, NULL, 2);
+ SkShader* s = SkGradientShader::MakeSweep(x, y, colors, NULL, 2).release();
ThrowIAE_IfNull(env, s);
return reinterpret_cast<jlong>(s);
}
@@ -234,8 +234,10 @@
{
SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
- SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeHandle);
- SkShader* shader = SkShader::CreateComposeShader(shaderA, shaderB, mode);
+ SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
+ SkShader* shader = SkShader::MakeComposeShader(sk_ref_sp(shaderA),
+ sk_ref_sp(shaderB),
+ (SkXfermode::Mode)mode).release();
return reinterpret_cast<jlong>(shader);
}
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index acf9996..d233f7b 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -81,7 +81,7 @@
assert(mCurrentPage != NULL);
assert(mCurrentPage->mPictureRecorder != NULL);
assert(mCurrentPage->mPicture == NULL);
- mCurrentPage->mPicture = mCurrentPage->mPictureRecorder->endRecording();
+ mCurrentPage->mPicture = mCurrentPage->mPictureRecorder->finishRecordingAsPicture().release();
delete mCurrentPage->mPictureRecorder;
mCurrentPage->mPictureRecorder = NULL;
mCurrentPage = NULL;
diff --git a/core/jni/android/graphics/pdf/PdfEditor.cpp b/core/jni/android/graphics/pdf/PdfEditor.cpp
index b5960dd..b142925 100644
--- a/core/jni/android/graphics/pdf/PdfEditor.cpp
+++ b/core/jni/android/graphics/pdf/PdfEditor.cpp
@@ -13,6 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "PdfEditor"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android/log.h>
+#include <utils/Log.h>
#include "PdfUtils.h"
@@ -30,11 +39,6 @@
#include "SkMatrix.h"
#include <core_jni_helpers.h>
-#include <vector>
-#include <utils/Log.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <unistd.h>
namespace android {
@@ -77,8 +81,7 @@
if (errno == EINTR) {
continue;
}
- __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
- "Error writing to buffer: %d", errno);
+ ALOGE("Error writing to buffer: %d", errno);
return false;
}
remainingBytes -= writtenByteCount;
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 6431b94..fd9e714 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -45,6 +45,7 @@
#include "core_jni_helpers.h"
+#include "ScopedUtfChars.h"
#define LOG_TRACE(...)
//#define LOG_TRACE(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -264,103 +265,108 @@
ALOGD("loadNativeCode_native");
}
- const char* pathStr = env->GetStringUTFChars(path, NULL);
+ ScopedUtfChars pathStr(env, path);
std::unique_ptr<NativeCode> code;
- bool needNativeBridge = false;
+ bool needs_native_bridge = false;
+ std::string error_msg;
- void* handle = OpenNativeLibrary(env, sdkVersion, pathStr, classLoader, libraryPath);
- if (handle == NULL) {
- if (NativeBridgeIsSupported(pathStr)) {
- handle = NativeBridgeLoadLibrary(pathStr, RTLD_LAZY);
- needNativeBridge = true;
- }
+ void* handle = OpenNativeLibrary(env,
+ sdkVersion,
+ pathStr.c_str(),
+ classLoader,
+ libraryPath,
+ &needs_native_bridge,
+ &error_msg);
+
+ if (handle == nullptr) {
+ ALOGW("NativeActivity LoadNativeLibrary(\"%s\") failed: %s",
+ pathStr.c_str(),
+ error_msg.c_str());
+ return 0;
}
- env->ReleaseStringUTFChars(path, pathStr);
- if (handle != NULL) {
- void* funcPtr = NULL;
- const char* funcStr = env->GetStringUTFChars(funcName, NULL);
- if (needNativeBridge) {
- funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0);
- } else {
- funcPtr = dlsym(handle, funcStr);
- }
-
- code.reset(new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr));
- env->ReleaseStringUTFChars(funcName, funcStr);
-
- if (code->createActivityFunc == NULL) {
- ALOGW("ANativeActivity_onCreate not found");
- return 0;
- }
-
- code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue);
- if (code->messageQueue == NULL) {
- ALOGW("Unable to retrieve native MessageQueue");
- return 0;
- }
-
- int msgpipe[2];
- if (pipe(msgpipe)) {
- ALOGW("could not create pipe: %s", strerror(errno));
- return 0;
- }
- code->mainWorkRead = msgpipe[0];
- code->mainWorkWrite = msgpipe[1];
- int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
- SLOGW_IF(result != 0, "Could not make main work read pipe "
- "non-blocking: %s", strerror(errno));
- result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
- SLOGW_IF(result != 0, "Could not make main work write pipe "
- "non-blocking: %s", strerror(errno));
- code->messageQueue->getLooper()->addFd(
- code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code.get());
-
- code->ANativeActivity::callbacks = &code->callbacks;
- if (env->GetJavaVM(&code->vm) < 0) {
- ALOGW("NativeActivity GetJavaVM failed");
- return 0;
- }
- code->env = env;
- code->clazz = env->NewGlobalRef(clazz);
-
- const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
- code->internalDataPathObj = dirStr;
- code->internalDataPath = code->internalDataPathObj.string();
- env->ReleaseStringUTFChars(internalDataDir, dirStr);
-
- if (externalDataDir != NULL) {
- dirStr = env->GetStringUTFChars(externalDataDir, NULL);
- code->externalDataPathObj = dirStr;
- env->ReleaseStringUTFChars(externalDataDir, dirStr);
- }
- code->externalDataPath = code->externalDataPathObj.string();
-
- code->sdkVersion = sdkVersion;
-
- code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
-
- if (obbDir != NULL) {
- dirStr = env->GetStringUTFChars(obbDir, NULL);
- code->obbPathObj = dirStr;
- env->ReleaseStringUTFChars(obbDir, dirStr);
- }
- code->obbPath = code->obbPathObj.string();
-
- jbyte* rawSavedState = NULL;
- jsize rawSavedSize = 0;
- if (savedState != NULL) {
- rawSavedState = env->GetByteArrayElements(savedState, NULL);
- rawSavedSize = env->GetArrayLength(savedState);
- }
-
- code->createActivityFunc(code.get(), rawSavedState, rawSavedSize);
-
- if (rawSavedState != NULL) {
- env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
- }
+ void* funcPtr = NULL;
+ const char* funcStr = env->GetStringUTFChars(funcName, NULL);
+ if (needs_native_bridge) {
+ funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0);
+ } else {
+ funcPtr = dlsym(handle, funcStr);
}
-
+
+ code.reset(new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr));
+ env->ReleaseStringUTFChars(funcName, funcStr);
+
+ if (code->createActivityFunc == NULL) {
+ ALOGW("ANativeActivity_onCreate not found");
+ return 0;
+ }
+
+ code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue);
+ if (code->messageQueue == NULL) {
+ ALOGW("Unable to retrieve native MessageQueue");
+ return 0;
+ }
+
+ int msgpipe[2];
+ if (pipe(msgpipe)) {
+ ALOGW("could not create pipe: %s", strerror(errno));
+ return 0;
+ }
+ code->mainWorkRead = msgpipe[0];
+ code->mainWorkWrite = msgpipe[1];
+ int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
+ SLOGW_IF(result != 0, "Could not make main work read pipe "
+ "non-blocking: %s", strerror(errno));
+ result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
+ SLOGW_IF(result != 0, "Could not make main work write pipe "
+ "non-blocking: %s", strerror(errno));
+ code->messageQueue->getLooper()->addFd(
+ code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code.get());
+
+ code->ANativeActivity::callbacks = &code->callbacks;
+ if (env->GetJavaVM(&code->vm) < 0) {
+ ALOGW("NativeActivity GetJavaVM failed");
+ return 0;
+ }
+ code->env = env;
+ code->clazz = env->NewGlobalRef(clazz);
+
+ const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
+ code->internalDataPathObj = dirStr;
+ code->internalDataPath = code->internalDataPathObj.string();
+ env->ReleaseStringUTFChars(internalDataDir, dirStr);
+
+ if (externalDataDir != NULL) {
+ dirStr = env->GetStringUTFChars(externalDataDir, NULL);
+ code->externalDataPathObj = dirStr;
+ env->ReleaseStringUTFChars(externalDataDir, dirStr);
+ }
+ code->externalDataPath = code->externalDataPathObj.string();
+
+ code->sdkVersion = sdkVersion;
+
+ code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
+
+ if (obbDir != NULL) {
+ dirStr = env->GetStringUTFChars(obbDir, NULL);
+ code->obbPathObj = dirStr;
+ env->ReleaseStringUTFChars(obbDir, dirStr);
+ }
+ code->obbPath = code->obbPathObj.string();
+
+ jbyte* rawSavedState = NULL;
+ jsize rawSavedSize = 0;
+ if (savedState != NULL) {
+ rawSavedState = env->GetByteArrayElements(savedState, NULL);
+ rawSavedSize = env->GetArrayLength(savedState);
+ }
+
+ code->createActivityFunc(code.get(), rawSavedState, rawSavedSize);
+
+ if (rawSavedState != NULL) {
+ env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
+ }
+
return (jlong)code.release();
}
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 10645e8..04a7543 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -207,7 +207,7 @@
}
static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
- SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(modeHandle);
+ SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
get_canvas(canvasHandle)->drawColor(color, mode);
}
@@ -341,13 +341,12 @@
jlong paintHandle, jint dstDensity, jint srcDensity) {
Canvas* canvas = get_canvas(canvasHandle);
- SkBitmap skiaBitmap;
- bitmap::toSkBitmap(bitmapHandle, &skiaBitmap);
+ Bitmap& bitmap = android::bitmap::toBitmap(env, bitmapHandle);
const android::Res_png_9patch* chunk = reinterpret_cast<android::Res_png_9patch*>(chunkHandle);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
if (CC_LIKELY(dstDensity == srcDensity || dstDensity == 0 || srcDensity == 0)) {
- canvas->drawNinePatch(skiaBitmap, *chunk, left, top, right, bottom, paint);
+ canvas->drawNinePatch(bitmap, *chunk, left, top, right, bottom, paint);
} else {
canvas->save(SaveFlags::MatrixClip);
@@ -361,7 +360,7 @@
}
filteredPaint.setFilterQuality(kLow_SkFilterQuality);
- canvas->drawNinePatch(skiaBitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale,
+ canvas->drawNinePatch(bitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale,
&filteredPaint);
canvas->restore();
@@ -372,8 +371,7 @@
jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
jint screenDensity, jint bitmapDensity) {
Canvas* canvas = get_canvas(canvasHandle);
- SkBitmap bitmap;
- GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
+ Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
@@ -408,8 +406,7 @@
jlong matrixHandle, jlong paintHandle) {
const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- SkBitmap bitmap;
- GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
+ Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap);
get_canvas(canvasHandle)->drawBitmap(bitmap, *matrix, paint);
}
@@ -420,8 +417,7 @@
Canvas* canvas = get_canvas(canvasHandle);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- SkBitmap bitmap;
- GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
+ Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap);
if (screenDensity != 0 && screenDensity != bitmapDensity) {
Paint filteredPaint;
if (paint) {
@@ -446,7 +442,8 @@
GraphicsJNI::defaultColorSpace());
SkBitmap bitmap;
bitmap.setInfo(info);
- if (!GraphicsJNI::allocatePixels(env, &bitmap, NULL)) {
+ sk_sp<Bitmap> androidBitmap = Bitmap::allocateHeapBitmap(&bitmap, NULL);
+ if (!androidBitmap) {
return;
}
@@ -455,7 +452,7 @@
}
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- get_canvas(canvasHandle)->drawBitmap(bitmap, x, y, paint);
+ get_canvas(canvasHandle)->drawBitmap(*androidBitmap, x, y, paint);
}
static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
@@ -466,8 +463,7 @@
AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- SkBitmap bitmap;
- GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
+ Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap);
get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight,
vertA.ptr(), colorA.ptr(), paint);
}
diff --git a/core/jni/android_hardware_Radio.cpp b/core/jni/android_hardware_Radio.cpp
index ec6471e..42ceec4 100644
--- a/core/jni/android_hardware_Radio.cpp
+++ b/core/jni/android_hardware_Radio.cpp
@@ -245,16 +245,17 @@
radio_metadata_key_t key;
radio_metadata_type_t type;
void *value;
- unsigned int size;
+ size_t size;
if (radio_metadata_get_at_index(nMetadata, i , &key, &type, &value, &size) != 0) {
continue;
}
switch (type) {
case RADIO_METADATA_TYPE_INT: {
ALOGV("%s RADIO_METADATA_TYPE_INT %d", __FUNCTION__, key);
+ int32_t val = *(int32_t *)value;
jStatus = env->CallIntMethod(*jMetadata,
gRadioMetadataMethods.putIntFromNative,
- key, *(jint *)value);
+ key, (jint)val);
if (jStatus == 0) {
jCount++;
}
@@ -271,7 +272,7 @@
env->DeleteLocalRef(jText);
} break;
case RADIO_METADATA_TYPE_RAW: {
- ALOGV("%s RADIO_METADATA_TYPE_RAW %d size %u", __FUNCTION__, key, size);
+ ALOGV("%s RADIO_METADATA_TYPE_RAW %d size %zu", __FUNCTION__, key, size);
if (size == 0) {
break;
}
@@ -720,7 +721,7 @@
if (module == NULL) {
return RADIO_STATUS_NO_INIT;
}
- status_t status = module->tune((unsigned int)channel, (unsigned int)subChannel);
+ status_t status = module->tune((uint32_t)channel, (uint32_t)subChannel);
return (jint)status;
}
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index 7ec17bf..6814506 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -24,12 +24,17 @@
#include <usbhost/usbhost.h>
+#include <chrono>
+
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace android;
+using namespace std::chrono;
+
+static const int USB_CONTROL_READ_TIMEOUT_MS = 200;
static jfieldID field_context;
@@ -215,7 +220,7 @@
}
static jobject
-android_hardware_UsbDeviceConnection_request_wait(JNIEnv *env, jobject thiz)
+android_hardware_UsbDeviceConnection_request_wait(JNIEnv *env, jobject thiz, jint timeoutMillis)
{
struct usb_device* device = get_device_from_object(env, thiz);
if (!device) {
@@ -223,11 +228,33 @@
return NULL;
}
- struct usb_request* request = usb_request_wait(device);
- if (request)
+ struct usb_request* request;
+ if (timeoutMillis == -1) {
+ request = usb_request_wait(device, -1);
+ } else {
+ steady_clock::time_point currentTime = steady_clock::now();
+ steady_clock::time_point endTime = currentTime + std::chrono::milliseconds(timeoutMillis);
+
+ // Poll the existence of a request via usb_request_wait until we get a result, an unexpected
+ // error or time out. As several threads can listen on the same fd, we might get wakeups
+ // without data.
+ while (1) {
+ request = usb_request_wait(device, duration_cast<std::chrono::milliseconds>(endTime
+ - currentTime).count());
+
+ int error = errno;
+ currentTime = steady_clock::now();
+ if (request != NULL || error != EAGAIN || currentTime >= endTime) {
+ break;
+ }
+ };
+ }
+
+ if (request) {
return (jobject)request->client_data;
- else
+ } else {
return NULL;
+ }
}
static jstring
@@ -238,7 +265,8 @@
ALOGE("device is closed in native_get_serial");
return NULL;
}
- char* serial = usb_device_get_serial(device);
+ char* serial = usb_device_get_serial(device,
+ USB_CONTROL_READ_TIMEOUT_MS);
if (!serial)
return NULL;
jstring result = env->NewStringUTF(serial);
@@ -272,7 +300,7 @@
(void *)android_hardware_UsbDeviceConnection_control_request},
{"native_bulk_request", "(I[BIII)I",
(void *)android_hardware_UsbDeviceConnection_bulk_request},
- {"native_request_wait", "()Landroid/hardware/usb/UsbRequest;",
+ {"native_request_wait", "(I)Landroid/hardware/usb/UsbRequest;",
(void *)android_hardware_UsbDeviceConnection_request_wait},
{ "native_get_serial", "()Ljava/lang/String;",
(void*)android_hardware_UsbDeviceConnection_get_serial },
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 06f22dd..aa4570f 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -291,7 +291,8 @@
whichHeap = HEAP_TTF;
is_swappable = true;
} else if ((nameLen > 4 && strstr(name, ".dex") != NULL) ||
- (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) {
+ (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0) ||
+ (nameLen > 5 && strcmp(name+nameLen-5, ".vdex") == 0)) {
whichHeap = HEAP_DEX;
is_swappable = true;
} else if (nameLen > 4 && strcmp(name+nameLen-4, ".oat") == 0) {
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 13e3f0d..816d5df 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -196,19 +196,32 @@
}
static void JHwBinder_native_registerService(
- JNIEnv *env, jobject thiz, jstring serviceNameObj) {
+ JNIEnv *env,
+ jobject thiz,
+ jstring serviceNameObj,
+ jint versionMajor,
+ jint versionMinor) {
if (serviceNameObj == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
return;
}
+ if (versionMajor < 0
+ || versionMajor > 65535
+ || versionMinor < 0
+ || versionMinor > 65535) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", 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);
+ const hardware::hidl_version kVersion =
+ hardware::make_hidl_version(versionMajor, versionMinor);
sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
@@ -231,19 +244,32 @@
}
static jobject JHwBinder_native_getService(
- JNIEnv *env, jclass /* clazzObj */, jstring serviceNameObj) {
+ JNIEnv *env,
+ jclass /* clazzObj */,
+ jstring serviceNameObj,
+ jint versionMajor,
+ jint versionMinor) {
if (serviceNameObj == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
return NULL;
}
+ if (versionMajor < 0
+ || versionMajor > 65535
+ || versionMinor < 0
+ || versionMinor > 65535) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", 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);
+ const hardware::hidl_version kVersion =
+ hardware::make_hidl_version(versionMajor, versionMinor);
LOG(INFO) << "looking for service '"
<< String8(String16(
@@ -280,10 +306,10 @@
"(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V",
(void *)JHwBinder_native_transact },
- { "registerService", "(Ljava/lang/String;)V",
+ { "registerService", "(Ljava/lang/String;II)V",
(void *)JHwBinder_native_registerService },
- { "getService", "(Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;",
+ { "getService", "(Ljava/lang/String;II)L" PACKAGE_PATH "/IHwBinder;",
(void *)JHwBinder_native_getService },
};
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index 5c879b88..7387b29 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -888,15 +888,28 @@
{ "readString", "()Ljava/lang/String;",
(void *)JHwParcel_native_readString },
- { "readBoolVector", "()[Z", (void *)JHwParcel_native_readBoolVector },
- { "readInt8Vector", "()[B", (void *)JHwParcel_native_readInt8Vector },
- { "readInt16Vector", "()[S", (void *)JHwParcel_native_readInt16Vector },
- { "readInt32Vector", "()[I", (void *)JHwParcel_native_readInt32Vector },
- { "readInt64Vector", "()[J", (void *)JHwParcel_native_readInt64Vector },
- { "readFloatVector", "()[F", (void *)JHwParcel_native_readFloatVector },
- { "readDoubleVector", "()[D", (void *)JHwParcel_native_readDoubleVector },
+ { "readBoolVectorAsArray", "()[Z",
+ (void *)JHwParcel_native_readBoolVector },
- { "readStringVector", "()[Ljava/lang/String;",
+ { "readInt8VectorAsArray", "()[B",
+ (void *)JHwParcel_native_readInt8Vector },
+
+ { "readInt16VectorAsArray", "()[S",
+ (void *)JHwParcel_native_readInt16Vector },
+
+ { "readInt32VectorAsArray", "()[I",
+ (void *)JHwParcel_native_readInt32Vector },
+
+ { "readInt64VectorAsArray", "()[J",
+ (void *)JHwParcel_native_readInt64Vector },
+
+ { "readFloatVectorAsArray", "()[F",
+ (void *)JHwParcel_native_readFloatVector },
+
+ { "readDoubleVectorAsArray", "()[D",
+ (void *)JHwParcel_native_readDoubleVector },
+
+ { "readStringVectorAsArray", "()[Ljava/lang/String;",
(void *)JHwParcel_native_readStringVector },
{ "readStrongBinder", "()L" PACKAGE_PATH "/IHwBinder;",
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 5a4348e..a58bc90 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -97,16 +97,6 @@
// ----------------------------------------------------------------------------
-enum {
- STYLE_NUM_ENTRIES = 6,
- STYLE_TYPE = 0,
- STYLE_DATA = 1,
- STYLE_ASSET_COOKIE = 2,
- STYLE_RESOURCE_ID = 3,
- STYLE_CHANGING_CONFIGURATIONS = 4,
- STYLE_DENSITY = 5
-};
-
static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
const Res_value& value, uint32_t ref, ssize_t block,
uint32_t typeSpecFlags, ResTable_config* config = NULL);
@@ -174,7 +164,7 @@
}
// Generic idmap parameters
- const char* argv[7];
+ const char* argv[8];
int argc = 0;
struct stat st;
@@ -185,25 +175,24 @@
argv[argc++] = AssetManager::TARGET_APK_PATH;
argv[argc++] = AssetManager::IDMAP_DIR;
- // Directories to scan for overlays: if OVERLAY_SKU_DIR_PROPERTY is defined,
- // use OVERLAY_DIR/<value of OVERLAY_SKU_DIR_PROPERTY> if exists, otherwise
- // use OVERLAY_DIR if exists.
+ // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
+ // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
char subdir[PROP_VALUE_MAX];
- int len = __system_property_get(AssetManager::OVERLAY_SKU_DIR_PROPERTY, subdir);
- String8 overlayPath;
+ int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir);
if (len > 0) {
- overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
- } else {
- overlayPath = String8(AssetManager::OVERLAY_DIR);
+ String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
+ if (stat(overlayPath.string(), &st) == 0) {
+ argv[argc++] = overlayPath.string();
+ }
}
- if (stat(overlayPath.string(), &st) == 0) {
- argv[argc++] = overlayPath.string();
+ if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
+ argv[argc++] = AssetManager::OVERLAY_DIR;
}
// Finally, invoke idmap (if any overlay directory exists)
if (argc > 5) {
execv(AssetManager::IDMAP_BIN, (char* const*)argv);
- ALOGE("failed to execl for idmap: %s", strerror(errno));
+ ALOGE("failed to execv for idmap: %s", strerror(errno));
exit(1); // should never get here
} else {
exit(0);
@@ -1171,7 +1160,7 @@
}
ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
- bool result = resolveAttrs(theme, defStyleAttr, defStyleRes,
+ bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes,
(uint32_t*) srcValues, NSV,
(uint32_t*) src, NI,
(uint32_t*) baseDest,
@@ -1235,7 +1224,7 @@
ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken);
- bool result = applyStyle(theme, xmlParser,
+ bool result = ApplyStyle(theme, xmlParser,
defStyleAttr, defStyleRes,
(uint32_t*) src, NI,
(uint32_t*) baseDest,
@@ -1300,7 +1289,7 @@
}
}
- bool result = retrieveAttributes(&res, xmlParser,
+ bool result = RetrieveAttributes(&res, xmlParser,
(uint32_t*) src, NI,
(uint32_t*) baseDest,
(uint32_t*) indices);
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 8d2a058..74c073f 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -169,14 +169,16 @@
}
static jlong android_view_DisplayListCanvas_createDisplayListCanvas(JNIEnv* env, jobject clazz,
- jint width, jint height) {
- return reinterpret_cast<jlong>(Canvas::create_recording_canvas(width, height));
+ jlong renderNodePtr, jint width, jint height) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ return reinterpret_cast<jlong>(Canvas::create_recording_canvas(width, height, renderNode));
}
static void android_view_DisplayListCanvas_resetDisplayListCanvas(JNIEnv* env, jobject clazz,
- jlong canvasPtr, jint width, jint height) {
+ jlong canvasPtr, jlong renderNodePtr, jint width, jint height) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
- canvas->resetRecording(width, height);
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ canvas->resetRecording(width, height, renderNode);
}
@@ -229,8 +231,8 @@
{ "nFinishRecording", "(J)J", (void*) android_view_DisplayListCanvas_finishRecording },
{ "nDrawRenderNode", "(JJ)V", (void*) android_view_DisplayListCanvas_drawRenderNode },
- { "nCreateDisplayListCanvas", "(II)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
- { "nResetDisplayListCanvas", "(JII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
+ { "nCreateDisplayListCanvas", "(JII)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
+ { "nResetDisplayListCanvas", "(JJII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
{ "nDrawLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawLayer },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 65f12ac..5b88181 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -186,14 +186,13 @@
return NULL;
}
- auto pixelRef = new PixelRef(
+ auto bitmap = new Bitmap(
(void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot,
screenshotInfo, rowBytes, nullptr);
screenshot.release();
- pixelRef->setImmutable();
-
- return bitmap::createBitmap(env, pixelRef,
- bitmap::kBitmapCreateFlag_Premultiplied, NULL);
+ bitmap->setImmutable();
+ return bitmap::createBitmap(env, bitmap,
+ android::bitmap::kBitmapCreateFlag_Premultiplied, NULL);
}
static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c346849..de6f2c5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1712,11 +1712,6 @@
<permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi @hide Allows an application to retrieve a package's importance.
- This permission is not available to third party applications. -->
- <permission android:name="android.permission.GET_PACKAGE_IMPORTANCE"
- android:protectionLevel="signature|privileged" />
-
<!-- Allows use of PendingIntent.getIntent().
@hide -->
<permission android:name="android.permission.GET_INTENT_SENDER_INTENT"
diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png
index 39d2c95f..3a9031e 100644
--- a/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png
index 3a9031e..39d2c95f 100644
--- a/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
index b9c364c..217ea4e 100644
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
index 217ea4e..b9c364c 100644
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-watch/scrollbar_vertical_thumb.xml b/core/res/res/drawable-watch/scrollbar_vertical_thumb.xml
new file mode 100644
index 0000000..51aced2
--- /dev/null
+++ b/core/res/res/drawable-watch/scrollbar_vertical_thumb.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="?attr/colorControlNormal"
+ android:shape="rectangle">
+ <solid android:color="#39757575" />
+ <size android:height="10dp" />
+ <corners android:radius="2dp" />
+</shape>
diff --git a/core/res/res/drawable-watch/scrollbar_vertical_track.xml b/core/res/res/drawable-watch/scrollbar_vertical_track.xml
new file mode 100644
index 0000000..5a04b1c
--- /dev/null
+++ b/core/res/res/drawable-watch/scrollbar_vertical_track.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="?attr/colorControlNormal"
+ android:shape="rectangle">
+ <solid android:color="#39ffffff" />
+ <size android:width="4dp" />
+</shape>
diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png
index cd0ca73..e443f45 100644
--- a/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png
index e443f45..cd0ca73 100644
--- a/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png
index 39dd3b8..b828430 100644
--- a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png
index b828430..39dd3b8 100644
--- a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/layout-round-watch/alert_dialog_title_material.xml b/core/res/res/layout-round-watch/alert_dialog_title_material.xml
index e543c9b..aefe28f 100644
--- a/core/res/res/layout-round-watch/alert_dialog_title_material.xml
+++ b/core/res/res/layout-round-watch/alert_dialog_title_material.xml
@@ -14,25 +14,31 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<FrameLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="top|center_horizontal"
- android:minHeight="@dimen/alert_dialog_title_height">
- <ImageView android:id="@+id/icon"
+ android:orientation="vertical"
+ android:gravity="top|center_horizontal">
+ <FrameLayout
android:adjustViewBounds="true"
- android:maxHeight="24dp"
- android:maxWidth="24dp"
- android:layout_marginTop="12dp"
- android:layout_gravity="center_horizontal"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:src="@null" />
+ android:minHeight="@dimen/screen_percentage_15">
+ <ImageView android:id="@+id/icon"
+ android:adjustViewBounds="true"
+ android:maxHeight="24dp"
+ android:maxWidth="24dp"
+ android:layout_marginTop="@dimen/screen_percentage_10"
+ android:layout_marginBottom="8dp"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@null" />
+ </FrameLayout>
<TextView android:id="@+id/alertTitle"
style="?android:attr/windowTitleStyle"
- android:layout_marginTop="36dp"
android:layout_marginBottom="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
-</FrameLayout>
+</LinearLayout>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 5f148e3..4283827 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -250,7 +250,7 @@
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Ημερολόγιο"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"έχει πρόσβαση στο ημερολόγιό σας"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"αποστολή και προβολή μηνυμάτων SMS"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"στέλνει και να διαβάζει μηνύματα SMS"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Αποθηκευτικός χώρος"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"έχει πρόσβαση στις φωτογραφίες/πολυμέσα/αρχεία στη συσκευή σας"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Μικρόφωνο"</string>
@@ -273,149 +273,149 @@
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ελέγξτε το επίπεδο ζουμ και τη θέση της οθόνης."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Εκτέλεση κινήσεων"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Επιτρέπει το πάτημα, την ολίσθηση, το πλησίασμα και άλλες κινήσεις."</string>
- <string name="permlab_statusBar" msgid="7417192629601890791">"απενεργοποίηση ή τροποποίηση γραμμής κατάστασης"</string>
+ <string name="permlab_statusBar" msgid="7417192629601890791">"απενεργοποιεί ή να τροποποιεί την γραμμή κατάστασης"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Επιτρέπει στην εφαρμογή να απενεργοποιεί τη γραμμή κατάστασης ή να προσθέτει και να αφαιρεί εικονίδια συστήματος."</string>
- <string name="permlab_statusBarService" msgid="4826835508226139688">"ορισμός ως γραμμής κατάστασης"</string>
+ <string name="permlab_statusBarService" msgid="4826835508226139688">"ορίζεται ως γραμμή κατάστασης"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Επιτρέπει στην εφαρμογή να αποτελεί τη γραμμή κατάστασης."</string>
- <string name="permlab_expandStatusBar" msgid="1148198785937489264">"ανάπτυξη/σύμπτυξη γραμμής κατάστασης"</string>
+ <string name="permlab_expandStatusBar" msgid="1148198785937489264">"αναπτύσσει/συμπτύσσει τη γραμμή κατάστασης"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Επιτρέπει στην εφαρμογή να αναπτύξει ή να συμπτύξει τη γραμμή κατάστασης."</string>
- <string name="permlab_install_shortcut" msgid="4279070216371564234">"εγκατάσταση συντομεύσεων"</string>
+ <string name="permlab_install_shortcut" msgid="4279070216371564234">"εγκαθιστά συντομεύσεις"</string>
<string name="permdesc_install_shortcut" msgid="8341295916286736996">"Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων στην Αρχική οθόνη χωρίς την παρέμβαση του χρήστη."</string>
- <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"κατάργηση εγκατάστασης συντομεύσεων"</string>
+ <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"καταργεί την εγκατάσταση συντομεύσεων"</string>
<string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Επιτρέπει στην εφαρμογή την κατάργηση συντομεύσεων από την Αρχική οθόνη χωρίς την παρέμβαση του χρήστη."</string>
- <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"αναδρομολόγηση εξερχόμενων κλήσεων"</string>
+ <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"αναδρομολογεί τις εξερχόμενες κλήσεις"</string>
<string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Επιτρέπει στην εφαρμογή να βλέπει τον αριθμό που καλέσατε κατά τη διάρκεια μιας εξερχόμενης κλήσης με επιλογή ανακατεύθυνσης της κλήσης σε έναν διαφορετικό αριθμό ή διακοπής της κλήσης."</string>
- <string name="permlab_receiveSms" msgid="8673471768947895082">"λήψη μηνυμάτων κειμένου (SMS)"</string>
+ <string name="permlab_receiveSms" msgid="8673471768947895082">"λαμβάνει μηνύματα κειμένου (SMS)"</string>
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Επιτρέπει στην εφαρμογή τη λήψη και την επεξεργασία μηνυμάτων SMS. Αυτό σημαίνει ότι η εφαρμογή θα μπορούσε να παρακολουθήσει ή να διαγράψει τα μηνύματα που αποστέλλονται στη συσκευή σας χωρίς αυτά να εμφανιστούν σε εσάς."</string>
- <string name="permlab_receiveMms" msgid="1821317344668257098">"λήψη μηνυμάτων κειμένου (MMS)"</string>
+ <string name="permlab_receiveMms" msgid="1821317344668257098">"λαμβάνει μηνύματα κειμένου (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Επιτρέπει στην εφαρμογή τη λήψη και την επεξεργασία μηνυμάτων MMS. Αυτό σημαίνει ότι η εφαρμογή θα μπορούσε να παρακολουθήσει ή να διαγράψει τα μηνύματα που αποστέλλονται στη συσκευή σας χωρίς αυτά να εμφανιστούν σε εσάς."</string>
- <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ανάγνωση μηνυμάτων που έχουν μεταδοθεί μέσω κινητού τηλεφώνου"</string>
+ <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"διαβάζει μηνύματα που έχουν μεταδοθεί μέσω κινητού τηλεφώνου"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Επιτρέπει στην εφαρμογή την ανάγνωση μηνυμάτων που έχουν μεταδοθεί μέσω κινητού τηλεφώνου και έχουν ληφθεί από τη συσκευή σας. Ειδοποιήσεις που μεταδίδονται μέσω κινητού παραδίδονται σε ορισμένες τοποθεσίες για να σας προειδοποιήσουν για καταστάσεις έκτακτης ανάγκης. Κακόβουλες εφαρμογές ενδέχεται να παρεμποδίσουν την απόδοση ή τη λειτουργία της συσκευής σας κατά τη λήψη μετάδοσης μέσω κινητού σχετικά με μια επείγουσα κατάσταση."</string>
- <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ανάγνωση ροών δεδομένων στις οποίες έχετε εγγραφεί"</string>
+ <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"διαβάζει ροές δεδομένων στις οποίες έχετε εγγραφεί"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Επιτρέπει στην εφαρμογή τη λήψη λεπτομερειών σχετικά με τις τρέχουσες συγχρονισμένες ροές δεδομένων."</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"πραγματοποιεί αποστολή και προβολή μηνυμάτων SMS"</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"στέλνει και να διαβάζει μηνύματα SMS"</string>
<string name="permdesc_sendSms" msgid="7094729298204937667">"Επιτρέπει στην εφαρμογή των αποστολή μηνυμάτων SMS. Αυτό μπορεί να προκαλέσει μη αναμενόμενες χρεώσεις. Οι κακόβουλες εφαρμογές ενδέχεται να σας κοστίσουν χρήματα, αποστέλλοντας μηνύματα χωρίς την έγκρισή σας."</string>
- <string name="permlab_readSms" msgid="8745086572213270480">"ανάγνωση των μηνυμάτων κειμένου σας (SMS ή MMS)"</string>
+ <string name="permlab_readSms" msgid="8745086572213270480">"διαβάζει τα μηνύματα κειμένου (SMS ή MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Επιτρέπει στην εφαρμογή την ανάγνωση μηνυμάτων SMS που είναι αποθηκευμένα στο tablet σας ή στην κάρτα σας SIM. Αυτό δίνει τη δυνατότητα στην εφαρμογή να διαβάζει όλα τα μηνύματα SMS, ανεξάρτητα από το περιεχόμενο ή το επίπεδο εμπιστευτικότητάς τους."</string>
<string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Επιτρέπει στην εφαρμογή να διαβάζει μηνύματα SMS που έχουν αποθηκευτεί στην τηλεόραση ή στην κάρτα SIM. Έτσι, η εφαρμογή μπορεί να διαβάζει όλα τα μηνύματα SMS, ανεξαρτήτως περιεχομένου ή εμπιστευτικότητας."</string>
<string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Επιτρέπει στην εφαρμογή την ανάγνωση μηνυμάτων SMS που είναι αποθηκευμένα στο τηλέφωνό σας ή στην κάρτα σας SIM. Αυτό δίνει τη δυνατότητα στην εφαρμογή να διαβάζει όλα τα μηνύματα SMS, ανεξάρτητα από το περιεχόμενο ή το επίπεδο εμπιστευτικότητάς τους."</string>
- <string name="permlab_receiveWapPush" msgid="5991398711936590410">"λήψη μηνυμάτων κειμένου (WAP)"</string>
+ <string name="permlab_receiveWapPush" msgid="5991398711936590410">"λαμβάνει μηνύματα κειμένου (WAP)"</string>
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Επιτρέπει στην εφαρμογή τη λήψη και την επεξεργασία μηνυμάτων WAP. Αυτό σημαίνει ότι η εφαρμογή θα μπορούσε να παρακολουθήσει ή να διαγράψει τα μηνύματα που αποστέλλονται στη συσκευή σας χωρίς αυτά να εμφανιστούν σε εσάς."</string>
- <string name="permlab_getTasks" msgid="6466095396623933906">"ανάκτηση εκτελούμενων εφαρμογών"</string>
+ <string name="permlab_getTasks" msgid="6466095396623933906">"ανακτά εκτελούμενες εφαρμογές"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Επιτρέπει στην εφαρμογή την ανάκτηση πληροφοριών σχετικά με τρέχουσες και πρόσφατα εκτελούμενες εργασίες. Αυτό μπορεί να δίνει τη δυνατότητα στην εφαρμογή να ανακαλύπτει πληροφορίες σχετικά με το ποιες εφαρμογές χρησιμοποιούνται στη συσκευή."</string>
- <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"διαχείριση προφίλ και κατόχων συσκευής"</string>
+ <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"διαχειρίζεται το προφίλ και τους κατόχους συσκευής"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"Επιτρέπει σε εφαρμογές να ορίζουν τους κατόχους προφίλ και τον κάτοχο της συσκευής."</string>
- <string name="permlab_reorderTasks" msgid="2018575526934422779">"αναδιάταξη εκτελούμενων εφαρμογών"</string>
+ <string name="permlab_reorderTasks" msgid="2018575526934422779">"αναδιατάσσει τις εκτελούμενες εφαρμογές"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"Επιτρέπει στην εφαρμογή τη μετακίνηση εργασιών στο προσκήνιο και το παρασκήνιο. Η εφαρμογή μπορεί να το κάνει αυτό χωρίς να καταχωρίσετε δεδομένα εισόδου."</string>
- <string name="permlab_enableCarMode" msgid="5684504058192921098">"ενεργοποίηση λειτουργίας αυτοκινήτου"</string>
+ <string name="permlab_enableCarMode" msgid="5684504058192921098">"ενεργοποιεί την λειτουργία αυτοκινήτου"</string>
<string name="permdesc_enableCarMode" msgid="4853187425751419467">"Επιτρέπει στην εφαρμογή την ενεργοποίηση της λειτουργίας αυτοκινήτου."</string>
- <string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"κλείσιμο των άλλων εφαρμογών"</string>
+ <string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"κλείνει άλλες εφαρμογές"</string>
<string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"Επιτρέπει στην εφαρμογή τον τερματισμό των διεργασιών παρασκηνίου άλλων εφαρμογών. Αυτό μπορεί να προκαλεί τη διακοπή λειτουργίας άλλων εφαρμογών."</string>
- <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"σχεδίαση πάνω σε άλλες εφαρμογές"</string>
+ <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"σχεδιάζει πάνω από άλλες εφαρμογές"</string>
<string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Επιτρέπει στην εφαρμογή το σχεδιασμό πάνω σε άλλες εφαρμογές ή τμήματα του περιβάλλοντος χρήστη. Ενδέχεται να παρεμβαίνουν στη χρήση του περιβάλλοντος σε άλλες εφαρμογές ή να αλλάζουν τα στοιχεία που βλέπετε σε άλλες εφαρμογές."</string>
- <string name="permlab_persistentActivity" msgid="8841113627955563938">"να εκτελείται συνεχώς η εφαρμογή"</string>
+ <string name="permlab_persistentActivity" msgid="8841113627955563938">"επιτρέπει στην εφαρμογή να εκτελείται συνεχώς"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Επιτρέπει στην εφαρμογή να δημιουργεί μόνιμα τμήματα του εαυτού της στη μνήμη. Αυτό μπορεί να περιορίζει τη μνήμη που διατίθεται σε άλλες εφαρμογές, καθυστερώντας τη λειτουργία του tablet."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Επιτρέπει στην εφαρμογή να καθιστά τμήματά της μόνιμα στη μνήμη. Αυτό μπορεί να περιορίσει τη μνήμη που διατίθεται σε άλλες εφαρμογές, επιβραδύνοντας τη λειτουργία της τηλεόρασης."</string>
<string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Επιτρέπει στην εφαρμογή να δημιουργεί μόνιμα τμήματα του εαυτού της στη μνήμη. Αυτό μπορεί να περιορίζει τη μνήμη που διατίθεται σε άλλες εφαρμογές, καθυστερώντας τη λειτουργία του τηλεφώνου."</string>
- <string name="permlab_getPackageSize" msgid="7472921768357981986">"μέτρηση αποθηκευτικού χώρου εφαρμογής"</string>
+ <string name="permlab_getPackageSize" msgid="7472921768357981986">"υπολογίζει τον αποθηκευτικό χώρο εφαρμογής"</string>
<string name="permdesc_getPackageSize" msgid="3921068154420738296">"Επιτρέπει στην εφαρμογή να ανακτήσει τα μεγέθη κώδικα, δεδομένων και προσωρινής μνήμης"</string>
<string name="permlab_writeSettings" msgid="2226195290955224730">"τροποποίηση ρυθμίσεων συστήματος"</string>
<string name="permdesc_writeSettings" msgid="7775723441558907181">"Επιτρέπει στην εφαρμογή την τροποποίηση των δεδομένων των ρυθμίσεων του συστήματος. Τυχόν κακόβουλες εφαρμογές ενδέχεται να καταστρέψουν τη διαμόρφωση του συστήματός σας."</string>
- <string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"εκτέλεση κατά την έναρξη"</string>
+ <string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"εκτελείται κατά την έναρξη"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"Επιτρέπει στην εφαρμογή να εκκινηθεί αμέσως μόλις ολοκληρωθεί η εκκίνηση του συστήματος. Αυτό ενδέχεται να καθυστερήσει την εκκίνηση του tablet και να προκαλέσει γενική μείωση της ταχύτητας λειτουργίας του tablet, καθώς η εφαρμογή θα εκτελείται συνεχώς."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4525890122209673621">"Επιτρέπει στην εφαρμογή να ξεκινάει μόλις ολοκληρώνεται η εκκίνηση του συστήματος. Αυτό μπορεί να καθυστερεί την εκκίνηση της τηλεόρασης και επιτρέπει στην εφαρμογή να επιβραδύνει τη συνολική λειτουργία του tablet, λόγω της συνεχούς προβολής."</string>
<string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"Επιτρέπει στην εφαρμογή να εκκινηθεί αμέσως μόλις ολοκληρωθεί η εκκίνηση του συστήματος. Αυτό ενδέχεται να καθυστερήσει την εκκίνηση του τηλεφώνου και να προκαλέσει γενική μείωση της ταχύτητας λειτουργίας του τηλεφώνου, καθώς η εφαρμογή θα εκτελείται συνεχώς."</string>
- <string name="permlab_broadcastSticky" msgid="7919126372606881614">"αποστολή εκπομπής sticky"</string>
+ <string name="permlab_broadcastSticky" msgid="7919126372606881614">"στέλνει εκπομπή sticky"</string>
<string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"Επιτρέπει στην εφαρμογή την αποστολή εκπομπών sticky, οι οποίες παραμένουν μετά το τέλος της εκπομπής. Η υπερβολική χρήση ενδέχεται να καταστήσει τη λειτουργία του tablet αργή ή ασταθή, προκαλώντας τη χρήση μεγάλου τμήματος της μνήμης."</string>
<string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Επιτρέπει στην εφαρμογή να στέλνει εκπομπές που παραμένουν μετά το τέλος της μετάδοσης. Η υπερβολική χρήση μπορεί να καταστήσει αργή ή ασταθή τη λειτουργία της τηλεόρασης, προκαλώντας τη χρήση υπερβολικά μεγάλου μέρους της μνήμης."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Επιτρέπει στην εφαρμογή την αποστολή εκπομπών sticky, οι οποίες παραμένουν μετά το τέλος της εκπομπής. Η υπερβολική χρήση ενδέχεται να καταστήσει τη λειτουργία του τηλεφώνου αργή ή ασταθή, προκαλώντας τη χρήση μεγάλου τμήματος της μνήμης."</string>
- <string name="permlab_readContacts" msgid="8348481131899886131">"ανάγνωση των επαφών σας"</string>
+ <string name="permlab_readContacts" msgid="8348481131899886131">"διαβάζει τις επαφές σας"</string>
<string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Επιτρέπει στην εφαρμογή την ανάγνωση δεδομένων σχετικά με τις επαφές σας που είναι αποθηκευμένες στο tablet σας, συμπεριλαμβανομένης της συχνότητας με την οποία έχετε καλέσει συγκεκριμένα άτομα ή έχετε επικοινωνήσει μαζί τους μέσω ηλεκτρονικού ταχυδρομείου ή άλλου τρόπου. Αυτή η άδεια δίνει τη δυνατότητα σε εφαρμογές να αποθηκεύουν τα δεδομένα των επαφών σας και οι κακόβουλες εφαρμογές ενδέχεται να μοιράζονται δεδομένα επαφών χωρίς να το γνωρίζετε."</string>
<string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Επιτρέπει στην εφαρμογή να διαβάζει δεδομένα σχετικά με τις επαφές σας που είναι αποθηκευμένες στην τηλεόρασή σας, συμπεριλαμβανομένης της συχνότητας με την οποία έχετε καλέσει συγκεκριμένα άτομα ή έχετε επικοινωνήσει μαζί τους μέσω ηλεκτρονικού ταχυδρομείου ή άλλου τρόπου. Αυτό το δικαίωμα επιτρέπει στις εφαρμογές να αποθηκεύουν τα δεδομένα των επαφών σας. Επίσης, κακόβουλες εφαρμογές μπορεί να μοιραστούν δεδομένα επαφών χωρίς να το γνωρίζετε."</string>
<string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Επιτρέπει στην εφαρμογή την ανάγνωση δεδομένων σχετικά με τις επαφές σας που είναι αποθηκευμένες στο τηλέφωνό σας, συμπεριλαμβανομένης της συχνότητας με την οποία έχετε καλέσει συγκεκριμένα άτομα ή έχετε επικοινωνήσει μαζί τους μέσω ηλεκτρονικού ταχυδρομείου ή άλλου τρόπου. Αυτή η άδεια δίνει τη δυνατότητα σε εφαρμογές να αποθηκεύουν τα δεδομένα των επαφών σας και οι κακόβουλες εφαρμογές ενδέχεται να μοιράζονται δεδομένα επαφών χωρίς να το γνωρίζετε."</string>
- <string name="permlab_writeContacts" msgid="5107492086416793544">"τροποποίηση των επαφών σας"</string>
+ <string name="permlab_writeContacts" msgid="5107492086416793544">"τροποποιεί τις επαφές σας"</string>
<string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Επιτρέπει στην εφαρμογή την τροποποίηση των δεδομένων σχετικά με τις επαφές σας που είναι αποθηκευμένες στο tablet σας, συμπεριλαμβανομένης της συχνότητας με την οποία έχετε καλέσει συγκεκριμένες επαφές ή έχετε επικοινωνήσει μαζί τους μέσω ηλεκτρονικού ταχυδρομείου ή άλλου τρόπου. Αυτή η άδεια δίνει τη δυνατότητα σε εφαρμογές να διαγράφουν δεδομένα επαφών."</string>
<string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Επιτρέπει στην εφαρμογή να τροποποιεί τα δεδομένα σχετικά με τις επαφές που είναι αποθηκευμένες στην τηλεόρασή σας, συμπεριλαμβανομένης της συχνότητας με την οποία έχετε καλέσει συγκεκριμένες επαφές ή έχετε επικοινωνήσει μαζί τους μέσω ηλεκτρονικού ταχυδρομείου ή άλλου τρόπου. Αυτό το δικαίωμα δίνει τη δυνατότητα σε εφαρμογές να διαγράφουν δεδομένα επαφών."</string>
<string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Επιτρέπει στην εφαρμογή την τροποποίηση των δεδομένων σχετικά με τις επαφές σας που είναι αποθηκευμένες στο τηλέφωνό σας, συμπεριλαμβανομένης της συχνότητας με την οποία έχετε καλέσει συγκεκριμένες επαφές ή έχετε επικοινωνήσει μαζί τους μέσω ηλεκτρονικού ταχυδρομείου ή άλλου τρόπου. Αυτή η άδεια δίνει τη δυνατότητα σε εφαρμογές να διαγράφουν δεδομένα επαφών."</string>
- <string name="permlab_readCallLog" msgid="3478133184624102739">"ανάγνωση αρχείου καταγραφής κλήσεων"</string>
+ <string name="permlab_readCallLog" msgid="3478133184624102739">"διαβάζει το αρχείο καταγραφής κλήσεων"</string>
<string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Επιτρέπει στην εφαρμογή την ανάγνωση του αρχείου καταγραφής κλήσεων του tablet σας, συμπεριλαμβανομένων των δεδομένων σχετικά με τις εισερχόμενες και τις εξερχόμενες κλήσεις. Αυτή η άδεια δίνει τη δυνατότητα στις εφαρμογές να αποθηκεύει τα δεδομένα του αρχείου καταγραφής κλήσεων και οι κακόβουλες εφαρμογές ενδέχεται να μοιράζονται δεδομένα του αρχείου καταγραφής κλήσεων χωρίς να το γνωρίζετε."</string>
<string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Επιτρέπει στην εφαρμογή να διαβάζει το αρχείο καταγραφής κλήσεων της τηλεόρασής σας, συμπεριλαμβανομένων δεδομένων σχετικά με τις εισερχόμενες και τις εξερχόμενες κλήσεις. Αυτό το δικαίωμα επιτρέπει στις εφαρμογές να αποθηκεύουν τα δεδομένα του αρχείου καταγραφής κλήσεων. Επίσης, κακόβουλες εφαρμογές μπορεί να μοιραστούν δεδομένα αρχείου καταγραφής κλήσεων χωρίς να το γνωρίζετε."</string>
<string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Επιτρέπει στην εφαρμογή την ανάγνωση του αρχείου καταγραφής κλήσεων του τηλεφώνου σας, συμπεριλαμβανομένων των δεδομένων σχετικά με τις εισερχόμενες και τις εξερχόμενες κλήσεις. Αυτή η άδεια δίνει τη δυνατότητα στις εφαρμογές να αποθηκεύει τα δεδομένα του αρχείου καταγραφής κλήσεων και οι κακόβουλες εφαρμογές ενδέχεται να μοιράζονται δεδομένα του αρχείου καταγραφής κλήσεων χωρίς να το γνωρίζετε."</string>
- <string name="permlab_writeCallLog" msgid="8552045664743499354">"εγγραφή αρχείου καταγραφής κλήσεων"</string>
+ <string name="permlab_writeCallLog" msgid="8552045664743499354">"εγγράφει αρχείο καταγραφής κλήσεων"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Επιτρέπει στην εφαρμογή να τροποποιεί το αρχείο καταγραφής κλήσεων του tablet σας, συμπεριλαμβανομένων των δεδομένων σχετικά με τις εισερχόμενες και τις εξερχόμενες κλήσεις. Οι κακόβουλες εφαρμογές μπορεί να το χρησιμοποιήσουν για να διαγράψουν ή να τροποποιήσουν το αρχείο καταγραφής κλήσεων."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Επιτρέπει στην εφαρμογή να τροποποιεί το αρχείο καταγραφής κλήσεων της τηλεόρασής σας, συμπεριλαμβανομένων των δεδομένων σχετικά με τις εισερχόμενες και τις εξερχόμενες κλήσεις. Κακόβουλες εφαρμογές μπορεί να χρησιμοποιήσουν αυτήν τη δυνατότητα, για να διαγράψουν ή να τροποποιήσουν το αρχείο καταγραφής κλήσεων."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Επιτρέπει στην εφαρμογή να τροποποιεί το αρχείο καταγραφής κλήσεων του τηλεφώνου σας, συμπεριλαμβανομένων των δεδομένων σχετικά με τις εισερχόμενες και τις εξερχόμενες κλήσεις. Οι κακόβουλες εφαρμογές μπορεί να το χρησιμοποιήσουν για να διαγράψουν ή να τροποποιήσουν το αρχείο καταγραφής κλήσεων."</string>
<string name="permlab_bodySensors" msgid="4683341291818520277">"πρόσβαση στους αισθητήρες λειτουργιών (π.χ. παρακολούθηση καρδιακού παλμού)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Επιτρέπει στην εφαρμογή να αποκτήσει πρόσβαση στα δεδομένα των αισθητήρων που παρακολουθούν τη φυσική σας κατάσταση, όπως τον καρδιακό ρυθμό σας."</string>
- <string name="permlab_readCalendar" msgid="5972727560257612398">"ανάγνωση συμβάντων ημερολογίου και εμπιστευτικών πληροφοριών"</string>
+ <string name="permlab_readCalendar" msgid="5972727560257612398">"διαβάζει συμβάντα ημερολογίου και εμπιστευτικές πληροφορίες"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Επιτρέπει στην εφαρμογή την ανάγνωση όλων των συμβάντων ημερολογίου που είναι αποθηκευμένα στο tablet σας, συμπεριλαμβανομένων εκείνων των φίλων ή των συναδέλφων σας. Αυτό μπορεί να δίνει τη δυνατότητα στην εφαρμογή να μοιράζεται ή να αποθηκεύει τα δεδομένα του ημερολογίου σας, ανεξάρτητα από το βαθμό εμπιστευτικότητας ή ευαισθησίας τους."</string>
<string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Επιτρέπει στην εφαρμογή να διαβάζει όλα τα συμβάντα ημερολογίου που είναι αποθηκευμένα στην τηλεόρασή σας, συμπεριλαμβανομένων εκείνων από τους φίλους ή τους συναδέλφους σας. Αυτό μπορεί να επιτρέψει στην εφαρμογή να μοιράζεται ή να αποθηκεύει τα δεδομένα ημερολογίου, ανεξαρτήτως εμπιστευτικότητας ή ευαισθησίας."</string>
<string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Επιτρέπει στην εφαρμογή την ανάγνωση όλων των συμβάντων ημερολογίου που είναι αποθηκευμένα στο τηλέφωνό σας, συμπεριλαμβανομένων εκείνων των φίλων ή των συναδέλφων σας. Αυτό μπορεί να δίνει τη δυνατότητα στην εφαρμογή να μοιράζεται ή να αποθηκεύει τα δεδομένα του ημερολογίου σας, ανεξάρτητα από το βαθμό εμπιστευτικότητας ή ευαισθησίας τους."</string>
- <string name="permlab_writeCalendar" msgid="8438874755193825647">"προσθήκη ή τροποποίηση συμβάντων ημερολογίου και αποστολή μηνυμάτων ηλεκτρονικού ταχυδρομείου σε προσκεκλημένους χωρίς να το γνωρίζουν οι κάτοχοι"</string>
+ <string name="permlab_writeCalendar" msgid="8438874755193825647">"προσθέτει ή τροποποιεί συμβάντα ημερολογίου και να στέλνει μηνύματα ηλ. ταχυδρομείου σε προσκεκλημένους χωρίς να το γνωρίζουν οι κάτοχοι"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Επιτρέπει στην εφαρμογή την προσθήκη, την κατάργηση και την αλλαγή συμβάντων που μπορείτε να τροποποιήσετε στο tablet σας, συμπεριλαμβανομένων εκείνων των φίλων ή των συναδέλφων σας. Αυτό μπορεί να επιτρέπει στην εφαρμογή να αποστέλλει μηνύματα που φαίνεται ότι προέρχονται από κατόχους ημερολογίων ή να τροποποιεί συμβάντα χωρίς να το γνωρίζουν οι κάτοχοι."</string>
<string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Επιτρέπει στην εφαρμογή να προσθέτει, να καταργεί και να αλλάζει συμβάντα που μπορείτε να τροποποιείτε στην τηλεόρασή σας, συμπεριλαμβανομένων όσων ανήκουν σε φίλους ή συναδέλφους. Αυτό ενδέχεται να επιτρέπει στην εφαρμογή να αποστέλλει μηνύματα τα οποία φαίνεται ότι προέρχονται από κατόχους ημερολογίου ή να τροποποιεί συμβάντα χωρίς να το γνωρίζουν οι κάτοχοί τους."</string>
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Επιτρέπει στην εφαρμογή την προσθήκη, την κατάργηση και την αλλαγή συμβάντων που μπορείτε να τροποποιήσετε στο τηλέφωνό σας, συμπεριλαμβανομένων εκείνων των φίλων ή των συναδέλφων σας. Αυτό μπορεί να επιτρέπει στην εφαρμογή να αποστέλλει μηνύματα που φαίνεται ότι προέρχονται από κατόχους ημερολογίων ή να τροποποιεί συμβάντα χωρίς να το γνωρίζουν οι κάτοχοι."</string>
- <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"πρόσβαση σε επιπλέον εντολές παρόχου τοποθεσίας"</string>
+ <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"έχει πρόσβαση σε επιπλέον εντολές παρόχου τοποθεσίας"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Επιτρέπει στην εφαρμογή την πρόσβαση σε επιπλέον εντολές παρόχου τοποθεσίας. Αυτό μπορεί να δώσει τη δυνατότητα στην εφαρμογή να παρέμβει στη λειτουργία του GPS ή άλλων πηγών τοποθεσίας."</string>
- <string name="permlab_accessFineLocation" msgid="251034415460950944">"πρόσβαση στην ακριβή τοποθεσία (με βάση το GPS και το δίκτυο)"</string>
+ <string name="permlab_accessFineLocation" msgid="251034415460950944">"έχει πρόσβαση στην ακριβή τοποθεσία (με βάση το GPS και το δίκτυο)"</string>
<string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Επιτρέπει στην εφαρμογή να λαμβάνει την ακριβή θέση σας με τη χρήση του Παγκόσμιου Συστήματος Εντοπισμού (GPS) ή πηγών τοποθεσίας δικτύου, όπως κεραίες κινητής τηλεφωνίας και Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας πρέπει να είναι ενεργοποιημένες και διαθέσιμες στην συσκευή σας, ώστε να μπορούν να χρησιμοποιηθούν από την εφαρμογή. Οι εφαρμογές ενδέχεται να τις χρησιμοποιήσουν για να προσδιορίσουν τη θέση σας και ενδέχεται να καταναλώσουν επιπλέον ισχύ μπαταρίας."</string>
- <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"πρόσβαση στην τοποθεσία κατά προσέγγιση (με βάση το δίκτυο)"</string>
+ <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"έχει πρόσβαση στην τοποθεσία κατά προσέγγιση (με βάση το δίκτυο)"</string>
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Επιτρέπει στην εφαρμογή τη λήψη της κατά προσέγγιση τοποθεσίας σας. Αυτή η τοποθεσία προκύπτει από τις υπηρεσίες τοποθεσίας με τη χρήση πηγών τοποθεσίας δικτύου, όπως κεραίες κινητής τηλεφωνίας και Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας πρέπει να είναι ενεργοποιημένες και διαθέσιμες στην συσκευή σας, ώστε να μπορούν να χρησιμοποιηθούν από την εφαρμογή. Οι εφαρμογές ενδέχεται να τις χρησιμοποιήσουν για να προσδιορίσουν κατά προσέγγιση τη θέση σας."</string>
- <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"αλλαγή των ρυθμίσεων ήχου"</string>
+ <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"αλλάζει τις ρυθμίσεις ήχου"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Επιτρέπει στην εφαρμογή την τροποποίηση καθολικών ρυθμίσεων ήχου, όπως η ένταση και ποιο ηχείο χρησιμοποιείται για έξοδο."</string>
- <string name="permlab_recordAudio" msgid="3876049771427466323">"εγγραφή ήχου"</string>
+ <string name="permlab_recordAudio" msgid="3876049771427466323">"εγγράφει ήχο"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"Επιτρέπει στην εφαρμογή την εγγραφή ήχου με το μικρόφωνο. Αυτή η άδεια δίνει τη δυνατότητα στην εφαρμογή να εγγράφει ήχο ανά πάσα στιγμή χωρίς την έγκρισή σας."</string>
<string name="permlab_sim_communication" msgid="2935852302216852065">"στέλνει εντολές στην κάρτα SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Επιτρέπει στην εφαρμογή την αποστολή εντολών στην κάρτα SIM. Αυτό είναι εξαιρετικά επικίνδυνο."</string>
- <string name="permlab_camera" msgid="3616391919559751192">"λήψη φωτογραφιών και βίντεο"</string>
+ <string name="permlab_camera" msgid="3616391919559751192">"κάνει λήψη φωτογραφιών και βίντεο"</string>
<string name="permdesc_camera" msgid="8497216524735535009">"Επιτρέπει στην εφαρμογή τη λήψη φωτογραφιών και βίντεο με τη φωτογραφική μηχανή. Αυτή η άδεια δίνει τη δυνατότητα στην εφαρμογή να χρησιμοποιεί τη φωτογραφική μηχανή ανά πάσα στιγμή χωρίς την έγκρισή σας."</string>
- <string name="permlab_vibrate" msgid="7696427026057705834">"έλεγχος δόνησης"</string>
+ <string name="permlab_vibrate" msgid="7696427026057705834">"ελέγχει τη δόνηση"</string>
<string name="permdesc_vibrate" msgid="6284989245902300945">"Επιτρέπει στην εφαρμογή τον έλεγχο της δόνησης."</string>
- <string name="permlab_callPhone" msgid="3925836347681847954">"απευθείας κλήση τηλεφωνικών αριθμών"</string>
+ <string name="permlab_callPhone" msgid="3925836347681847954">"πραγματοποιεί απευθείας κλήση τηλεφωνικών αριθμών"</string>
<string name="permdesc_callPhone" msgid="3740797576113760827">"Επιτρέπει στην εφαρμογή την κλήση αριθμών τηλεφώνου χωρίς δική σας παρέμβαση. Αυτό μπορεί να προκαλέσει μη αναμενόμενες χρεώσεις ή κλήσεις. Έχετε υπόψη ότι δεν επιτρέπεται στην εφαρμογή η κλήση αριθμών έκτακτης ανάγκης. Οι κακόβουλες εφαρμογές ενδέχεται να σας κοστίσουν χρήματα, πραγματοποιώντας κλήσεις χωρίς την έγκρισή σας."</string>
- <string name="permlab_accessImsCallService" msgid="3574943847181793918">"πρόσβαση στην υπηρεσία κλήσεων της IMS"</string>
+ <string name="permlab_accessImsCallService" msgid="3574943847181793918">"έχει πρόσβαση στην υπηρεσία κλήσεων της IMS"</string>
<string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Επιτρέπει στην εφαρμογή τη χρήση της υπηρεσίας IMS για την πραγματοποίηση κλήσεων χωρίς τη δική σας παρέμβαση."</string>
- <string name="permlab_readPhoneState" msgid="9178228524507610486">"ανάγνωση κατάστασης και ταυτότητας τηλεφώνου"</string>
+ <string name="permlab_readPhoneState" msgid="9178228524507610486">"διαβάζει την κατάσταση και ταυτότητα τηλεφώνου"</string>
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Επιτρέπει στην εφαρμογή την πρόσβαση στις λειτουργίες τηλεφώνου της συσκευής. Αυτή η άδεια δίνει τη δυνατότητα στην εφαρμογή να καθορίζει τον αριθμό τηλεφώνου και τα αναγνωριστικά συσκευών, εάν μια κλήση είναι ενεργή, καθώς και τον απομακρυσμένο αριθμό που συνδέεται από μια κλήση."</string>
- <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"παρεμπόδιση μετάβασης του tablet σε κατάσταση αδράνειας"</string>
- <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"αποτροπή μετάβασης της τηλεόρασης στην κατάσταση αδράνειας"</string>
- <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"παρεμπόδιση μετάβασης του τηλεφώνου σε κατάσταση αδράνειας"</string>
+ <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"αποτρέπει την μετάβαση του tablet σε κατάσταση αδράνειας"</string>
+ <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"αποτρέπει την μετάβαση της τηλεόρασης σε κατάσταση αδράνειας"</string>
+ <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"αποτρέπει το τηλεφώνο να μεταβεί σε κατάσταση αδράνειας"</string>
<string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Επιτρέπει στην εφαρμογή την παρεμπόδιση της μετάβασης του tablet σε κατάσταση αδράνειας."</string>
<string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"Επιτρέπει στην εφαρμογή να εμποδίζει τη μετάβαση της τηλεόρασης στην κατάσταση αδράνειας."</string>
<string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Επιτρέπει στην εφαρμογή την παρεμπόδιση της μετάβασης του τηλεφώνου σε κατάσταση αδράνειας."</string>
- <string name="permlab_transmitIr" msgid="7545858504238530105">"μετάδοση υπερύθρων"</string>
+ <string name="permlab_transmitIr" msgid="7545858504238530105">"μεταδίδει με υπερύθρες"</string>
<string name="permdesc_transmitIr" product="tablet" msgid="5358308854306529170">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί τον πομπό υπερύθρων του tablet."</string>
<string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί τον πομπό υπερύθρων της τηλεόρασης."</string>
<string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί τον πομπό υπερύθρων του τηλεφώνου."</string>
- <string name="permlab_setWallpaper" msgid="6627192333373465143">"ορισμός ταπετσαρίας"</string>
+ <string name="permlab_setWallpaper" msgid="6627192333373465143">"ορίζει ταπετσαρία"</string>
<string name="permdesc_setWallpaper" msgid="7373447920977624745">"Επιτρέπει στην εφαρμογή τον ορισμό της ταπετσαρίας συστήματος."</string>
- <string name="permlab_setWallpaperHints" msgid="3278608165977736538">"ρύθμιση του μεγέθους της ταπετσαρίας σας"</string>
+ <string name="permlab_setWallpaperHints" msgid="3278608165977736538">"ρυθμίζει το μέγεθος της ταπετσαρίας"</string>
<string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"Επιτρέπει στην εφαρμογή τον ορισμό συμβουλών μεγέθους ταπετσαρίας συστήματος."</string>
- <string name="permlab_setTimeZone" msgid="2945079801013077340">"ορισμός ζώνης ώρας"</string>
+ <string name="permlab_setTimeZone" msgid="2945079801013077340">"ορίζει τη ζώνης ώρας"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"Επιτρέπει στην εφαρμογή την αλλαγή της ζώνης ώρας του tablet."</string>
<string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"Επιτρέπει στην εφαρμογή να αλλάζει τη ζώνη ώρας της τηλεόρασης."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"Επιτρέπει στην εφαρμογή την αλλαγή της ζώνης ώρας του τηλεφώνου."</string>
- <string name="permlab_getAccounts" msgid="1086795467760122114">"εύρεση λογαριασμών στη συσκευή"</string>
+ <string name="permlab_getAccounts" msgid="1086795467760122114">"βρίσκει λογαριασμούς στη συσκευή"</string>
<string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Επιτρέπει στην εφαρμογή τη λήψη της λίστας λογαριασμών που υπάρχουν στο tablet. Μπορεί να περιλαμβάνονται τυχόν λογαριασμοί που δημιουργήθηκαν από εφαρμογές που έχετε εγκαταστήσει."</string>
<string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Επιτρέπει στην εφαρμογή να λαμβάνει τη λίστα λογαριασμών που γνωρίζει η τηλεόραση. Αυτό μπορεί να περιλαμβάνει λογαριασμούς που δημιουργούνται από λογαριασμούς που έχετε εγκαταστήσει."</string>
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Επιτρέπει στην εφαρμογή τη λήψη της λίστας λογαριασμών που υπάρχουν στο τηλέφωνο. Μπορεί να περιλαμβάνονται τυχόν λογαριασμοί που δημιουργήθηκαν από εφαρμογές που έχετε εγκαταστήσει."</string>
- <string name="permlab_accessNetworkState" msgid="4951027964348974773">"προβολή συνδέσεων δικτύου"</string>
+ <string name="permlab_accessNetworkState" msgid="4951027964348974773">"βλέπει τις συνδέσεις δικτύου"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Επιτρέπει στην εφαρμογή την προβολή πληροφοριών σχετικά με συνδέσεις δικτύου, όπως ποια δίκτυα υπάρχουν και είναι συνδεδεμένα."</string>
- <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"πλήρης πρόσβαση στο δίκτυο"</string>
+ <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"έχει πλήρη πρόσβαση στο δίκτυο"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Επιτρέπει στην εφαρμογή τη δημιουργία θέσεων δικτύου και τη χρήση προσαρμοσμένων πρωτοκόλλων δικτύου. Το πρόγραμμα περιήγησης και άλλες εφαρμογές παρέχουν μέσα για την αποστολή δεδομένων στο διαδίκτυο, επομένως η συγκεκριμένη άδεια δεν είναι απαραίτητη για την αποστολή δεδομένων στο διαδίκτυο."</string>
- <string name="permlab_changeNetworkState" msgid="958884291454327309">"αλλαγή συνδεσιμότητας δικτύου"</string>
+ <string name="permlab_changeNetworkState" msgid="958884291454327309">"αλλάζει την συνδεσιμότητα δικτύου"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Επιτρέπει στην εφαρμογή την αλλαγή της κατάστασης συνδεσιμότητας δικτύου."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"αλλαγή συνδεσιμότητας μέσω σύνδεσης με κινητή συσκευή"</string>
+ <string name="permlab_changeTetherState" msgid="5952584964373017960">"αλλάζει συνδεσιμότητα μέσω σύνδεσης με κινητή συσκευή"</string>
<string name="permdesc_changeTetherState" msgid="1524441344412319780">"Επιτρέπει στην εφαρμογή την αλλαγή της κατάστασης συνδεσιμότητας δικτύου."</string>
- <string name="permlab_accessWifiState" msgid="5202012949247040011">"προβολή συνδέσεων Wi-Fi"</string>
+ <string name="permlab_accessWifiState" msgid="5202012949247040011">"βλέπει τις συνδέσεις Wi-Fi"</string>
<string name="permdesc_accessWifiState" msgid="5002798077387803726">"Επιτρέπει στην εφαρμογή την προβολή πληροφοριών σχετικά με τη δικτύωση Wi-Fi, όπως εάν το Wi-Fi είναι ενεργοποιημένο και τα ονόματα των συνδεδεμένων συσκευών Wi-Fi."</string>
- <string name="permlab_changeWifiState" msgid="6550641188749128035">"σύνδεση και αποσύνδεση από το Wi-Fi"</string>
+ <string name="permlab_changeWifiState" msgid="6550641188749128035">"συνδέεται/αποσυνδέεται από το Wi-Fi"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"Επιτρέπει στην εφαρμογή τη σύνδεση σε σημεία πρόσβασης Wi-Fi και την αποσύνδεση από αυτά, καθώς και την πραγματοποίηση αλλαγών σε διαμόρφωση συσκευών για δίκτυα Wi-Fi."</string>
- <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"να επιτρέπεται η λήψη πολλαπλής διανομής Wi-Fi"</string>
+ <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"επιτρέπει την λήψη πολλαπλής διανομής Wi-Fi"</string>
<string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Επιτρέπει στην εφαρμογή τη λήψη πακέτων που αποστέλλονται σε όλες τις συσκευές σε ένα δίκτυο Wi-Fi, με χρήση διευθύνσεων πολλαπλής διανομής και όχι απλώς στο tablet σας. Χρησιμοποιεί περισσότερη ενέργεια σε σχέση με τη λειτουργία χωρίς πολλαπλή διανομή."</string>
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Επιτρέπει στην εφαρμογή να λαμβάνει πακέτα που αποστέλλονται σε όλες τις συσκευές σε ένα δίκτυο Wi-Fi, χρησιμοποιώντας διευθύνσεις multicast και όχι μόνο την τηλεόρασή σας. Χρησιμοποιεί περισσότερη ενέργεια από τη λειτουργία χωρίς multicast."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Επιτρέπει στην εφαρμογή τη λήψη πακέτων που αποστέλλονται σε όλες τις συσκευές σε ένα δίκτυο Wi-Fi, με χρήση διευθύνσεων πολλαπλής διανομής και όχι απλώς στο τηλέφωνό σας. Χρησιμοποιεί περισσότερη ενέργεια σε σχέση με τη λειτουργία χωρίς πολλαπλή διανομή."</string>
- <string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"πρόσβαση στις ρυθμίσεις Bluetooth"</string>
+ <string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"διαβάζει τις ρυθμίσεις Bluetooth"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Επιτρέπει στην εφαρμογή τη διαμόρφωση του τοπικού tablet Bluetooth, τον εντοπισμό και τη σύζευξη με απομακρυσμένες συσκευές."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Επιτρέπει στην εφαρμογή να διαμορφώνει το τοπικό Bluetooth στην τηλεόραση, καθώς και να ανακαλύπτει απομακρυσμένες συσκευές και να συνδέεται μαζί τους."</string>
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Επιτρέπει στην εφαρμογή τη διαμόρφωση του τοπικού tablet Bluetooth, τον εντοπισμό και τη σύζευξη με απομακρυσμένες συσκευές."</string>
@@ -425,17 +425,17 @@
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Επιτρέπει στην εφαρμογή τη σύνδεση στο tablet και την αποσύνδεση από αυτό, από δίκτυα WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Επιτρέπει στην εφαρμογή να συνδέει και να αποσυνδέει την τηλεόραση από δίκτυα WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Επιτρέπει στην εφαρμογή τη σύνδεση στο τηλέφωνο και την αποσύνδεση από αυτό, από δίκτυα WiMAX."</string>
- <string name="permlab_bluetooth" msgid="6127769336339276828">"σύζευξη με συσκευές Bluetooth"</string>
+ <string name="permlab_bluetooth" msgid="6127769336339276828">"πραγματοποιεί σύζευξη με συσκευές Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Επιτρέπει στην εφαρμογή να προβάλλει τη διαμόρφωση του Bluetooth στο tablet, καθώς και να πραγματοποιεί και να αποδέχεται συνδέσεις με συνδεδεμένες συσκευές."</string>
<string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"Επιτρέπει στην εφαρμογή να προβάλλει τη διαμόρφωση του Bluetooth στην τηλεόραση, καθώς και να δημιουργεί και να αποδέχεται συνδέσεις με συσκευές σε σύζευξη."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Επιτρέπει στην εφαρμογή να προβάλλει τη διαμόρφωση του Bluetooth στο τηλέφωνο, καθώς και να πραγματοποιεί και να αποδέχεται συνδέσεις με συνδεδεμένες συσκευές."</string>
- <string name="permlab_nfc" msgid="4423351274757876953">"έλεγχος Επικοινωνίας κοντινού πεδίου (Near Field Communication)"</string>
+ <string name="permlab_nfc" msgid="4423351274757876953">"ελέγχει την Επικοινωνία κοντινού πεδίου (FNC)"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"Επιτρέπει στην εφαρμογή την επικοινωνία με ετικέτες, κάρτες και αναγνώστες της Επικοινωνίας κοντινού πεδίου (NFC)."</string>
- <string name="permlab_disableKeyguard" msgid="3598496301486439258">"απενεργοποίηση κλειδώματος οθόνης"</string>
+ <string name="permlab_disableKeyguard" msgid="3598496301486439258">"απενεργοποιεί το κλείδωμα οθόνης"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Επιτρέπει στην εφαρμογή την απενεργοποίηση του κλειδώματος πληκτρολογίου και άλλης σχετικής ασφάλειας με κωδικό πρόσβασης. Για παράδειγμα, το κλείδωμα πληκτρολογίου στο τηλέφωνο απενεργοποιείται όταν λαμβάνεται εισερχόμενη τηλεφωνική κλήση και ενεργοποιείται ξανά όταν η κλήση τερματιστεί."</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"διαχείριση εξοπλισμού μοναδικού χαρακτηριστικού"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"διαχειρίζεται τον εξοπλισμό δακτυλικού αποτυπώματος"</string>
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Επιτρέπει στην εφαρμογή να επικαλείται μεθόδους για την προσθήκη και τη διαγραφή προτύπων μοναδικού χαρακτηριστικού για χρήση."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"χρήση εξοπλισμού μοναδικού χαρακτηριστικού"</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"χρησιμοποιεί τον εξοπλισμό δακτυλικού αποτυπώματος"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί εξοπλισμό μοναδικού χαρακτηριστικού για έλεγχο ταυτότητας"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Εντοπίστηκε μερικό μοναδικό χαρακτηριστικό. Δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Δεν ήταν δυνατή η επεξεργασία του μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
@@ -454,65 +454,65 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string>
- <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ανάγνωση ρυθμίσεων συγχρονισμού"</string>
+ <string name="permlab_readSyncSettings" msgid="6201810008230503052">"διαβάζει τις ρυθμίσεις συγχρονισμού"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Επιτρέπει στην εφαρμογή την ανάγνωση των ρυθμίσεων συγχρονισμού για έναν λογαριασμό. Για παράδειγμα, αυτό μπορεί να καθορίσει εάν η εφαρμογή \"Άτομα\" είναι συγχρονισμένη με έναν λογαριασμό."</string>
- <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"εναλλαγή ενεργοποίησης και απενεργοποίησης συγχρονισμού"</string>
+ <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ενεργοποιεί/απενεργοποιεί τον συγχρονισμό"</string>
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Επιτρέπει σε μια εφαρμογή την τροποποίηση των ρυθμίσεων συγχρονισμού για έναν λογαριασμό. Για παράδειγμα, αυτό μπορεί να χρησιμοποιηθεί για να ενεργοποιηθεί ο συγχρονισμός της εφαρμογής \"Άτομα\" με έναν λογαριασμό."</string>
- <string name="permlab_readSyncStats" msgid="7396577451360202448">"ανάγνωση στατιστικών συγχρονισμού"</string>
+ <string name="permlab_readSyncStats" msgid="7396577451360202448">"διαβάζει στατιστικά στοιχεία συγχρονισμού"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Επιτρέπει σε μια εφαρμογή την ανάγνωση των στατιστικών στοιχείων συγχρονισμού για έναν λογαριασμό, συμπεριλαμβανομένων του ιστορικού των συμβάντων συγχρονισμού και του όγκου των δεδομένων που συγχρονίζονται."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ανάγν. περιεχ. αποθηκ. χώρ.USB"</string>
- <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ανάγνωση του περιεχομένου της κάρτας SD"</string>
+ <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"διαβάζει το περιεχομένο της κάρτας SD"</string>
<string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Επιτρέπει στην εφαρμογή την ανάγνωση του περιεχομένου του αποθηκευτικού σας χώρου USB."</string>
<string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Επιτρέπει στην εφαρμογή την ανάγνωση του περιεχομένου της κάρτας SD."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"τροποποίηση ή διαγραφή του USB"</string>
- <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"τροποποίηση ή διαγραφή των περιεχομένων της κάρτας SD"</string>
+ <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"τροποποιεί/διαγράφει το USB"</string>
+ <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"τροποποιεί ή διαγράφει τα περιεχόμενα της κάρτας SD"</string>
<string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Επιτρέπει στην εφαρμογή την εγγραφή στον αποθηκευτικό χώρο USB."</string>
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Επιτρέπει στην εφαρμογή την εγγραφή στην κάρτα SD."</string>
- <string name="permlab_use_sip" msgid="2052499390128979920">"πραγματοποίηση/λήψη κλήσεων SIP"</string>
+ <string name="permlab_use_sip" msgid="2052499390128979920">"πραγματοποιεί/λαμβάνει κλήσεις SIP"</string>
<string name="permdesc_use_sip" msgid="2297804849860225257">"Επιτρέπει στην εφαρμογή να πραγματοποιεί και να λαμβάνει κλήσεις SIP."</string>
- <string name="permlab_register_sim_subscription" msgid="3166535485877549177">"εγγραφή νέων συνδέσεων SIM τηλεπικοινωνιών"</string>
+ <string name="permlab_register_sim_subscription" msgid="3166535485877549177">"πραγματοποιεί εγγραφή νέων συνδέσεων SIM τηλεπικοινωνιών"</string>
<string name="permdesc_register_sim_subscription" msgid="2138909035926222911">"Επιτρέπει στην εφαρμογή την εγγραφή νέων συνδέσεων SIM τηλεπικοινωνιών."</string>
- <string name="permlab_register_call_provider" msgid="108102120289029841">"εγγραφή νέων συνδέσεων τηλεπικοινωνιών"</string>
+ <string name="permlab_register_call_provider" msgid="108102120289029841">"πραγματοποιεί εγγραφή των νέων συνδέσεων τηλεπικοινωνιών"</string>
<string name="permdesc_register_call_provider" msgid="7034310263521081388">"Επιτρέπει στην εφαρμογή την εγγραφή νέων συνδέσεων τηλεπικοινωνιών."</string>
- <string name="permlab_connection_manager" msgid="1116193254522105375">"διαχείριση των συνδέσεων τηλεπικοινωνιών"</string>
+ <string name="permlab_connection_manager" msgid="1116193254522105375">"διαχειρίζεται τις συνδέσεις τηλεπικοινωνιών"</string>
<string name="permdesc_connection_manager" msgid="5925480810356483565">"Επιτρέπει στην εφαρμογή να διαχειρίζεται τις συνδέσεις τηλεπικοινωνιών."</string>
- <string name="permlab_bind_incall_service" msgid="6773648341975287125">"αλληλεπίδραση με την οθόνη κατά τη διάρκεια κλήσης"</string>
+ <string name="permlab_bind_incall_service" msgid="6773648341975287125">"αλληλεπιδρά με την οθόνη κατά τη διάρκεια κλήσης"</string>
<string name="permdesc_bind_incall_service" msgid="8343471381323215005">"Επιτρέπει στην εφαρμογή να ελέγχει πότε και πώς βλέπει ο χρήστης την οθόνη κατά τη διάρκεια κλήσης."</string>
- <string name="permlab_bind_connection_service" msgid="3557341439297014940">"αλληλεπίδραση με υπηρεσίες τηλεφωνίας"</string>
+ <string name="permlab_bind_connection_service" msgid="3557341439297014940">"αλληλεπιδρά με υπηρεσίες τηλεφωνίας"</string>
<string name="permdesc_bind_connection_service" msgid="4008754499822478114">"Επιτρέπει στην εφαρμογή να αλληλεπιδρά με υπηρεσίες τηλεφωνίας για την πραγματοποίηση/λήψη κλήσεων."</string>
- <string name="permlab_control_incall_experience" msgid="9061024437607777619">"παροχή εμπειρίας χρήστη κατά τη διάρκεια κλήσης"</string>
+ <string name="permlab_control_incall_experience" msgid="9061024437607777619">"παρέχει εμπειρία χρήστη κατά τη διάρκεια κλήσης"</string>
<string name="permdesc_control_incall_experience" msgid="915159066039828124">"Επιτρέπει στην εφαρμογή να παρέχει μια εμπειρία στο χρήστη κατά τη διάρκεια κλήσης."</string>
- <string name="permlab_readNetworkUsageHistory" msgid="7862593283611493232">"ανάγνωση ιστορικών δεδομένων χρήσης δικτύου"</string>
+ <string name="permlab_readNetworkUsageHistory" msgid="7862593283611493232">"διαβάζει ιστορικά στοιχεία δεδομένων χρήσης δικτύου"</string>
<string name="permdesc_readNetworkUsageHistory" msgid="7689060749819126472">"Επιτρέπει στην εφαρμογή την ανάγνωση ιστορικών στοιχείων χρήσης δικτύου για συγκεκριμένα δίκτυα και εφαρμογές."</string>
- <string name="permlab_manageNetworkPolicy" msgid="2562053592339859990">"διαχείριση πολιτικής δικτύου"</string>
+ <string name="permlab_manageNetworkPolicy" msgid="2562053592339859990">"διαχειρίζεται την πολιτική δικτύου"</string>
<string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Επιτρέπει στην εφαρμογή τη διαχείριση των πολιτικών δικτύου και τον ορισμό κανόνων για ορισμένες εφαρμογές."</string>
- <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"τροποποίηση υπολογισμού χρήσης δικτύου"</string>
+ <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"τροποποιεί τον υπολογισμό χρήσης δικτύου"</string>
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Επιτρέπει στην εφαρμογή την τροποποίηση του τρόπου υπολογισμού της χρήσης δικτύου έναντι των εφαρμογών. Δεν προορίζεται για χρήση από συνήθεις εφαρμογές."</string>
- <string name="permlab_accessNotifications" msgid="7673416487873432268">"πρόσβαση στις ειδοποιήσεις"</string>
+ <string name="permlab_accessNotifications" msgid="7673416487873432268">"έχει πρόσβαση στις ειδοποιήσεις"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Επιτρέπει στην εφαρμογή να ανακτά, να εξετάζει και να απαλείφει ειδοποιήσεις, συμπεριλαμβανομένων εκείνων που δημοσιεύονται από άλλες εφαρμογές."</string>
- <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"δέσμευση σε υπηρεσία ακρόασης ειδοποίησης"</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"συνδέεται σε υπηρεσία ακρόασης ειδοποίησης"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας υπηρεσίας ακρόασης ειδοποιήσεων. Δεν απαιτείται σε κανονικές εφαρμογές."</string>
- <string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"σύνδεση σε μια υπηρεσία παρόχου συνθηκών"</string>
+ <string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"δεσμεύεται σε μια υπηρεσία παρόχου συνθηκών"</string>
<string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"Επιτρέπει στον κάτοχο τη σύνδεση στη διεπαφή ανωτάτου επιπέδου ενός παρόχου συνθηκών. Δεν απαιτείται για κανονικές εφαρμογές."</string>
- <string name="permlab_bindDreamService" msgid="4153646965978563462">"δέσμευση σε υπηρεσία dream"</string>
+ <string name="permlab_bindDreamService" msgid="4153646965978563462">"δεσμεύεται σε υπηρεσία dream"</string>
<string name="permdesc_bindDreamService" msgid="7325825272223347863">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας υπηρεσίας dream. Δεν απαιτείται σε κανονικές εφαρμογές."</string>
- <string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"κλήση της εφαρμογής διαμόρφωσης που παρέχεται από την εταιρεία κινητής τηλεφωνίας"</string>
+ <string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"καλέι την εφαρμογή διαμόρφωσης του παρόχου κινητής τηλεφωνίας"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Επιτρέπει στον κάτοχο την κλήση της εφαρμογής διαμόρφωσης που παρέχεται από την εταιρεία κινητής τηλεφωνίας. Δεν απαιτείται για κανονικές εφαρμογές."</string>
- <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"λήψη παρατηρήσεων σχετικά με την κατάσταση δικτύου"</string>
+ <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"ανιχνεύει παρατηρήσεις σχετικά με την κατάσταση δικτύου"</string>
<string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Επιτρέπει σε μια εφαρμογή να λαμβάνει παρατηρήσεις σχετικά με την κατάσταση δικτύου. Δεν θα πρέπει να απαιτείται ποτέ για κανονικές εφαρμογές."</string>
<string name="permlab_setInputCalibration" msgid="4902620118878467615">"αλλαγή βαθμονόμησης της συσκευής εισόδου"</string>
<string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Επιτρέπει στην εφαρμογή να τροποποιεί τις παραμέτρους βαθμονόμησης της οθόνης αφής. Δεν απαιτείται για τις κανονικές εφαρμογές."</string>
- <string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"πρόσβαση σε πιστοποιητικά DRM"</string>
+ <string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"έχει πρόσβαση σε πιστοποιητικά DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"Επιτρέπει σε μια εφαρμογή να παρέχει και να χρησιμοποιεί πιστοποιητικά DRM. Δεν θα χρειαστεί ποτέ για κανονικές εφαρμογές."</string>
<string name="permlab_handoverStatus" msgid="7820353257219300883">"λήψη κατάστασης μεταφοράς Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Επιτρέπει σε αυτήν την εφαρμογή να λαμβάνει πληροφορίες σχετικά με τις τρέχουσες μεταφορές Android Beam"</string>
- <string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"κατάργηση πιστοποιητικών DRM"</string>
+ <string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"καταργεί πιστοποιητικά DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Επιτρέπει σε μια εφαρμογή την κατάργηση πιστοποιητικών DRM. Δεν χρειάζεται ποτέ για κανονικές εφαρμογές."</string>
- <string name="permlab_bindCarrierMessagingService" msgid="1490229371796969158">"δέσμευση σε υπηρεσία ανταλλαγής μηνυμάτων εταιρείας κινητής τηλεφωνίας"</string>
+ <string name="permlab_bindCarrierMessagingService" msgid="1490229371796969158">"δεσμεύεται σε υπηρεσία ανταλλαγής μηνυμάτων παρόχου κινητής τηλεφωνίας"</string>
<string name="permdesc_bindCarrierMessagingService" msgid="2762882888502113944">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας υπηρεσίας ανταλλαγής μηνυμάτων εταιρείας κινητής τηλεφωνίας. Δεν απαιτείται για συνήθεις εφαρμογές."</string>
- <string name="permlab_bindCarrierServices" msgid="3233108656245526783">"δέσμευση σε υπηρεσίες εταιρείας κινητής τηλεφωνίας"</string>
+ <string name="permlab_bindCarrierServices" msgid="3233108656245526783">"δεσμεύεται σε υπηρεσίες του παρόχου κινητής τηλεφωνίας"</string>
<string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Δίνει στον κάτοχο τη δυνατότητα δέσμευσης σε υπηρεσίες εταιρείας κινητής τηλεφωνίας. Δεν απαιτείται ποτέ για κανονικές εφαρμογές."</string>
- <string name="permlab_access_notification_policy" msgid="4247510821662059671">"πρόσβαση στη λειτουργία \"Μην ενοχλείτε\""</string>
+ <string name="permlab_access_notification_policy" msgid="4247510821662059671">"έχει πρόσβαση στη λειτουργία \"Μην ενοχλείτε\""</string>
<string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Επιτρέπει στην εφαρμογή την εγγραφή και τη σύνταξη διαμόρφωσης για τη λειτουργία \"Μην ενοχλείτε\"."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Ορισμός κανόνων κωδικού πρόσβασης"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"Ελέγξτε την έκταση και τους επιτρεπόμενους χαρακτήρες σε κωδικούς πρόσβασης κλειδώματος οθόνης και PIN."</string>
@@ -792,17 +792,17 @@
<string name="autofill_parish" msgid="8202206105468820057">"Ενορία"</string>
<string name="autofill_area" msgid="3547409050889952423">"Περιοχή"</string>
<string name="autofill_emirate" msgid="2893880978835698818">"Εμιράτο"</string>
- <string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"ανάγνωση των σελιδοδεικτών και του ιστορικού ιστού σας"</string>
+ <string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"διαβάζει τους σελιδοδείκτες και το ιστορικού ιστού"</string>
<string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Επιτρέπει στην εφαρμογή την ανάγνωση του ιστορικού όλων των διευθύνσεων URL που έχει επισκεφτεί το πρόγραμμα περιήγησης, καθώς και όλων των σελιδοδεικτών του προγράμματος περιήγησης. Σημείωση: αυτή η άδεια ίσως να μην μπορεί να εφαρμοστεί από τρίτα προγράμματα περιήγησης ή άλλες εφαρμογές με δυνατότητες περιήγησης ιστού."</string>
- <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"εγγραφή σελιδοδεικτών και ιστορικού ιστού"</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"εγγράφει σελιδοδείκτες και ιστορικό ιστού"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Επιτρέπει στην εφαρμογή την τροποποίηση του ιστορικού του προγράμματος περιήγησης ή των σελιδοδεικτών που έχουν αποθηκευτεί στο tablet σας. Αυτό μπορεί να δίνει τη δυνατότητα στην εφαρμογή να διαγράφει ή να τροποποιεί δεδομένα του προγράμματος περιήγησης. Σημείωση: αυτή η άδεια ίσως να μην μπορεί να εφαρμοστεί από τρίτα προγράμματα περιήγησης ή άλλες εφαρμογές με δυνατότητες περιήγησης ιστού."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Επιτρέπει στην εφαρμογή να τροποποιεί το ιστορικό του προγράμματος περιήγησης ή τους σελιδοδείκτες που είναι αποθηκευμένοι στην τηλεόρασή σας. Σημείωση: Αυτό το δικαίωμα δεν πρέπει να εφαρμόζεται από τρίτα προγράμματα περιήγησης ή άλλες εφαρμογές με δυνατότητες περιήγησης στον ιστό."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Επιτρέπει στην εφαρμογή την τροποποίηση του ιστορικού του προγράμματος περιήγησης ή των σελιδοδεικτών που έχουν αποθηκευτεί στο τηλέφωνό σας. Αυτό μπορεί να δίνει τη δυνατότητα στην εφαρμογή να διαγράφει ή να τροποποιεί δεδομένα του προγράμματος περιήγησης. Σημείωση: αυτή η άδεια ίσως να μην μπορεί να εφαρμοστεί από τρίτα προγράμματα περιήγησης ή άλλες εφαρμογές με δυνατότητες περιήγησης ιστού."</string>
- <string name="permlab_setAlarm" msgid="1379294556362091814">"ρύθμιση ξυπνητηριού"</string>
+ <string name="permlab_setAlarm" msgid="1379294556362091814">"ρυθμίζει το ξυπνητήρι"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Επιτρέπει στην εφαρμογή τη ρύθμιση μιας ειδοποίησης σε μια εγκατεστημένη εφαρμογή ξυπνητηριού. Ορισμένες εφαρμογές ξυπνητηριού ενδέχεται να μην μπορούν να ενσωματώσουν αυτήν τη λειτουργία."</string>
- <string name="permlab_addVoicemail" msgid="5525660026090959044">"προσθήκη τηλεφωνητή"</string>
+ <string name="permlab_addVoicemail" msgid="5525660026090959044">"προσθέτει τηλεφωνητή"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Επιτρέπει στην εφαρμογή να προσθέτει μηνύματα στα εισερχόμενα του αυτόματου τηλεφωνητή σας."</string>
- <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"τροποποίηση δικαιωμάτων γεωγραφικής θέσης του Προγράμματος περιήγησης"</string>
+ <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"τροποποιεί δικαιώματα γεωγραφικής θέσης του Προγράμματος περιήγησης"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Επιτρέπει στην εφαρμογή την τροποποίηση των αδειών γεωτοποθεσίας του Προγράμματος περιήγησης. Τυχόν κακόβουλες εφαρμογές ενδέχεται να το χρησιμοποιήσουν για να επιτρέψουν την αποστολή πληροφοριών τοποθεσίας σε αυθαίρετους ιστότοπους."</string>
<string name="save_password_message" msgid="767344687139195790">"Θέλετε το πρόγραμμα περιήγησης να διατηρήσει αυτόν τον κωδικό πρόσβασης;"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Να μην γίνει τώρα"</string>
@@ -1202,11 +1202,11 @@
<string name="ext_media_status_formatting" msgid="1085079556538644861">"Διαμόρφωση…"</string>
<string name="ext_media_status_missing" msgid="5638633895221670766">"Δεν έχει εισαχθεί"</string>
<string name="activity_list_empty" msgid="1675388330786841066">"Δεν βρέθηκαν δραστηριότητες που να συμφωνούν με τα κριτήρια."</string>
- <string name="permlab_route_media_output" msgid="6243022988998972085">"δρομολόγηση εξόδου μέσων"</string>
+ <string name="permlab_route_media_output" msgid="6243022988998972085">"δρομολογεί την έξοδο μέσων"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"Επιτρέπει σε μια εφαρμογή τη διαγραφή διαδρομής δεδομένων εξόδου μέσων σε άλλες εξωτερικές συσκευές."</string>
- <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ανάγνωση περιόδων σύνδεσης εγκατάστασης"</string>
+ <string name="permlab_readInstallSessions" msgid="3713753067455750349">"διαβάζει τις περιόδους σύνδεσης εγκατάστασης"</string>
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Επιτρέπει σε μια εφαρμογή την ανάγνωση των περιόδων σύνδεσης εγκατάστασης. Αυτό της επιτρέπει να βλέπει λεπτομέρειες σχετικά με τις εγκαταστάσεις του ενεργού πακέτου."</string>
- <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"αίτημα εγκατάστασης πακέτων"</string>
+ <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ζητά πακέτα εγκατάστασης"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Επιτρέπει σε μια εφαρμογή να ζητά εγκατάσταση πακέτων."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Πατήστε δύο φορές για έλεγχο εστίασης"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Δεν ήταν δυνατή η προσθήκη του γραφικού στοιχείου."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 09b55d9..1e5f6a1 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1570,7 +1570,7 @@
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Supprimé par votre administrateur"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Pour améliorer l\'autonomie de la batterie, l\'économiseur de batterie réduit les performances et désactive le vibreur, les services de localisation et la plupart des données en arrière-plan. Les messageries électroniques ou autres applications utilisant la synchronisation pourraient ne pas se mettre à jour, sauf si vous les ouvrez.\n\nL\'économiseur de batterie s\'éteint automatiquement lorsque l\'appareil est en charge."</string>
<string name="data_saver_description" msgid="6015391409098303235">"Pour réduire la consommation des données, l\'économiseur de données empêche certaines applications d\'envoyer ou de recevoir des données en arrière-plan. Ainsi, une application que vous utilisez actuellement peut accéder à des données, mais moins souvent. Par exemple, il se peut que les images ne s\'affichent pas tant que vous n\'appuyez pas dessus."</string>
- <string name="data_saver_enable_title" msgid="4674073932722787417">"Activer sauvegarde données ?"</string>
+ <string name="data_saver_enable_title" msgid="4674073932722787417">"Activer l\'économiseur de données ?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"Activer"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">Pendant %1$d minute (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 7cdc9bb..5a1ad70 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -214,7 +214,7 @@
<string name="global_actions" product="default" msgid="2406416831541615258">"Հեռախոսի ընտրանքներ"</string>
<string name="global_action_lock" msgid="2844945191792119712">"Էկրանի փական"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"Անջատել"</string>
- <string name="global_action_emergency" msgid="7112311161137421166">"Արտակարգ իրավիճակ"</string>
+ <string name="global_action_emergency" msgid="7112311161137421166">"Շտապ կանչ"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Վրիպակի զեկույց"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Գրել սխալի զեկույց"</string>
<string name="bugreport_message" msgid="398447048750350456">"Սա տեղեկություններ կհավաքագրի ձեր սարքի առկա կարգավիճակի մասին և կուղարկի այն էլեկտրոնային նամակով: Որոշակի ժամանակ կպահանջվի վրիպակի մասին զեկուցելու պահից սկսած մինչ ուղարկելը: Խնդրում ենք փոքր-ինչ համբերատար լինել:"</string>
@@ -250,7 +250,7 @@
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Օրացույց"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"օգտագործել օրացույցը"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"Կարճ հաղորդագրություն"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"ուղարկել և դիտել SMS հաղորդ․-ները"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"ուղարկել և դիտել SMS-ները"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Պահոց"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"օգտագործել լուսանկարները, մեդիա ֆայլերը և ձեր սարքում պահվող մյուս ֆայլերը"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Բարձրախոս"</string>
@@ -674,7 +674,7 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Ապակողպելու կամ շտապ կանչ անելու համար սեղմեք «Ընտրացանկ»"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Ապակողպելու համար սեղմեք Ցանկը:"</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Հավաքեք սխեման` ապակողպելու համար"</string>
- <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Արտակարգ իրավիճակ"</string>
+ <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Շտապ կանչ"</string>
<string name="lockscreen_return_to_call" msgid="5244259785500040021">"Վերադառնալ զանգին"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Ճիշտ է:"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Կրկին փորձեք"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index d655477..05fe8d0 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -254,7 +254,7 @@
<string name="permgrouplab_storage" msgid="1971118770546336966">"Penyimpanan"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"mengakses foto, media, dan file di perangkat"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"rekam audio"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"merekam audio"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"mengambil gambar dan merekam video"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telepon"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 5305b1f..02736ec 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -512,8 +512,8 @@
<string name="permdesc_bindCarrierMessagingService" msgid="2762882888502113944">"携帯通信会社のSMSサービスのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
<string name="permlab_bindCarrierServices" msgid="3233108656245526783">"携帯通信会社のサービスへのバインド"</string>
<string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"携帯通信会社のサービスにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
- <string name="permlab_access_notification_policy" msgid="4247510821662059671">"[通知を非表示]へのアクセス"</string>
- <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"[通知を非表示]の設定の読み取りと書き込みをアプリに許可します。"</string>
+ <string name="permlab_access_notification_policy" msgid="4247510821662059671">"マナーモードへのアクセス"</string>
+ <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"マナーモード設定の読み取りと書き込みをアプリに許可します。"</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"パスワードルールの設定"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"画面ロックのパスワードとPINの長さと使用できる文字を制御します。"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"画面ロック解除試行の監視"</string>
@@ -1607,10 +1607,10 @@
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>まで"</string>
<string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(次のアラーム)まで"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"ユーザーがOFFにするまで"</string>
- <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"[通知を非表示]をOFFにするまで"</string>
+ <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"マナーモードを OFF にするまで"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
<string name="toolbar_collapse_description" msgid="2821479483960330739">"折りたたむ"</string>
- <string name="zen_mode_feature_name" msgid="5254089399895895004">"通知を非表示"</string>
+ <string name="zen_mode_feature_name" msgid="5254089399895895004">"マナーモード"</string>
<string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"ダウンタイム"</string>
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"平日の夜"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"週末"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index b8c90d7..732a321 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -958,9 +958,9 @@
<string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಚಾಲನೆಯಲ್ಲಿದೆ"</string>
<string name="app_running_notification_text" msgid="1197581823314971177">"ಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ ಅಥವಾ ಅಪ್ಲಿಕೇಶನ್ ನಿಲ್ಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="ok" msgid="5970060430562524910">"ಸರಿ"</string>
- <string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡು"</string>
+ <string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡಿ"</string>
<string name="yes" msgid="5362982303337969312">"ಸರಿ"</string>
- <string name="no" msgid="5141531044935541497">"ರದ್ದುಮಾಡು"</string>
+ <string name="no" msgid="5141531044935541497">"ರದ್ದುಮಾಡಿ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ಗಮನಿಸಿ"</string>
<string name="loading" msgid="7933681260296021180">"ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
<string name="capital_on" msgid="1544682755514494298">"ಆನ್ ಮಾಡು"</string>
@@ -1118,7 +1118,7 @@
<string name="sms_short_code_details" msgid="5873295990846059400">"ಇದು ನಿಮ್ಮ ಮೊಬೈಲ್ ಖಾತೆಯಲ್ಲಿ "<b>"ಶುಲ್ಕಗಳನ್ನು ವಿಧಿಸುವುದಕ್ಕೆ ಕಾರಣವಾಗಬಹುದು"</b>"."</string>
<string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"ಇದು ನಿಮ್ಮ ಮೊಬೈಲ್ ಖಾತೆಯಲ್ಲಿ ಶುಲ್ಕಗಳನ್ನು ವಿಧಿಸುವುದಕ್ಕೆ ಕಾರಣವಾಗುತ್ತದೆ."</b></string>
<string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"ಕಳುಹಿಸು"</string>
- <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"ರದ್ದುಮಾಡು"</string>
+ <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"ರದ್ದುಮಾಡಿ"</string>
<string name="sms_short_code_remember_choice" msgid="5289538592272218136">"ನನ್ನ ಆಯ್ಕೆಯನ್ನು ನೆನಪಿಡು"</string>
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"ನೀವು ಇದನ್ನು ನಂತರದಲ್ಲಿ ಸೆಟ್ಟಿಂಗ್ಗಳು > ಅಪ್ಲಿಕೇಶನ್ಗಳಲ್ಲಿ ಬದಲಾಯಿಸಬಹುದು"</string>
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"ಯಾವಾಗಲೂ ಅನುಮತಿಸು"</string>
@@ -1303,7 +1303,7 @@
<string name="date_picker_prev_month_button" msgid="2858244643992056505">"ಹಿಂದಿನ ತಿಂಗಳು"</string>
<string name="date_picker_next_month_button" msgid="5559507736887605055">"ಮುಂದಿನ ತಿಂಗಳು"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
- <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ರದ್ದುಮಾಡು"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ರದ್ದುಮಾಡಿ"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ಅಳಿಸು"</string>
<string name="keyboardview_keycode_done" msgid="1992571118466679775">"ಮುಗಿದಿದೆ"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ಮೋಡ್ ಬದಲಾವಣೆ"</string>
@@ -1673,7 +1673,7 @@
<string name="demo_restarting_message" msgid="952118052531642451">"ಸಾಧನ ಮರುಹೊಂದಿಸಲಾಗುತ್ತಿದೆ..."</string>
<string name="demo_user_inactivity_timeout_title" msgid="6596109959002331334">"ಸಾಧನವನ್ನು ಮರುಹೊಂದಿಸುವುದೇ?"</string>
<string name="demo_user_inactivity_timeout_countdown" msgid="5675588824402569506">"ನೀವು ಯಾವುದೇ ಬದಲಾವಣೆಗಳನ್ನು ಕಳೆದುಕೊಳ್ಳುತ್ತೀರಿ ಮತ್ತು <xliff:g id="TIMEOUT">%1$s</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಡೆಮೋ ಮತ್ತೆ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ..."</string>
- <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"ರದ್ದುಮಾಡು"</string>
+ <string name="demo_user_inactivity_timeout_left_button" msgid="5314271347014802475">"ರದ್ದುಮಾಡಿ"</string>
<string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ಈಗಲೇ ಮರುಹೊಂದಿಸು"</string>
<string name="audit_safemode_notification" msgid="6416076898350685856">"ನಿರ್ಬಂಧಗಳು ಇಲ್ಲದೆಯೇ ಈ ಸಾಧನವನ್ನು ಬಳಸಲು ಫ್ಯಾಕ್ಟರಿ ಮರುಹೊಂದಿಸಿ"</string>
<string name="audit_safemode_notification_details" msgid="1860601176690176413">"ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಸ್ಪರ್ಶಿಸಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 4191865..f0cfd0b 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -260,7 +260,7 @@
<string name="permgrouplab_phone" msgid="5229115638567440675">"전화"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"통화 상태를 관리하거나 전화를 걸 수 있도록"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"신체 센서"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"생체 신호에 관한 센서 데이터에 접근할 수 있도록"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"생체 신호에 관한 센서 데이터에 액세스"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"창 콘텐츠 가져오기"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"상호작용 중인 창의 콘텐츠를 검사합니다."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"터치하여 탐색 사용"</string>
diff --git a/core/res/res/values-ldrtl-television/config.xml b/core/res/res/values-ldrtl-television/config.xml
index e237acc..503b902 100644
--- a/core/res/res/values-ldrtl-television/config.xml
+++ b/core/res/res/values-ldrtl-television/config.xml
@@ -21,7 +21,8 @@
for TV products. Do not translate. -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
- <string translatable="false" name="config_defaultPictureInPictureBounds">"112 54 592 324"</string>
+ <!-- The default gravity for the picture-in-picture window.
+ Currently, this maps to Gravity.TOP | Gravity.LEFT -->
+ <integer name="config_defaultPictureInPictureGravity">0x33</integer>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index a2f5eb8..c7806f2 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1105,7 +1105,7 @@
<string name="volume_icon_description_media" msgid="4217311719665194215">"Medijos garsumas"</string>
<string name="volume_icon_description_notification" msgid="7044986546477282274">"Pranešimo apimtis"</string>
<string name="ringtone_default" msgid="3789758980357696936">"Numatytasis skambėjimo tonas"</string>
- <string name="ringtone_default_with_actual" msgid="8129563480895990372">"Numatytasis skambėjimo tonas (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
+ <string name="ringtone_default_with_actual" msgid="8129563480895990372">"Numatytasis skambėjimo tonas („<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>“)"</string>
<string name="ringtone_silent" msgid="7937634392408977062">"Nėra"</string>
<string name="ringtone_picker_title" msgid="3515143939175119094">"Skambėjimo tonai"</string>
<string name="ringtone_unknown" msgid="5477919988701784788">"Nežinomas skambėjimo tonas"</string>
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
index ddf0e9f..1d7a45b 100755
--- a/core/res/res/values-mcc204-mnc04/config.xml
+++ b/core/res/res/values-mcc204-mnc04/config.xml
@@ -32,10 +32,5 @@
-->
<integer name="config_LTE_RSRP_threshold_type">0</integer>
- <string-array translatable="false" name="config_sms_convert_destination_number_support">
- <item>true;BAE0000000000000</item>
- <item>false</item>
- </string-array>
-
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true;BAE0000000000000</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc004/config.xml b/core/res/res/values-mcc310-mnc004/config.xml
index cbe5145..63431a4 100755
--- a/core/res/res/values-mcc310-mnc004/config.xml
+++ b/core/res/res/values-mcc310-mnc004/config.xml
@@ -35,8 +35,5 @@
<bool name="config_auto_attach_data_on_creation">false</bool>
- <string-array translatable="false" name="config_sms_convert_destination_number_support">
- <item>true</item>
- </string-array>
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
</resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index 439f1d6..a0a361b 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -57,9 +57,6 @@
-->
<integer name="config_LTE_RSRP_threshold_type">0</integer>
- <string-array translatable="false" name="config_sms_convert_destination_number_support">
- <item>true</item>
- </string-array>
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 9ff7803..0371543 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1328,7 +1328,7 @@
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-stasjon"</string>
<string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB-stasjon"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB-lagring"</string>
- <string name="extract_edit_menu_button" msgid="8940478730496610137">"Rediger"</string>
+ <string name="extract_edit_menu_button" msgid="8940478730496610137">"Endre"</string>
<string name="data_usage_warning_title" msgid="3620440638180218181">"Varsel om databruk"</string>
<string name="data_usage_warning_body" msgid="6660692274311972007">"Trykk for å se bruken og innstillingene."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Datagrensen for 2G-3G er nådd"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 66f085f..ac74164 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -667,7 +667,7 @@
<string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Ange lösenord för att låsa upp"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Ange PIN-kod för att låsa upp"</string>
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Fel PIN-kod."</string>
- <string name="keyguard_label_text" msgid="861796461028298424">"Tryck på Menu och sedan på 0 om du vill låsa upp."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Tryck på Menu och sedan på 0 för att låsa upp."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nödsamtalsnummer"</string>
<string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ingen tjänst"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skärmen har låsts."</string>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index c0716e9..c27cb06 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -24,6 +24,15 @@
<!-- Flags enabling default window features. See Window.java -->
<bool name="config_defaultWindowFeatureOptionsPanel">false</bool>
- <!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
- <string translatable="false" name="config_defaultPictureInPictureBounds">"1328 54 1808 324"</string>
+ <!-- Max default size [WIDTHxHEIGHT] on screen for picture-in-picture windows to fit inside.
+ These values are in DPs and will be converted to pixel sizes internally. -->
+ <string translatable="false" name="config_defaultPictureInPictureSize">240x135</string>
+
+ <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
+ These values are in DPs and will be converted to pixel sizes internally. -->
+ <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">56x27</string>
+
+ <!-- The default gravity for the picture-in-picture window.
+ Currently, this maps to Gravity.TOP | Gravity.RIGHT -->
+ <integer name="config_defaultPictureInPictureGravity">0x35</integer>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 4c135ab..fb0fda8 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1104,8 +1104,8 @@
<string name="volume_icon_description_incall" msgid="8890073218154543397">"Гучність сигналу виклику"</string>
<string name="volume_icon_description_media" msgid="4217311719665194215">"Гучність медіа"</string>
<string name="volume_icon_description_notification" msgid="7044986546477282274">"Гучність сповіщення"</string>
- <string name="ringtone_default" msgid="3789758980357696936">"Мелодія за умовч."</string>
- <string name="ringtone_default_with_actual" msgid="8129563480895990372">"Мелодія за умовч. (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
+ <string name="ringtone_default" msgid="3789758980357696936">"Мелодія за умовчанням"</string>
+ <string name="ringtone_default_with_actual" msgid="8129563480895990372">"Мелодія за умовчанням (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
<string name="ringtone_silent" msgid="7937634392408977062">"Немає"</string>
<string name="ringtone_picker_title" msgid="3515143939175119094">"Мелодії"</string>
<string name="ringtone_unknown" msgid="5477919988701784788">"Невідома мелодія"</string>
diff --git a/core/res/res/values-watch/colors_device_defaults.xml b/core/res/res/values-watch/colors_device_defaults.xml
index 9150cc4..15786b4 100644
--- a/core/res/res/values-watch/colors_device_defaults.xml
+++ b/core/res/res/values-watch/colors_device_defaults.xml
@@ -18,4 +18,7 @@
overlaying new theme colors. -->
<resources>
<color name="button_normal_device_default_dark">@color/btn_default_material_dark</color>
+ <!-- Use the same value as for accent_device_default_dark but start with #99,
+ i.e. 60% opacity -->
+ <color name="accent_device_default_dark_60_percent_opacity">#995E97f6</color>
</resources>
diff --git a/core/res/res/values-watch/config_material.xml b/core/res/res/values-watch/config_material.xml
index 104d122..529f18b 100644
--- a/core/res/res/values-watch/config_material.xml
+++ b/core/res/res/values-watch/config_material.xml
@@ -29,4 +29,8 @@
<!-- Always overscan by default to ensure onApplyWindowInsets will always be called. -->
<bool name="config_windowOverscanByDefault">true</bool>
+
+ <!-- Style the scrollbars accoridngly. -->
+ <drawable name="config_scrollbarThumbVertical">@drawable/scrollbar_vertical_thumb</drawable>
+ <drawable name="config_scrollbarTrackVertical">@drawable/scrollbar_vertical_track</drawable>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index b67d6e2..68e87c2 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -195,7 +195,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"振铃器开启"</string>
<string name="reboot_to_update_title" msgid="6212636802536823850">"Android 系统更新"</string>
<string name="reboot_to_update_prepare" msgid="6305853831955310890">"正在准备更新…"</string>
- <string name="reboot_to_update_package" msgid="3871302324500927291">"正在处理更新文件包…"</string>
+ <string name="reboot_to_update_package" msgid="3871302324500927291">"正在处理更新软件包…"</string>
<string name="reboot_to_update_reboot" msgid="6428441000951565185">"正在重新启动…"</string>
<string name="reboot_to_reset_title" msgid="4142355915340627490">"恢复出厂设置"</string>
<string name="reboot_to_reset_message" msgid="2432077491101416345">"正在重新启动…"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c40436a..5df33bd 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1715,6 +1715,12 @@
turned off and the screen off animation has been performed. -->
<bool name="config_dozeAfterScreenOff">false</bool>
+ <!-- Doze: should the TYPE_PICK_UP_GESTURE sensor be used as a pulse signal. -->
+ <bool name="config_dozePulsePickup">false</bool>
+
+ <!-- Type of the double tap sensor. Empty if double tap is not supported. -->
+ <string name="config_dozeDoubleTapSensorType" translatable="false"></string>
+
<!-- Power Management: Specifies whether to decouple the auto-suspend state of the
device from the display on/off state.
@@ -2327,22 +2333,6 @@
default scale-up transition. -->
<bool name="config_overrideRemoteViewsActivityTransition">false</bool>
- <!-- This config is used to check if the carrier requires converting destination
- number before sending out a SMS.
- Formats for this configuration as below:
- [true or false][;optional gid]
- The logic to pick up the configuration:
- (1) If the "config_sms_convert_destination_number_support" array has no gid
- special item, the last one will be picked
- (2) If the "config_sms_convert_destination_number_support" array has gid special
- item and it matches the current sim's gid, it will be picked.
- (3) If the "config_sms_convert_destination_number_support" array has gid special
- item but it doesn't match the current sim's gid, the last one without gid
- will be picked -->
- <string-array translatable="false" name="config_sms_convert_destination_number_support">
- <item>false</item>
- </string-array>
-
<!-- The maximum bitmap size that can be written to a MediaMetadata object. This value
is the max width/height allowed in dips.-->
<dimen name="config_mediaMetadataBitmapMaxSize">320dp</dimen>
@@ -2482,8 +2472,17 @@
-->
<integer name="config_navBarOpacityMode">0</integer>
- <!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
- <string translatable="false" name="config_defaultPictureInPictureBounds">"0 0 100 100"</string>
+ <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
+ These values are in DPs and will be converted to pixel sizes internally. -->
+ <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">10x10</string>
+
+ <!-- Max default size [WIDTHxHEIGHT] on screen for picture-in-picture windows to fit inside.
+ These values are in DPs and will be converted to pixel sizes internally. -->
+ <string translatable="false" name="config_defaultPictureInPictureSize">216x135</string>
+
+ <!-- The default gravity for the picture-in-picture window.
+ Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
+ <integer name="config_defaultPictureInPictureGravity">0x55</integer>
<!-- Controls the snap mode for the docked stack divider
0 - 3 snap targets: left/top has 16:9 ratio, 1:1, and right/bottom has 16:9 ratio
diff --git a/core/res/res/values/config_material.xml b/core/res/res/values/config_material.xml
index 29494db..840a551 100644
--- a/core/res/res/values/config_material.xml
+++ b/core/res/res/values/config_material.xml
@@ -37,4 +37,8 @@
<!-- The amount to offset when scrolling to a selection in an AlertDialog -->
<dimen name="config_alertDialogSelectionScrollOffset">0dp</dimen>
+
+ <!-- Style the scrollbars accoridngly. -->
+ <drawable name="config_scrollbarThumbVertical">@drawable/scrollbar_handle_material</drawable>
+ <drawable name="config_scrollbarTrackVertical">@null</drawable>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 97a679e..48cea84 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -310,7 +310,9 @@
<java-symbol type="bool" name="config_guestUserEphemeral" />
<java-symbol type="bool" name="config_localDisplaysMirrorContent" />
<java-symbol type="bool" name="config_enableAppWidgetService" />
- <java-symbol type="string" name="config_defaultPictureInPictureBounds" />
+ <java-symbol type="string" name="config_defaultPictureInPictureScreenEdgeInsets" />
+ <java-symbol type="string" name="config_defaultPictureInPictureSize" />
+ <java-symbol type="integer" name="config_defaultPictureInPictureGravity" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
@@ -2279,7 +2281,6 @@
<java-symbol type="bool" name="config_overrideRemoteViewsActivityTransition" />
<java-symbol type="layout" name="simple_account_item" />
- <java-symbol type="array" name="config_sms_convert_destination_number_support" />
<java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
<java-symbol type="id" name="profile_button" />
@@ -2678,6 +2679,9 @@
<java-symbol type="string" name="config_emergency_call_number" />
<java-symbol type="array" name="config_emergency_mcc_codes" />
+ <java-symbol type="string" name="config_dozeDoubleTapSensorType" />
+ <java-symbol type="bool" name="config_dozePulsePickup" />
+
<!-- Used for MimeIconUtils. -->
<java-symbol type="drawable" name="ic_doc_apk" />
<java-symbol type="drawable" name="ic_doc_audio" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 0eb4c8d..ff8693b 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -212,9 +212,9 @@
<item name="scrollbarDefaultDelayBeforeFade">400</item>
<item name="scrollbarSize">10dp</item>
<item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_material</item>
- <item name="scrollbarThumbVertical">@drawable/scrollbar_handle_material</item>
+ <item name="scrollbarThumbVertical">@drawable/config_scrollbarThumbVertical</item>
<item name="scrollbarTrackHorizontal">@null</item>
- <item name="scrollbarTrackVertical">@null</item>
+ <item name="scrollbarTrackVertical">@drawable/config_scrollbarTrackVertical</item>
<!-- Text selection handle attributes -->
<item name="textSelectHandleLeft">@drawable/text_select_handle_left_material</item>
@@ -573,9 +573,9 @@
<item name="scrollbarDefaultDelayBeforeFade">400</item>
<item name="scrollbarSize">10dp</item>
<item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_material</item>
- <item name="scrollbarThumbVertical">@drawable/scrollbar_handle_material</item>
+ <item name="scrollbarThumbVertical">@drawable/config_scrollbarThumbVertical</item>
<item name="scrollbarTrackHorizontal">@null</item>
- <item name="scrollbarTrackVertical">@null</item>
+ <item name="scrollbarTrackVertical">@drawable/config_scrollbarTrackVertical</item>
<!-- Text selection handle attributes -->
<item name="textSelectHandleLeft">@drawable/text_select_handle_left_material</item>
diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
new file mode 100644
index 0000000..e9e3a18
--- /dev/null
+++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.app.admin;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/** Unit tests for {@link PasswordMetrics}. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PasswordMetricsTest {
+
+ @Test
+ public void testIsDefault() {
+ final PasswordMetrics metrics = new PasswordMetrics();
+ assertEquals(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, metrics.quality);
+ assertEquals(0, metrics.length);
+ assertEquals(0, metrics.letters);
+ assertEquals(0, metrics.upperCase);
+ assertEquals(0, metrics.lowerCase);
+ assertEquals(0, metrics.numeric);
+ assertEquals(0, metrics.symbols);
+ assertEquals(0, metrics.nonLetter);
+ assertTrue("default constructor does not produce default metrics", metrics.isDefault());
+ }
+
+ @Test
+ public void testIsNotDefault() {
+ final PasswordMetrics metrics = new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, 12);
+ assertFalse("non-default metrics are repoted as default", metrics.isDefault());
+ }
+
+ @Test
+ public void testComputeForEmptyPassword() {
+ final PasswordMetrics metrics = PasswordMetrics.computeForPassword("");
+ assertTrue("empty password has default metrics", metrics.isDefault());
+ }
+
+ @Test
+ public void testParceling() {
+ final int quality = 0;
+ final int length = 1;
+ final int letters = 2;
+ final int upperCase = 3;
+ final int lowerCase = 4;
+ final int numeric = 5;
+ final int symbols = 6;
+ final int nonLetter = 7;
+
+ final Parcel parcel = Parcel.obtain();
+ final PasswordMetrics metrics;
+ try {
+ new PasswordMetrics(
+ quality, length, letters, upperCase, lowerCase, numeric, symbols, nonLetter)
+ .writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ metrics = PasswordMetrics.CREATOR.createFromParcel(parcel);
+ } finally {
+ parcel.recycle();
+ }
+
+ assertEquals(quality, metrics.quality);
+ assertEquals(length, metrics.length);
+ assertEquals(letters, metrics.letters);
+ assertEquals(upperCase, metrics.upperCase);
+ assertEquals(lowerCase, metrics.lowerCase);
+ assertEquals(numeric, metrics.numeric);
+ assertEquals(symbols, metrics.symbols);
+ assertEquals(nonLetter, metrics.nonLetter);
+
+ }
+
+ @Test
+ public void testComputeForPassword_metrics() {
+ final PasswordMetrics metrics = PasswordMetrics.computeForPassword("6B~0z1Z3*8A");
+ assertEquals(11, metrics.length);
+ assertEquals(4, metrics.letters);
+ assertEquals(3, metrics.upperCase);
+ assertEquals(1, metrics.lowerCase);
+ assertEquals(5, metrics.numeric);
+ assertEquals(2, metrics.symbols);
+ assertEquals(7, metrics.nonLetter);
+ }
+
+ @Test
+ public void testComputeForPassword_quality() {
+ assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
+ PasswordMetrics.computeForPassword("a1").quality);
+ assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
+ PasswordMetrics.computeForPassword("a").quality);
+ assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
+ PasswordMetrics.computeForPassword("*~&%$").quality);
+ assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
+ PasswordMetrics.computeForPassword("1").quality);
+ // contains a long sequence so isn't complex
+ assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
+ PasswordMetrics.computeForPassword("1234").quality);
+ assertEquals(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
+ PasswordMetrics.computeForPassword("").quality);
+ }
+
+ @Test
+ public void testMaxLengthSequence() {
+ assertEquals(4, PasswordMetrics.maxLengthSequence("1234"));
+ assertEquals(5, PasswordMetrics.maxLengthSequence("13579"));
+ assertEquals(4, PasswordMetrics.maxLengthSequence("1234abd"));
+ assertEquals(3, PasswordMetrics.maxLengthSequence("aabc"));
+ assertEquals(1, PasswordMetrics.maxLengthSequence("qwertyuio"));
+ assertEquals(3, PasswordMetrics.maxLengthSequence("@ABC"));
+ // anything that repeats
+ assertEquals(4, PasswordMetrics.maxLengthSequence(";;;;"));
+ // ordered, but not composed of alphas or digits
+ assertEquals(1, PasswordMetrics.maxLengthSequence(":;<=>"));
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/DocumentsProviderTest.java b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java
new file mode 100644
index 0000000..0b4675c
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.provider;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.DocumentsContract.Path;
+import android.support.test.filters.SmallTest;
+import android.test.ProviderTestCase2;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link DocumentsProvider}.
+ */
+@SmallTest
+public class DocumentsProviderTest extends ProviderTestCase2<TestDocumentsProvider> {
+
+ private static final String ROOT_ID = "rootId";
+ private static final String DOCUMENT_ID = "docId";
+ private static final String PARENT_DOCUMENT_ID = "parentDocId";
+ private static final String ANCESTOR_DOCUMENT_ID = "ancestorDocId";
+
+ private TestDocumentsProvider mProvider;
+
+ private ContentResolver mResolver;
+
+ public DocumentsProviderTest() {
+ super(TestDocumentsProvider.class, TestDocumentsProvider.AUTHORITY);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mProvider = getProvider();
+ mResolver = getMockContentResolver();
+ }
+
+ public void testFindPath_docUri() throws Exception {
+ final Path expected = new Path(ROOT_ID, Arrays.asList(PARENT_DOCUMENT_ID, DOCUMENT_ID));
+ mProvider.nextPath = expected;
+
+ final Uri docUri =
+ DocumentsContract.buildDocumentUri(TestDocumentsProvider.AUTHORITY, DOCUMENT_ID);
+ try (ContentProviderClient client =
+ mResolver.acquireUnstableContentProviderClient(docUri)) {
+ final Path actual = DocumentsContract.findPath(client, docUri);
+ assertEquals(expected, actual);
+ }
+ }
+
+ public void testFindPath_treeUri() throws Exception {
+ mProvider.nextIsChildDocument = true;
+
+ final Path expected = new Path(null, Arrays.asList(PARENT_DOCUMENT_ID, DOCUMENT_ID));
+ mProvider.nextPath = expected;
+
+ final Uri docUri = buildTreeDocumentUri(
+ TestDocumentsProvider.AUTHORITY, PARENT_DOCUMENT_ID, DOCUMENT_ID);
+ final List<String> actual = DocumentsContract.findPath(mResolver, docUri);
+
+ assertEquals(expected.getPath(), actual);
+ }
+
+ public void testFindPath_treeUri_throwsOnNonChildDocument() throws Exception {
+ mProvider.nextPath = new Path(null, Arrays.asList(PARENT_DOCUMENT_ID, DOCUMENT_ID));
+
+ final Uri docUri = buildTreeDocumentUri(
+ TestDocumentsProvider.AUTHORITY, PARENT_DOCUMENT_ID, DOCUMENT_ID);
+ assertNull(DocumentsContract.findPath(mResolver, docUri));
+ }
+
+ public void testFindPath_treeUri_throwsOnNonNullRootId() throws Exception {
+ mProvider.nextIsChildDocument = true;
+
+ mProvider.nextPath = new Path(ROOT_ID, Arrays.asList(PARENT_DOCUMENT_ID, DOCUMENT_ID));
+
+ final Uri docUri = buildTreeDocumentUri(
+ TestDocumentsProvider.AUTHORITY, PARENT_DOCUMENT_ID, DOCUMENT_ID);
+ assertNull(DocumentsContract.findPath(mResolver, docUri));
+ }
+
+ public void testFindPath_treeUri_throwsOnDifferentParentDocId() throws Exception {
+ mProvider.nextIsChildDocument = true;
+
+ mProvider.nextPath = new Path(
+ null, Arrays.asList(ANCESTOR_DOCUMENT_ID, PARENT_DOCUMENT_ID, DOCUMENT_ID));
+
+ final Uri docUri = buildTreeDocumentUri(
+ TestDocumentsProvider.AUTHORITY, PARENT_DOCUMENT_ID, DOCUMENT_ID);
+ assertNull(DocumentsContract.findPath(mResolver, docUri));
+ }
+
+ private static Uri buildTreeDocumentUri(String authority, String parentDocId, String docId) {
+ final Uri treeUri = DocumentsContract.buildTreeDocumentUri(authority, parentDocId);
+ return DocumentsContract.buildDocumentUriUsingTree(treeUri, docId);
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
new file mode 100644
index 0000000..8dcf566
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
@@ -0,0 +1,125 @@
+/*
+ * 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.provider;
+
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract.Path;
+
+import org.mockito.Mockito;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Provides a test double of {@link DocumentsProvider}.
+ */
+public class TestDocumentsProvider extends DocumentsProvider {
+ public static final String AUTHORITY = "android.provider.TestDocumentsProvider";
+
+ public Path nextPath;
+
+ public boolean nextIsChildDocument;
+
+ public String lastDocumentId;
+ public String lastParentDocumentId;
+
+ @Override
+ public void attachInfoForTesting(Context context, ProviderInfo info) {
+ context = new TestContext(context);
+ super.attachInfoForTesting(context, info);
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor queryRoots(String[] projection) throws FileNotFoundException {
+ return null;
+ }
+
+ @Override
+ public Cursor queryDocument(String documentId, String[] projection)
+ throws FileNotFoundException {
+ return null;
+ }
+
+ @Override
+ public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
+ String sortOrder) throws FileNotFoundException {
+ return null;
+ }
+
+ @Override
+ public ParcelFileDescriptor openDocument(String documentId, String mode,
+ CancellationSignal signal) throws FileNotFoundException {
+ return null;
+ }
+
+ @Override
+ public boolean isChildDocument(String parentDocumentId, String documentId) {
+ return nextIsChildDocument;
+ }
+
+ @Override
+ public Path findPath(String documentId, @Nullable String parentDocumentId) {
+ lastDocumentId = documentId;
+ lastParentDocumentId = parentDocumentId;
+
+ return nextPath;
+ }
+
+ @Override
+ protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken) {
+ return AppOpsManager.MODE_ALLOWED;
+ }
+
+ @Override
+ protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken) {
+ return AppOpsManager.MODE_ALLOWED;
+ }
+
+ private static class TestContext extends ContextWrapper {
+
+ private TestContext(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void enforceCallingPermission(String permission, String message) {
+ // Always granted
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (Context.APP_OPS_SERVICE.equals(name)) {
+ return Mockito.mock(AppOpsManager.class);
+ }
+
+ return super.getSystemService(name);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/util/TokenBucketTest.java b/core/tests/coretests/src/android/util/TokenBucketTest.java
new file mode 100644
index 0000000..f7ac20c
--- /dev/null
+++ b/core/tests/coretests/src/android/util/TokenBucketTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.os.SystemClock;
+import android.text.format.DateUtils;
+import junit.framework.TestCase;
+
+public class TokenBucketTest extends TestCase {
+
+ static final int FILL_DELTA_VERY_SHORT = 1;
+ static final int FILL_DELTA_VERY_LONG = Integer.MAX_VALUE;
+
+ public void testArgumentValidation() {
+ assertThrow(() -> new TokenBucket(0, 1, 1));
+ assertThrow(() -> new TokenBucket(1, 0, 1));
+ assertThrow(() -> new TokenBucket(1, 1, 0));
+ assertThrow(() -> new TokenBucket(0, 1));
+ assertThrow(() -> new TokenBucket(1, 0));
+ assertThrow(() -> new TokenBucket(-1, 1, 1));
+ assertThrow(() -> new TokenBucket(1, -1, 1));
+ assertThrow(() -> new TokenBucket(1, 1, -1));
+ assertThrow(() -> new TokenBucket(-1, 1));
+ assertThrow(() -> new TokenBucket(1, -1));
+
+ new TokenBucket(1000, 100, 0);
+ new TokenBucket(1000, 100, 10);
+ new TokenBucket(5000, 50);
+ new TokenBucket(5000, 1);
+ }
+
+ public void testInitialCapacity() {
+ drain(new TokenBucket(FILL_DELTA_VERY_LONG, 1), 1);
+ drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10), 10);
+ drain(new TokenBucket(FILL_DELTA_VERY_LONG, 1000), 1000);
+
+ drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10, 0), 0);
+ drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10, 3), 3);
+ drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10, 10), 10);
+
+ drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10, 100), 10);
+
+ drain(new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50), 50);
+ drain(new TokenBucket((int)DateUtils.HOUR_IN_MILLIS, 10), 10);
+ drain(new TokenBucket((int)DateUtils.DAY_IN_MILLIS, 200), 200);
+ }
+
+ public void testReset() {
+ TokenBucket tb = new TokenBucket(FILL_DELTA_VERY_LONG, 100, 10);
+ drain(tb, 10);
+
+ tb.reset(50);
+ drain(tb, 50);
+
+ tb.reset(50);
+ getOneByOne(tb, 10);
+ assertTrue(tb.has());
+
+ tb.reset(30);
+ drain(tb, 30);
+ }
+
+ public void testFill() throws Exception {
+ int delta = 50;
+ TokenBucket tb = new TokenBucket(delta, 10, 0);
+
+ assertEmpty(tb);
+
+ Thread.sleep(3 * delta / 2);
+
+ assertTrue(tb.has());
+ }
+
+ public void testRefill() throws Exception {
+ TokenBucket tb = new TokenBucket(FILL_DELTA_VERY_SHORT, 10, 10);
+
+ assertEquals(5, tb.get(5));
+ assertEquals(5, tb.get(5));
+
+ while (tb.available() < 10) {
+ Thread.sleep(2);
+ }
+
+ assertEquals(10, tb.get(10));
+
+ while (tb.available() < 10) {
+ Thread.sleep(2);
+ }
+
+ assertEquals(10, tb.get(100));
+ }
+
+ public void testAverage() throws Exception {
+ final int delta = 3;
+ final int want = 60;
+
+ long start = SystemClock.elapsedRealtime();
+ TokenBucket tb = new TokenBucket(delta, 20, 0);
+
+ for (int i = 0; i < want; i++) {
+ while (!tb.has()) {
+ Thread.sleep(5 * delta);
+ }
+ tb.get();
+ }
+
+ assertDuration(want * delta, SystemClock.elapsedRealtime() - start);
+ }
+
+ public void testBurst() throws Exception {
+ final int delta = 2;
+ final int capacity = 20;
+ final int want = 100;
+
+ long start = SystemClock.elapsedRealtime();
+ TokenBucket tb = new TokenBucket(delta, capacity, 0);
+
+ int total = 0;
+ while (total < want) {
+ while (!tb.has()) {
+ Thread.sleep(capacity * delta - 2);
+ }
+ total += tb.get(tb.available());
+ }
+
+ assertDuration(total * delta, SystemClock.elapsedRealtime() - start);
+ }
+
+ static void getOneByOne(TokenBucket tb, int n) {
+ while (n > 0) {
+ assertTrue(tb.has());
+ assertTrue(tb.available() >= n);
+ assertTrue(tb.get());
+ assertTrue(tb.available() >= n - 1);
+ n--;
+ }
+ }
+
+ void assertEmpty(TokenBucket tb) {
+ assertFalse(tb.has());
+ assertEquals(0, tb.available());
+ assertFalse(tb.get());
+ }
+
+ void drain(TokenBucket tb, int n) {
+ getOneByOne(tb, n);
+ assertEmpty(tb);
+ }
+
+ void assertDuration(long expected, long elapsed) {
+ String msg = String.format(
+ "expected elapsed time at least %d ms, but was %d ms", expected, elapsed);
+ elapsed += 1; // one millisecond extra guard
+ assertTrue(msg, elapsed >= expected);
+ }
+
+ void assertThrow(Fn fn) {
+ try {
+ fn.call();
+ fail("expected n exception to be thrown.");
+ } catch (Throwable t) {}
+ }
+
+ interface Fn { void call(); }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index ea22cd1..71dd526 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -17,7 +17,6 @@
package android.widget;
import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
-import static android.support.test.espresso.action.ViewActions.longClick;
import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
import static android.widget.espresso.DragHandleUtils.onHandleView;
import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
@@ -37,6 +36,7 @@
import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.longClick;
import static android.support.test.espresso.action.ViewActions.pressKey;
import static android.support.test.espresso.action.ViewActions.replaceText;
import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
@@ -44,24 +44,25 @@
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.is;
import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider;
-import com.android.frameworks.coretests.R;
import android.support.test.espresso.action.EspressoKey;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.MediumTest;
import android.text.Selection;
import android.text.Spannable;
import android.text.InputType;
import android.view.KeyEvent;
-import static org.hamcrest.Matchers.anyOf;
-import static org.hamcrest.Matchers.is;
+import com.android.frameworks.coretests.R;
/**
* Tests the TextView widget from an Activity
*/
+@MediumTest
public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextViewActivity>{
public TextViewActivityTest() {
@@ -74,7 +75,6 @@
getActivity();
}
- @SmallTest
public void testTypedTextIsOnScreen() throws Exception {
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
@@ -83,7 +83,6 @@
onView(withId(R.id.textview)).check(matches(withText(helloWorld)));
}
- @SmallTest
public void testPositionCursorAtTextAtIndex() throws Exception {
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
@@ -95,7 +94,6 @@
onView(withId(R.id.textview)).check(matches(withText("Hello orld!")));
}
- @SmallTest
public void testPositionCursorAtTextAtIndex_arabic() throws Exception {
// Arabic text. The expected cursorable boundary is
// | \u0623 \u064F | \u067A | \u0633 \u0652 |
@@ -117,7 +115,6 @@
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(5));
}
- @SmallTest
public void testPositionCursorAtTextAtIndex_devanagari() throws Exception {
// Devanagari text. The expected cursorable boundary is | \u0915 \u093E |
final String text = "\u0915\u093E";
@@ -132,7 +129,6 @@
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(2));
}
- @SmallTest
public void testLongPressToSelect() throws Exception {
final String helloWorld = "Hello Kirk!";
onView(withId(R.id.textview)).perform(click());
@@ -143,7 +139,6 @@
onView(withId(R.id.textview)).check(hasSelection("Kirk"));
}
- @SmallTest
public void testLongPressEmptySpace() throws Exception {
final String helloWorld = "Hello big round sun!";
onView(withId(R.id.textview)).perform(click());
@@ -158,7 +153,6 @@
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(helloWorld.length()));
}
- @SmallTest
public void testLongPressAndDragToSelect() throws Exception {
final String helloWorld = "Hello little handsome boy!";
onView(withId(R.id.textview)).perform(click());
@@ -169,7 +163,20 @@
onView(withId(R.id.textview)).check(hasSelection("little handsome"));
}
- @SmallTest
+ public void testLongPressAndDragToSelect_emoji() throws Exception {
+ final String text = "\uD83D\uDE00\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ onView(withId(R.id.textview)).perform(longPressAndDragOnText(4, 6));
+ onView(withId(R.id.textview)).check(hasSelection("\uD83D\uDE02"));
+
+ onView(withId(R.id.textview)).perform(click());
+
+ onView(withId(R.id.textview)).perform(longPressAndDragOnText(4, 2));
+ onView(withId(R.id.textview)).check(hasSelection("\uD83D\uDE01"));
+ }
+
public void testDragAndDrop() throws Exception {
final String text = "abc def ghi.";
onView(withId(R.id.textview)).perform(click());
@@ -191,7 +198,6 @@
onView(withId(R.id.textview)).check(matches(withText(text)));
}
- @SmallTest
public void testDoubleTapToSelect() throws Exception {
final String helloWorld = "Hello SuetYi!";
onView(withId(R.id.textview)).perform(click());
@@ -202,7 +208,6 @@
onView(withId(R.id.textview)).check(hasSelection("SuetYi"));
}
- @SmallTest
public void testDoubleTapAndDragToSelect() throws Exception {
final String helloWorld = "Hello young beautiful girl!";
onView(withId(R.id.textview)).perform(click());
@@ -213,7 +218,6 @@
onView(withId(R.id.textview)).check(hasSelection("young beautiful"));
}
- @SmallTest
public void testDoubleTapAndDragToSelect_multiLine() throws Exception {
final String helloWorld = "abcd\n" + "efg\n" + "hijklm\n" + "nop";
onView(withId(R.id.textview)).perform(click());
@@ -223,7 +227,6 @@
onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijklm"));
}
- @SmallTest
public void testSelectBackwordsByTouch() throws Exception {
final String helloWorld = "Hello king of the Jungle!";
onView(withId(R.id.textview)).perform(click());
@@ -234,7 +237,6 @@
onView(withId(R.id.textview)).check(hasSelection("king of the"));
}
- @SmallTest
public void testToolbarAppearsAfterSelection() throws Exception {
final String text = "Toolbar appears after selection.";
onView(withId(R.id.textview)).perform(click());
@@ -251,7 +253,6 @@
assertFloatingToolbarIsNotDisplayed();
}
- @SmallTest
public void testToolbarAppearsAfterSelection_withFirstStringLtrAlgorithmAndRtlHint()
throws Exception {
// after the hint layout change, the floating toolbar was not visible in the case below
@@ -279,7 +280,6 @@
assertFloatingToolbarIsDisplayed();
}
- @SmallTest
public void testToolbarAndInsertionHandle() throws Exception {
final String text = "text";
onView(withId(R.id.textview)).perform(click());
@@ -299,7 +299,6 @@
getActivity().getString(com.android.internal.R.string.cut));
}
- @SmallTest
public void testToolbarAndSelectionHandle() throws Exception {
final String text = "abcd efg hijk";
onView(withId(R.id.textview)).perform(click());
@@ -335,7 +334,6 @@
getActivity().getString(com.android.internal.R.string.cut));
}
- @SmallTest
public void testInsertionHandle() throws Exception {
final String text = "abcd efg hijk ";
onView(withId(R.id.textview)).perform(click());
@@ -355,7 +353,6 @@
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
}
- @SmallTest
public void testInsertionHandle_multiLine() throws Exception {
final String text = "abcd\n" + "efg\n" + "hijk\n";
onView(withId(R.id.textview)).perform(click());
@@ -375,7 +372,6 @@
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
}
- @SmallTest
public void testSelectionHandles() throws Exception {
final String text = "abcd efg hijk lmn";
onView(withId(R.id.textview)).perform(click());
@@ -400,7 +396,6 @@
onView(withId(R.id.textview)).check(hasSelection("abcd efg hijk"));
}
- @SmallTest
public void testSelectionHandles_bidi() throws Exception {
final String text = "abc \u0621\u0622\u0623 def";
onView(withId(R.id.textview)).perform(click());
@@ -445,7 +440,6 @@
onView(withId(R.id.textview)).check(hasSelection("abc \u0621\u0622\u0623 def"));
}
- @SmallTest
public void testSelectionHandles_multiLine() throws Exception {
final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
onView(withId(R.id.textview)).perform(click());
@@ -470,7 +464,6 @@
onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk\nlmn\nopqr"));
}
- @SmallTest
public void testSelectionHandles_multiLine_rtl() throws Exception {
// Arabic text.
final String text = "\u062A\u062B\u062C\n" + "\u062D\u062E\u062F\n"
@@ -503,7 +496,6 @@
}
- @SmallTest
public void testSelectionHandles_doesNotPassAnotherHandle() throws Exception {
final String text = "abcd efg hijk lmn";
onView(withId(R.id.textview)).perform(click());
@@ -521,7 +513,6 @@
onView(withId(R.id.textview)).check(hasSelection("e"));
}
- @SmallTest
public void testSelectionHandles_doesNotPassAnotherHandle_multiLine() throws Exception {
final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
onView(withId(R.id.textview)).perform(click());
@@ -539,7 +530,6 @@
onView(withId(R.id.textview)).check(hasSelection("h"));
}
- @SmallTest
public void testSelectionHandles_snapToWordBoundary() throws Exception {
final String text = "abcd efg hijk lmn opqr";
onView(withId(R.id.textview)).perform(click());
@@ -592,7 +582,6 @@
onView(withId(R.id.textview)).check(hasSelection("hijk lmn opq"));
}
- @SmallTest
public void testSelectionHandles_snapToWordBoundary_multiLine() throws Exception {
final String text = "abcd efg\n" + "hijk lmn\n" + "opqr stu";
onView(withId(R.id.textview)).perform(click());
@@ -628,7 +617,6 @@
onView(withId(R.id.textview)).check(hasSelection("hijk"));
}
- @SmallTest
public void testSetSelectionAndActionMode() throws Exception {
final String text = "abc def";
onView(withId(R.id.textview)).perform(click());
@@ -692,7 +680,6 @@
getActivity().getString(com.android.internal.R.string.copy));
}
- @SmallTest
public void testTransientState() throws Exception {
final String text = "abc def";
onView(withId(R.id.textview)).perform(click());
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 6185dab..7daf85c 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -1237,7 +1237,7 @@
- from: /r/studio-ui/run-with-work-profile.html
to: /studio/run/index.html?utm_source=android-studio#ir-work-profile
- from: /r/studio-ui/am-gpu-debugger.html
- to: /studio/profile/am-gpu.html?utm_source=android-studio
+ to: /studio/debug/am-gpu-debugger.html?utm_source=android-studio
- from: /r/studio-ui/theme-editor.html
to: /studio/write/theme-editor.html?utm_source=android-studio
- from: /r/studio-ui/translations-editor.html
diff --git a/docs/html/google/play/billing/billing_integrate.jd b/docs/html/google/play/billing/billing_integrate.jd
index 5d6b3a8..506a440 100755
--- a/docs/html/google/play/billing/billing_integrate.jd
+++ b/docs/html/google/play/billing/billing_integrate.jd
@@ -9,18 +9,18 @@
<h2>In this document</h2>
<ol>
<li><a href="#billing-add-aidl">Adding the AIDL file</a></li>
- <li><a href="#billing-permission">Updating Your Manifest</a></li>
+ <li><a href="#billing-permission">Updating your manifest</a></li>
<li><a href="#billing-service">Creating a ServiceConnection</a></li>
- <li><a href="#billing-requests">Making In-app Billing Requests</a>
+ <li><a href="#billing-requests">Making In-app Billing requests</a>
<ol>
- <li><a href="#QueryDetails">Querying Items Available for Purchase</a><li>
- <li><a href="#Purchase">Purchasing an Item</a></li>
- <li><a href="#QueryPurchases">Querying Purchased Items</a></li>
- <li><a href="#Consume">Consuming a Purchase</a></li>
- <li><a href="#Subs">Implementing Subscriptions</a></li>
+ <li><a href="#QueryDetails">Querying items available for purchase</a><li>
+ <li><a href="#Purchase">Purchasing an item</a></li>
+ <li><a href="#QueryPurchases">Querying purchased items</a></li>
+ <li><a href="#Consume">Consuming a purchase</a></li>
+ <li><a href="#Subs">Implementing subscriptions</a></li>
</ol>
</li>
- <li><a href="#billing-security">Securing Your App</a>
+ <li><a href="#billing-security">Securing your app</a>
</ol>
<h2>Reference</h2>
<ol>
@@ -42,7 +42,7 @@
In-app Billing on Google Play provides a straightforward, simple interface
for sending In-app Billing requests and managing In-app Billing transactions
using Google Play. The information below covers the basics of how to make
- calls from your application to the In-app Billing service using the Version 3
+ calls from your application to the In-app Billing service using the In-app Billing Version 3
API.
</p>
@@ -51,26 +51,25 @@
your application, see the <a href=
"{@docRoot}training/in-app-billing/index.html">Selling In-app Products</a>
training class. The training class provides a complete sample In-app Billing
- application, including convenience classes to handle key tasks related to
- setting up your connection, sending billing requests and processing responses
+ application, including convenience classes to handle key tasks that are related to
+ setting up your connection, sending billing requests, processing responses
from Google Play, and managing background threading so that you can make
In-app Billing calls from your main activity.
</p>
<p>
- Before you start, be sure that you read the <a href=
+ Before you start, read the <a href=
"{@docRoot}google/play/billing/billing_overview.html">In-app Billing
- Overview</a> to familiarize yourself with concepts that will make it easier
+ Overview</a> to familiarize yourself with concepts that make it easier
for you to implement In-app Billing.
</p>
-<p>To implement In-app Billing in your application, you need to do the
-following:</p>
+<p>Complete these steps to implement In-app Billing in your application:</p>
<ol>
<li>Add the In-app Billing library to your project.</li>
<li>Update your {@code AndroidManifest.xml} file.</li>
- <li>Create a {@code ServiceConnection} and bind it to
+ <li>Create a {@code ServiceConnection} and bind it to the
{@code IInAppBillingService}.</li>
<li>Send In-app Billing requests from your application to
{@code IInAppBillingService}.</li>
@@ -79,55 +78,56 @@
<h2 id="billing-add-aidl">Adding the AIDL file to your project</h2>
-<p>{@code IInAppBillingService.aidl} is an Android Interface Definition
+<p>The {@code IInAppBillingService.aidl} is an Android Interface Definition
Language (AIDL) file that defines the interface to the In-app Billing Version
-3 service. You will use this interface to make billing requests by invoking IPC
+3 service. You can use this interface to make billing requests by invoking IPC
method calls.</p>
-<p>To get the AIDL file:</p>
+
+<p>Complete these steps to get the AIDL file:</p>
<ol>
<li>Open the <a href="{@docRoot}tools/help/sdk-manager.html">Android SDK Manager</a>.</li>
<li>In the SDK Manager, expand the {@code Extras} section.</li>
<li>Select <strong>Google Play Billing Library</strong>.</li>
<li>Click <strong>Install packages</strong> to complete the download.</li>
</ol>
-<p>The {@code IInAppBillingService.aidl} file will be installed to {@code <sdk>/extras/google/play_billing/}.</p>
+<p>The {@code IInAppBillingService.aidl} file will be installed to {@code <sdk>/extras/google/play_billing/}.</p>
-<p>To add the AIDL to your project:</p>
+<p>Complete these steps to add the AIDL to your project:</p>
<ol>
- <li>First, download the Google Play Billing Library to your Android project:
+ <li>Download the Google Play Billing Library to your Android project:
<ol type="a">
<li>Select <strong>Tools > Android > SDK Manager</strong>.</li>
<li>Under <strong>Appearance & Behavior > System Settings > Android SDK</strong>,
select the <em>SDK Tools</em> tab to select and download <em>Google Play Billing
Library</em>.</li></ol>
- <li>Next, copy the {@code IInAppBillingService.aidl} file to your project.
+ <li>Copy the {@code IInAppBillingService.aidl} file to your project.
<ul>
- <li>If you are using Android Studio:
+ <li>If you are using Android Studio, complete these steps to copy the file:
<ol type="a">
<li>Navigate to {@code src/main} in the Project tool window.</li>
- <li>Select <strong>File > New > Directory</strong> and enter {@code aidl} in the
- <em>New Directory</em> window, then select <strong>OK</strong>.
+ <li>Select <strong>File > New > Directory</strong>, enter {@code aidl} in the
+ <em>New Directory</em> window, and select <strong>OK</strong>.
- <li>Select <strong>File > New > Package</strong> and enter
- {@code com.android.vending.billing} in the <em>New Package</em> window, then select
+ <li>Select <strong>File > New > Package</strong>, enter
+ {@code com.android.vending.billing} in the <em>New Package</em> window, and select
<strong>OK</strong>.</li>
<li>Using your operating system file explorer, navigate to
- {@code <sdk>/extras/google/play_billing/}, copy the
+ {@code <sdk>/extras/google/play_billing/}, copy the
{@code IInAppBillingService.aidl} file, and paste it into the
{@code com.android.vending.billing} package in your project.
</li>
</ol>
</li>
- <li>If you are developing in a non-Android Studio environment: Create the
- following directory {@code /src/com/android/vending/billing} and copy the
- {@code IInAppBillingService.aidl} file into this directory. Put the AIDL
- file into your project and use the Gradle tool to build your project so that
- the <code>IInAppBillingService.java</code> file gets generated.
+ <li>If you are developing in a non-Android Studio environment, create the
+ following directory: {@code /src/com/android/vending/billing}. Copy the
+ {@code IInAppBillingService.aidl} file into this directory. Place the AIDL
+ file in your project and use the Gradle tool to build your project so that
+ the <code>IInAppBillingService.java</code> file is generated.
</li>
</ul>
</li>
@@ -137,16 +137,16 @@
</li>
</ol>
-<h2 id="billing-permission">Updating Your App's Manifest</h2>
+<h2 id="billing-permission">Updating your app's manifest</h2>
<p>
In-app billing relies on the Google Play application, which handles all
- communication between your application and the Google Play server. To use the
+ of the communication between your application and the Google Play server. To use the
Google Play application, your application must request the proper permission.
You can do this by adding the {@code com.android.vending.BILLING} permission
to your AndroidManifest.xml file. If your application does not declare the
In-app Billing permission, but attempts to send billing requests, Google Play
- will refuse the requests and respond with an error.
+ refuses the requests and responds with an error.
</p>
<p>
@@ -182,7 +182,7 @@
onServiceDisconnected} and {@link
android.content.ServiceConnection#onServiceConnected onServiceConnected}
methods to get a reference to the {@code IInAppBillingService} instance after
- a connection has been established.
+ a connection is established.
</p>
<pre>
@@ -208,20 +208,25 @@
bindService} method. Pass the method an {@link android.content.Intent} that
references the In-app Billing service and an instance of the {@link
android.content.ServiceConnection} that you created, and explicitly set the
- Intent's target package name to <code>com.android.vending</code> — the
+ Intent's target package name to <code>com.android.vending</code>—the
package name of Google Play app.
</p>
<p class="caution">
<strong>Caution:</strong> To protect the security of billing transactions,
- always make sure to explicitly set the intent's target package name to
+ always explicitly set the intent's target package name to
<code>com.android.vending</code>, using {@link
- android.content.Intent#setPackage(java.lang.String) setPackage()} as shown in
- the example below. Setting the package name explicitly ensures that
+ android.content.Intent#setPackage(java.lang.String) setPackage()}.
+ Setting the package name explicitly ensures that
<em>only</em> the Google Play app can handle billing requests from your app,
preventing other apps from intercepting those requests.
</p>
+<p>
+ The following code sample demonstrates how to set the intent's target package
+ to protect the security of transactions:
+</p>
+
<pre>@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -233,6 +238,13 @@
}
</pre>
+<p class="caution"><strong>Caution</strong>: To ensure that your app is secure, always use an
+explicit intent when starting a {@link android.app.Service} and do not declare intent filters for
+your services. Using an implicit intent to start a service is a security hazard because you cannot
+be certain of the service that will respond to the intent, and the user cannot see which service
+starts. Beginning with Android 5.0 (API level 21), the system throws an exception if you call
+{@link android.content.Context#bindService bindService()} with an implicit intent.</p>
+
<p>
You can now use the mService reference to communicate with the Google Play
service.
@@ -242,10 +254,14 @@
<strong>Important:</strong> Remember to unbind from the In-app Billing
service when you are done with your {@link android.app.Activity}. If you
don’t unbind, the open service connection could cause your device’s
- performance to degrade. This example shows how to perform the unbind
+ performance to degrade.
+</p>
+
+<p>
+ This example shows how to perform the unbind
operation on a service connection to In-app Billing called {@code
mServiceConn} by overriding the activity’s {@link
- android.app.Activity#onDestroy onDestroy} method.
+ android.app.Activity#onDestroy onDestroy} method:
</p>
<pre>
@@ -264,29 +280,29 @@
"{@docRoot}training/in-app-billing/preparing-iab-app.html">Selling In-app
Products</a> training class and associated sample.
</p>
-<h2 id="billing-requests">Making In-app Billing Requests</h2>
+<h2 id="billing-requests">Making In-app Billing requests</h2>
<p>
- Once your application is connected to Google Play, you can initiate purchase
+ After your application is connected to Google Play, you can initiate purchase
requests for in-app products. Google Play provides a checkout interface for
- users to enter their payment method, so your application does not need to
+ users to enter their payment method, so your application doesn't need to
handle payment transactions directly. When an item is purchased, Google Play
recognizes that the user has ownership of that item and prevents the user
from purchasing another item with the same product ID until it is consumed.
- You can control how the item is consumed in your application, and notify
+ You can control how the item is consumed in your application and notify
Google Play to make the item available for purchase again. You can also query
- Google Play to quickly retrieve the list of purchases that were made by the
- user. This is useful, for example, when you want to restore the user's
+ Google Play to quickly retrieve the list of purchases that the
+ user made. This is useful, for example, when you want to restore the user's
purchases when your user launches your app.
</p>
-<h3 id="QueryDetails">Querying for Items Available for Purchase</h3>
+<h3 id="QueryDetails">Querying for items available for purchase</h3>
<p>
In your application, you can query the item details from Google Play using
the In-app Billing Version 3 API. To pass a request to the In-app Billing
- service, first create a {@link android.os.Bundle} that contains a String
+ service, create a {@link android.os.Bundle} that contains a String
{@link java.util.ArrayList} of product IDs with key "ITEM_ID_LIST", where
- each string is a product ID for an purchasable item.
+ each string is a product ID for an purchasable item. Here is an example:
</p>
<pre>
@@ -299,9 +315,9 @@
<p>
To retrieve this information from Google Play, call the {@code getSkuDetails}
- method on the In-app Billing Version 3 API, and pass the method the In-app
+ method on the In-app Billing Version 3 API and pass the In-app
Billing API version (“3”), the package name of your calling app, the purchase
- type (“inapp”), and the {@link android.os.Bundle} that you created.
+ type (“inapp”), and the {@link android.os.Bundle} that you created, into the method:
</p>
<pre>
@@ -310,35 +326,35 @@
</pre>
<p>
- If the request is successful, the returned {@link android.os.Bundle}has a
+ If the request is successful, the returned {@link android.os.Bundle} has a
response code of {@code BILLING_RESPONSE_RESULT_OK} (0).
</p>
<p class="note">
- <strong>Warning:</strong> Do not call the {@code getSkuDetails} method on the
- main thread. Calling this method triggers a network request which could block
+ <strong>Warning:</strong> Don't call the {@code getSkuDetails} method on the
+ main thread. Calling this method triggers a network request that could block
your main thread. Instead, create a separate thread and call the {@code
- getSkuDetails} method from inside that thread.
+ getSkuDetails} method from inside of that thread.
</p>
<p>
- To see all the possible response codes from Google Play, see <a href=
+ To view all of the possible response codes from Google Play, see <a href=
"{@docRoot}google/play/billing/billing_reference.html#billing-codes">In-app
Billing Reference</a>.
</p>
<p>
The query results are stored in a String ArrayList with key {@code
- DETAILS_LIST}. The purchase information is stored in the String in JSON
- format. To see the types of product detail information that are returned, see
+ DETAILS_LIST}. The purchase information is stored within the String in JSON
+ format. To view the types of product detail information that are returned, see
<a href=
"{@docRoot}google/play/billing/billing_reference.html#getSkuDetails">In-app
Billing Reference</a>.
</p>
<p>
- In this example, you are retrieving the prices for your in-app items from the
- skuDetails {@link android.os.Bundle} returned from the previous code snippet.
+ In this example shows how to retrieve the prices for your in-app items from the
+ skuDetails {@link android.os.Bundle} that is returned from the previous code snippet:
</p>
<pre>
@@ -357,15 +373,15 @@
}
</pre>
-<h3 id="Purchase">Purchasing an Item</h3>
+<h3 id="Purchase">Purchasing an item</h3>
<p>
To start a purchase request from your app, call the {@code getBuyIntent}
- method on the In-app Billing service. Pass in to the method the In-app
+ method on the In-app Billing service. Pass the In-app
Billing API version (“3”), the package name of your calling app, the product
ID for the item to purchase, the purchase type (“inapp” or "subs"), and a
- {@code developerPayload} String. The {@code developerPayload} String is used
+ {@code developerPayload} String into the method. The {@code developerPayload} String is used
to specify any additional arguments that you want Google Play to send back
- along with the purchase information.
+ along with the purchase information. Here is an example:
</p>
<pre>
@@ -377,10 +393,13 @@
If the request is successful, the returned {@link android.os.Bundle} has a
response code of {@code BILLING_RESPONSE_RESULT_OK} (0) and a {@link
android.app.PendingIntent} that you can use to start the purchase flow. To
- see all the possible response codes from Google Play, see <a href=
+ view all of the possible response codes from Google Play, see <a href=
"{@docRoot}google/play/billing/billing_reference.html#billing-codes">In-app
- Billing Reference</a>. Next, extract a {@link android.app.PendingIntent} from
- the response {@link android.os.Bundle} with key {@code BUY_INTENT}.
+ Billing Reference</a>.
+
+<p>
+ The next step is to extract a {@link android.app.PendingIntent} from
+ the response {@link android.os.Bundle} with key {@code BUY_INTENT}, as shown here:
</p>
<pre>
@@ -390,8 +409,8 @@
<p>
To complete the purchase transaction, call the {@link
android.app.Activity#startIntentSenderForResult startIntentSenderForResult}
- method and use the {@link android.app.PendingIntent} that you created. In
- this example, you are using an arbitrary value of 1001 for the request code.
+ method and use the {@link android.app.PendingIntent} that you created. This
+ example uses an arbitrary value of 1001 for the request code:
</p>
<pre>
@@ -404,9 +423,9 @@
Google Play sends a response to your {@link android.app.PendingIntent} to the
{@link android.app.Activity#onActivityResult onActivityResult} method of your
application. The {@link android.app.Activity#onActivityResult
- onActivityResult} method will have a result code of {@code
- Activity.RESULT_OK} (1) or {@code Activity.RESULT_CANCELED} (0). To see the
- types of order information that is returned in the response {@link
+ onActivityResult} method has a result code of {@code
+ Activity.RESULT_OK} (1) or {@code Activity.RESULT_CANCELED} (0). To view the
+ types of order information that are returned in the response {@link
android.content.Intent}, see <a href=
"{@docRoot}google/play/billing/billing_reference.html#getBuyIntent">In-app
Billing Reference</a>.
@@ -415,7 +434,7 @@
<p>
The purchase data for the order is a String in JSON format that is mapped to
the {@code INAPP_PURCHASE_DATA} key in the response {@link
- android.content.Intent}, for example:
+ android.content.Intent}. Here is an example:
</p>
<pre>
@@ -436,13 +455,13 @@
long. Pass this entire token to other methods, such as when you consume the
purchase, as described in <a href=
"{@docRoot}training/in-app-billing/purchase-iab-products.html#Consume">Consume
- a Purchase</a>. Do not abbreviate or truncate this token; you must save and
+ a Purchase</a>. Don't abbreviate or truncate this token; you must save and
return the entire token.
</p>
<p>
- Continuing from the previous example, you get the response code, purchase
- data, and signature from the response {@link android.content.Intent}.
+ Continuing from the previous example, you receive the response code, purchase
+ data, and signature from the response {@link android.content.Intent}:
</p>
<pre>
@@ -472,23 +491,23 @@
<p class="note">
<strong>Security Recommendation:</strong> When you send a purchase request,
create a String token that uniquely identifies this purchase request and
- include this token in the {@code developerPayload}.You can use a randomly
- generated string as the token. When you receive the purchase response from
- Google Play, make sure to check the returned data signature, the {@code
+ include this token in the {@code developerPayload}. You can use a randomly-generated
+ string as the token. When you receive the purchase response from
+ Google Play, ensure that you check the returned data signature, the {@code
orderId}, and the {@code developerPayload} String. For added security, you
- should perform the checking on your own secure server. Make sure to verify
+ should perform the checking on your own secure server. Verify
that the {@code orderId} is a unique value that you have not previously
- processed, and the {@code developerPayload} String matches the token that you
+ processed and that the {@code developerPayload} String matches the token that you
sent previously with the purchase request.
</p>
-<h3 id="QueryPurchases">Querying for Purchased Items</h3>
+<h3 id="QueryPurchases">Querying for purchased items</h3>
<p>
- To retrieve information about purchases made by a user from your app, call
+ To retrieve information about purchases that are made by a user from your app, call
the {@code getPurchases} method on the In-app Billing Version 3 service. Pass
- in to the method the In-app Billing API version (“3”), the package name of
- your calling app, and the purchase type (“inapp” or "subs").
+ the In-app Billing API version (“3”), the package name of
+ your calling app, and the purchase type (“inapp” or "subs") into the method. Here is an example:
</p>
<pre>
@@ -507,18 +526,18 @@
To improve performance, the In-app Billing service returns only up to 700
products that are owned by the user when {@code getPurchase} is first called.
If the user owns a large number of products, Google Play includes a String
- token mapped to the key {@code INAPP_CONTINUATION_TOKEN} in the response
+ token that is mapped to the key {@code INAPP_CONTINUATION_TOKEN} in the response
{@link android.os.Bundle} to indicate that more products can be retrieved.
- Your application can then make a subsequent {@code getPurchases} call, and
+ Your application can then make a subsequent {@code getPurchases} call and
pass in this token as an argument. Google Play continues to return a
continuation token in the response {@link android.os.Bundle} until all
- products that are owned by the user has been sent to your app.
+ of the products that are owned by the user are sent to your app.
</p>
-<p>For more information about the data returned by {@code getPurchases}, see
+<p>For more information about the data that is returned by {@code getPurchases}, see
<a href="{@docRoot}google/play/billing/billing_reference.html#getPurchases">
In-app Billing Reference</a>. The following example shows how you can
- retrieve this data from the response.
+ retrieve this data from the response:
</p>
<pre>
@@ -548,26 +567,26 @@
</pre>
-<h3 id="Consume">Consuming a Purchase</h3>
+<h3 id="Consume">Consuming a purchase</h3>
<p>
You can use the In-app Billing Version 3 API to track the ownership of
purchased in-app products in Google Play. Once an in-app product is
- purchased, it is considered to be "owned" and cannot be purchased from Google
+ purchased, it is considered to be <em>owned</em> and cannot be purchased from Google
Play. You must send a consumption request for the in-app product before
Google Play makes it available for purchase again.
</p>
-<p class="caution">
+<p class="note">
<strong>Important</strong>: Managed in-app products are consumable, but
subscriptions are not.
</p>
<p>
- How you use the consumption mechanism in your app is up to you. Typically,
- you would implement consumption for in-app products with temporary benefits
+ The way that you use the consumption mechanism in your app is up to you. Typically,
+ you implement consumption for in-app products with temporary benefits
that users may want to purchase multiple times (for example, in-game currency
- or equipment). You would typically not want to implement consumption for
+ or equipment). You typically don't want to implement consumption for
in-app products that are purchased once and provide a permanent effect (for
example, a premium upgrade).
</p>
@@ -576,21 +595,21 @@
To record a purchase consumption, send the {@code consumePurchase} method to
the In-app Billing service and pass in the {@code purchaseToken} String value
that identifies the purchase to be removed. The {@code purchaseToken} is part
- of the data returned in the {@code INAPP_PURCHASE_DATA} String by the Google
- Play service following a successful purchase request. In this example, you
- are recording the consumption of a product that is identified with the {@code
- purchaseToken} in the {@code token} variable.
+ of the data that is returned in the {@code INAPP_PURCHASE_DATA} String by the Google
+ Play service following a successful purchase request. This example
+ records the consumption of a product that is identified with the {@code
+ purchaseToken} in the {@code token} variable:
</p>
<pre>
int response = mService.consumePurchase(3, getPackageName(), token);
</pre>
-<p class="note">
- <strong>Warning:</strong> Do not call the {@code consumePurchase} method on
- the main thread. Calling this method triggers a network request which could
+<p class="caution">
+ <strong>Warning:</strong> Don't call the {@code consumePurchase} method on
+ the main thread. Calling this method triggers a network request that could
block your main thread. Instead, create a separate thread and call the {@code
- consumePurchase} method from inside that thread.
+ consumePurchase} method from inside of that thread.
</p>
<p>
@@ -600,20 +619,20 @@
purchased.
</p>
-<p class="note">
- <strong>Security Recommendation:</strong> You must send a consumption request
+<p class="caution">
+ <strong>Security Recommendation:</strong> Send a consumption request
before provisioning the benefit of the consumable in-app purchase to the
- user. Make sure that you have received a successful consumption response from
+ user. Ensure that you receive a successful consumption response from
Google Play before you provision the item.
</p>
-<h3 id="Subs">Implementing Subscriptions</h3>
+<h3 id="Subs">Implementing subscriptions</h3>
<p>Launching a purchase flow for a subscription is similar to launching the
purchase flow for a product, with the exception that the product type must be set
to "subs". The purchase result is delivered to your Activity's
{@link android.app.Activity#onActivityResult onActivityResult} method, exactly
-as in the case of in-app products.</p>
+as in the case of in-app products. Here is an example:</p>
<pre>
Bundle bundle = mService.getBuyIntent(3, "com.example.myapp",
@@ -629,18 +648,18 @@
</pre>
<p>To query for active subscriptions, use the {@code getPurchases} method, again
-with the product type parameter set to "subs".</p>
+with the product type parameter set to "subs":</p>
<pre>
Bundle activeSubs = mService.getPurchases(3, "com.example.myapp",
"subs", continueToken);
</pre>
-<p>The call returns a {@code Bundle} with all the active subscriptions owned by
-the user. Once a subscription expires without renewal, it will no longer appear
+<p>The call returns a {@code Bundle} with all of the active subscriptions that are owned by
+the user. When a subscription expires without renewal, it no longer appears
in the returned {@code Bundle}.</p>
-<h2 id="billing-security">Securing Your Application</h2>
+<h2 id="billing-security">Securing your application</h2>
<p>To help ensure the integrity of the transaction information that is sent to
your application, Google Play signs the JSON string that contains the response
@@ -648,21 +667,21 @@
with your application in the Developer Console to create this signature. The
Developer Console generates an RSA key pair for each application.<p>
-<p class="note"><strong>Note:</strong>To find the public key portion of this key
-pair, open your application's details in the Developer Console, then click on
-<strong>Services & APIs</strong>, and look at the field titled
+<p class="note"><strong>Note:</strong> To find the public key portion of this key
+pair, open your application's details in the Developer Console, click
+<strong>Services & APIs</strong>, and review the field titled
<strong>Your License Key for This Application</strong>.</p>
-<p>The Base64-encoded RSA public key generated by Google Play is in binary
+<p>The Base64-encoded RSA public key that is generated by Google Play is in binary
encoded, X.509 subjectPublicKeyInfo DER SEQUENCE format. It is the same public
key that is used with Google Play licensing.</p>
-<p>When your application receives this signed response you can
+<p>When your application receives this signed response, you can
use the public key portion of your RSA key pair to verify the signature.
-By performing signature verification you can detect responses that have
+By performing signature verification, you can detect any responses that have
been tampered with or that have been spoofed. You can perform this signature
verification step in your application; however, if your application connects
-to a secure remote server then we recommend that you perform the signature
+to a secure remote server, Google recommends that you perform the signature
verification on that server.</p>
<p>For more information about best practices for security and design, see <a
diff --git a/docs/html/guide/components/bound-services.jd b/docs/html/guide/components/bound-services.jd
index f71ba87..2ee2061 100644
--- a/docs/html/guide/components/bound-services.jd
+++ b/docs/html/guide/components/bound-services.jd
@@ -8,19 +8,19 @@
<ol id="qv">
<h2>In this document</h2>
<ol>
- <li><a href="#Basics">The Basics</a></li>
- <li><a href="#Creating">Creating a Bound Service</a>
+ <li><a href="#Basics">The basics</a></li>
+ <li><a href="#Creating">Creating a bound service</a>
<ol>
<li><a href="#Binder">Extending the Binder class</a></li>
<li><a href="#Messenger">Using a Messenger</a></li>
</ol>
</li>
- <li><a href="#Binding">Binding to a Service</a>
+ <li><a href="#Binding">Binding to a service</a>
<ol>
<li><a href="#Additional_Notes">Additional notes</a></li>
</ol>
</li>
- <li><a href="#Lifecycle">Managing the Lifecycle of a Bound Service</a></li>
+ <li><a href="#Lifecycle">Managing the lifecycle of a bound service</a></li>
</ol>
<h2>Key classes</h2>
@@ -32,9 +32,13 @@
<h2>Samples</h2>
<ol>
- <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code
+ <li><a
+ href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">
+ {@code
RemoteService}</a></li>
- <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code
+ <li><a
+ href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">
+ {@code
LocalService}</a></li>
</ol>
@@ -45,19 +49,23 @@
</div>
-<p>A bound service is the server in a client-server interface. A bound service allows components
-(such as activities) to bind to the service, send requests, receive responses, and even perform
+<p>A bound service is the server in a client-server interface. It allows components
+(such as activities) to bind to the service, send requests, receive responses, and perform
interprocess communication (IPC). A bound service typically lives only while it serves another
application component and does not run in the background indefinitely.</p>
-<p>This document shows you how to create a bound service, including how to bind
-to the service from other application components. However, you should also refer to the <a
-href="{@docRoot}guide/components/services.html">Services</a> document for additional
-information about services in general, such as how to deliver notifications from a service, set
-the service to run in the foreground, and more.</p>
+<p class="note"><strong>Note:</strong> If your app targets Android 5.0 (API level 21) or later,
+it's recommended that you use the {@link android.app.job.JobScheduler} to execute background
+ services. For more information about {@link android.app.job.JobScheduler}, see its
+ {@link android.app.job.JobScheduler API-reference documentation}.</p>
+<p>This document describes how to create a bound service, including how to bind
+to the service from other application components. For additional information about services in
+ general, such as how to deliver notifications from a service and set the service to run
+ in the foreground, refer to the <a href="{@docRoot}guide/components/services.html">
+ Services</a> document.</p>
-<h2 id="Basics">The Basics</h2>
+<h2 id="Basics">The basics</h2>
<p>A bound service is an implementation of the {@link android.app.Service} class that allows
other applications to bind to it and interact with it. To provide binding for a
@@ -67,57 +75,61 @@
<div class="sidebox-wrapper">
<div class="sidebox">
- <h3>Binding to a Started Service</h3>
+ <h3>Binding to a started service</h3>
<p>As discussed in the <a href="{@docRoot}guide/components/services.html">Services</a>
-document, you can create a service that is both started and bound. That is, the service can be
-started by calling {@link android.content.Context#startService startService()}, which allows the
-service to run indefinitely, and also allow a client to bind to the service by calling {@link
+document, you can create a service that is both started and bound. That is, you can start a
+ service by calling {@link android.content.Context#startService startService()}, which allows the
+service to run indefinitely, and you can also allow a client to bind to the service by
+ calling {@link
android.content.Context#bindService bindService()}.
<p>If you do allow your service to be started and bound, then when the service has been
-started, the system does <em>not</em> destroy the service when all clients unbind. Instead, you must
-explicitly stop the service, by calling {@link android.app.Service#stopSelf stopSelf()} or {@link
+started, the system does <em>not</em> destroy the service when all clients unbind.
+ Instead, you must
+explicitly stop the service by calling {@link android.app.Service#stopSelf stopSelf()} or {@link
android.content.Context#stopService stopService()}.</p>
-<p>Although you should usually implement either {@link android.app.Service#onBind onBind()}
-<em>or</em> {@link android.app.Service#onStartCommand onStartCommand()}, it's sometimes necessary to
+<p>Although you usually implement either {@link android.app.Service#onBind onBind()}
+<em>or</em> {@link android.app.Service#onStartCommand onStartCommand()}, it's sometimes
+ necessary to
implement both. For example, a music player might find it useful to allow its service to run
indefinitely and also provide binding. This way, an activity can start the service to play some
music and the music continues to play even if the user leaves the application. Then, when the user
-returns to the application, the activity can bind to the service to regain control of playback.</p>
+returns to the application, the activity can bind to the service to regain control of
+ playback.</p>
-<p>Be sure to read the section about <a href="#Lifecycle">Managing the Lifecycle of a Bound
-Service</a>, for more information about the service lifecycle when adding binding to a
-started service.</p>
+<p>For more information about the service lifecycle when adding binding to a started service,
+ see <a href="#Lifecycle">Managing the lifecycle of a bound Service</a>.</p>
</div>
</div>
-<p>A client can bind to the service by calling {@link android.content.Context#bindService
+<p>A client can bind to a service by calling {@link android.content.Context#bindService
bindService()}. When it does, it must provide an implementation of {@link
android.content.ServiceConnection}, which monitors the connection with the service. The {@link
-android.content.Context#bindService bindService()} method returns immediately without a value, but
+android.content.Context#bindService bindService()} method returns immediately without a
+ value, but
when the Android system creates the connection between the
client and service, it calls {@link
android.content.ServiceConnection#onServiceConnected onServiceConnected()} on the {@link
android.content.ServiceConnection}, to deliver the {@link android.os.IBinder} that
the client can use to communicate with the service.</p>
-<p>Multiple clients can connect to the service at once. However, the system calls your service's
-{@link android.app.Service#onBind onBind()} method to retrieve the {@link android.os.IBinder} only
+<p>Multiple clients can connect to a service simultaneously. However, the system calls your service's
+{@link android.app.Service#onBind onBind()} method to retrieve the
+ {@link android.os.IBinder} only
when the first client binds. The system then delivers the same {@link android.os.IBinder} to any
-additional clients that bind, without calling {@link android.app.Service#onBind onBind()} again.</p>
+additional clients that bind, without calling {@link android.app.Service#onBind onBind()}
+ again.</p>
-<p>When the last client unbinds from the service, the system destroys the service (unless the
-service was also started by {@link android.content.Context#startService startService()}).</p>
+<p>When the last client unbinds from the service, the system destroys the service, unless the
+service was also started by {@link android.content.Context#startService startService()}.</p>
-<p>When you implement your bound service, the most important part is defining the interface
-that your {@link android.app.Service#onBind onBind()} callback method returns. There are a few
-different ways you can define your service's {@link android.os.IBinder} interface and the following
-section discusses each technique.</p>
+<p>The most important part of your bound service implementation is defining the interface
+that your {@link android.app.Service#onBind onBind()} callback method returns. The following
+section discusses several different ways that you can define your service's
+ {@link android.os.IBinder} interface.</p>
-
-
-<h2 id="Creating">Creating a Bound Service</h2>
+<h2 id="Creating">Creating a bound service</h2>
<p>When creating a service that provides binding, you must provide an {@link android.os.IBinder}
that provides the programming interface that clients can use to interact with the service. There
@@ -125,12 +137,14 @@
<dl>
<dt><a href="#Binder">Extending the Binder class</a></dt>
- <dd>If your service is private to your own application and runs in the same process as the client
-(which is common), you should create your interface by extending the {@link android.os.Binder} class
+ <dd>If your service is private to your own application and runs in the same process
+ as the client
+(which is common), you should create your interface by extending the {@link android.os.Binder}
+ class
and returning an instance of it from
{@link android.app.Service#onBind onBind()}. The client receives the {@link android.os.Binder} and
can use it to directly access public methods available in either the {@link android.os.Binder}
-implementation or even the {@link android.app.Service}.
+implementation or the {@link android.app.Service}.
<p>This is the preferred technique when your service is merely a background worker for your own
application. The only reason you would not create your interface this way is because
your service is used by other applications or across separate processes.</dd>
@@ -143,20 +157,20 @@
is the basis for a {@link android.os.Messenger} that can then share an {@link android.os.IBinder}
with the client, allowing the client to send commands to the service using {@link
android.os.Message} objects. Additionally, the client can define a {@link android.os.Messenger} of
-its own so the service can send messages back.
+its own, so the service can send messages back.
<p>This is the simplest way to perform interprocess communication (IPC), because the {@link
android.os.Messenger} queues all requests into a single thread so that you don't have to design
your service to be thread-safe.</p>
</dd>
- <dt>Using AIDL</dt>
- <dd>AIDL (Android Interface Definition Language) performs all the work to decompose objects into
-primitives that the operating system can understand and marshall them across processes to perform
+ <dt><a href="{@docRoot}guide/components/aidl.html">Using AIDL</a></dt>
+ <dd>Android Interface Definition Language (AIDL) decomposes objects into
+primitives that the operating system can understand and marshals them across processes to perform
IPC. The previous technique, using a {@link android.os.Messenger}, is actually based on AIDL as
its underlying structure. As mentioned above, the {@link android.os.Messenger} creates a queue of
all the client requests in a single thread, so the service receives requests one at a time. If,
however, you want your service to handle multiple requests simultaneously, then you can use AIDL
-directly. In this case, your service must be capable of multi-threading and be built thread-safe.
+directly. In this case, your service must be thread-safe and capable of multi-threading.
<p>To use AIDL directly, you must
create an {@code .aidl} file that defines the programming interface. The Android SDK tools use
this file to generate an abstract class that implements the interface and handles IPC, which you
@@ -164,19 +178,18 @@
</dd>
</dl>
- <p class="note"><strong>Note:</strong> Most applications <strong>should not</strong> use AIDL to
+ <p class="note"><strong>Note:</strong> Most applications <em>shouldn't</em> use AIDL to
create a bound service, because it may require multithreading capabilities and
-can result in a more complicated implementation. As such, AIDL is not suitable for most applications
+can result in a more complicated implementation. As such, AIDL is not suitable for
+ most applications
and this document does not discuss how to use it for your service. If you're certain that you need
to use AIDL directly, see the <a href="{@docRoot}guide/components/aidl.html">AIDL</a>
document.</p>
-
-
-
<h3 id="Binder">Extending the Binder class</h3>
-<p>If your service is used only by the local application and does not need to work across processes,
+<p>If your service is used only by the local application and does not need to
+ work across processes,
then you can implement your own {@link android.os.Binder} class that provides your client direct
access to public methods in the service.</p>
@@ -187,13 +200,14 @@
<p>Here's how to set it up:</p>
<ol>
- <li>In your service, create an instance of {@link android.os.Binder} that either:
+ <li>In your service, create an instance of {@link android.os.Binder} that does
+ one of the following:
<ul>
- <li>contains public methods that the client can call</li>
- <li>returns the current {@link android.app.Service} instance, which has public methods the
-client can call</li>
- <li>or, returns an instance of another class hosted by the service with public methods the
-client can call</li>
+ <li>Contains public methods that the client can call.</li>
+ <li>Returns the current {@link android.app.Service} instance, which has public methods the
+client can call.</li>
+ <li>Returns an instance of another class hosted by the service with public methods the
+client can call.</li>
</ul>
<li>Return this instance of {@link android.os.Binder} from the {@link
android.app.Service#onBind onBind()} callback method.</li>
@@ -202,12 +216,13 @@
make calls to the bound service using the methods provided.</li>
</ol>
-<p class="note"><strong>Note:</strong> The reason the service and client must be in the same
-application is so the client can cast the returned object and properly call its APIs. The service
+<p class="note"><strong>Note:</strong> The service and client must be in the same
+application so that the client can cast the returned object and properly call its APIs.
+ The service
and client must also be in the same process, because this technique does not perform any
-marshalling across processes.</p>
+marshaling across processes.</p>
-<p>For example, here's a service that provides clients access to methods in the service through
+<p>For example, here's a service that provides clients with access to methods in the service through
a {@link android.os.Binder} implementation:</p>
<pre>
@@ -316,32 +331,30 @@
<p class="note"><strong>Note:</strong> In the example above, the
{@link android.app.Activity#onStop onStop()} method unbinds the client from the service. Clients
should unbind from services at appropriate times, as discussed in
-<a href="#Additional_Notes">Additional Notes</a>.
+<a href="#Additional_Notes">Additional notes</a>.
</p>
<p>For more sample code, see the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">
+{@code
LocalService.java}</a> class and the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.html">{@code
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.html">
+{@code
LocalServiceActivities.java}</a> class in <a
href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>.</p>
-
-
-
-
<h3 id="Messenger">Using a Messenger</h3>
<div class="sidebox-wrapper">
<div class="sidebox">
<h4>Compared to AIDL</h4>
<p>When you need to perform IPC, using a {@link android.os.Messenger} for your interface is
-simpler than implementing it with AIDL, because {@link android.os.Messenger} queues
-all calls to the service, whereas, a pure AIDL interface sends simultaneous requests to the
+simpler than using AIDL, because {@link android.os.Messenger} queues
+all calls to the service. A pure AIDL interface sends simultaneous requests to the
service, which must then handle multi-threading.</p>
<p>For most applications, the service doesn't need to perform multi-threading, so using a {@link
android.os.Messenger} allows the service to handle one call at a time. If it's important
-that your service be multi-threaded, then you should use <a
+that your service be multi-threaded, use <a
href="{@docRoot}guide/components/aidl.html">AIDL</a> to define your interface.</p>
</div>
</div>
@@ -352,10 +365,11 @@
<p>Here's a summary of how to use a {@link android.os.Messenger}:</p>
-<ul>
+<ol>
<li>The service implements a {@link android.os.Handler} that receives a callback for each
call from a client.</li>
- <li>The {@link android.os.Handler} is used to create a {@link android.os.Messenger} object
+ <li>The service uses the {@link android.os.Handler} to create a {@link android.os.Messenger}
+ object
(which is a reference to the {@link android.os.Handler}).</li>
<li>The {@link android.os.Messenger} creates an {@link android.os.IBinder} that the service
returns to clients from {@link android.app.Service#onBind onBind()}.</li>
@@ -365,11 +379,12 @@
<li>The service receives each {@link android.os.Message} in its {@link
android.os.Handler}—specifically, in the {@link android.os.Handler#handleMessage
handleMessage()} method.</li>
-</ul>
+</ol>
-<p>In this way, there are no "methods" for the client to call on the service. Instead, the
-client delivers "messages" ({@link android.os.Message} objects) that the service receives in
+<p>In this way, there are no <em>methods</em> for the client to call on the service. Instead, the
+client delivers <em>messages</em> ({@link android.os.Message} objects) that the service
+ receives in
its {@link android.os.Handler}.</p>
<p>Here's a simple example service that uses a {@link android.os.Messenger} interface:</p>
@@ -488,41 +503,42 @@
}
</pre>
-<p>Notice that this example does not show how the service can respond to the client. If you want the
-service to respond, then you need to also create a {@link android.os.Messenger} in the client. Then
-when the client receives the {@link android.content.ServiceConnection#onServiceConnected
+<p>Notice that this example does not show how the service can respond to the client.
+ If you want the
+service to respond, you need to also create a {@link android.os.Messenger} in the client.
+When the client receives the {@link android.content.ServiceConnection#onServiceConnected
onServiceConnected()} callback, it sends a {@link android.os.Message} to the service that includes
the client's {@link android.os.Messenger} in the {@link android.os.Message#replyTo} parameter
of the {@link android.os.Messenger#send send()} method.</p>
<p>You can see an example of how to provide two-way messaging in the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.html">{@code
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.html">
+{@code
MessengerService.java}</a> (service) and <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.html">{@code
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.html">
+{@code
MessengerServiceActivities.java}</a> (client) samples.</p>
-
-
-
-
-<h2 id="Binding">Binding to a Service</h2>
+<h2 id="Binding">Binding to a service</h2>
<p>Application components (clients) can bind to a service by calling
{@link android.content.Context#bindService bindService()}. The Android
system then calls the service's {@link android.app.Service#onBind
-onBind()} method, which returns an {@link android.os.IBinder} for interacting with the service.</p>
+onBind()} method, which returns an {@link android.os.IBinder} for interacting with
+ the service.</p>
-<p>The binding is asynchronous. {@link android.content.Context#bindService
-bindService()} returns immediately and does <em>not</em> return the {@link android.os.IBinder} to
-the client. To receive the {@link android.os.IBinder}, the client must create an instance of {@link
+<p>The binding is asynchronous, and {@link android.content.Context#bindService
+bindService()} returns immediately without <em>not</em> returning the {@link android.os.IBinder} to
+the client. To receive the {@link android.os.IBinder}, the client must create an
+ instance of {@link
android.content.ServiceConnection} and pass it to {@link android.content.Context#bindService
bindService()}. The {@link android.content.ServiceConnection} includes a callback method that the
system calls to deliver the {@link android.os.IBinder}.</p>
<p class="note"><strong>Note:</strong> Only activities, services, and content providers can bind
-to a service—you <strong>cannot</strong> bind to a service from a broadcast receiver.</p>
+to a service—you <strong>can't</strong> bind to a service from a broadcast receiver.</p>
-<p>So, to bind to a service from your client, you must: </p>
+<p>To bind to a service from your client, follow these steps: </p>
<ol>
<li>Implement {@link android.content.ServiceConnection}.
<p>Your implementation must override two callback methods:</p>
@@ -533,7 +549,8 @@
<dt>{@link android.content.ServiceConnection#onServiceDisconnected
onServiceDisconnected()}</dt>
<dd>The Android system calls this when the connection to the service is unexpectedly
-lost, such as when the service has crashed or has been killed. This is <em>not</em> called when the
+lost, such as when the service has crashed or has been killed. This is <em>not</em>
+ called when the
client unbinds.</dd>
</dl>
</li>
@@ -548,12 +565,12 @@
<p>If your client is still bound to a service when your app destroys the client, destruction
causes the client to unbind. It is better practice to unbind the client as soon as it is done
interacting with the service. Doing so allows the idle service to shut down. For more information
-about appropriate times to bind and unbind, see <a href="#Additional_Notes">Additional Notes</a>.
+about appropriate times to bind and unbind, see <a href="#Additional_Notes">Additional notes</a>.
</p>
</li>
</ol>
-<p>For example, the following snippet connects the client to the service created above by
+<p>The following example connects the client to the service created above by
<a href="#Binder">extending the Binder class</a>, so all it must do is cast the returned
{@link android.os.IBinder} to the {@code LocalService} class and request the {@code
LocalService} instance:</p>
@@ -579,8 +596,9 @@
};
</pre>
-<p>With this {@link android.content.ServiceConnection}, the client can bind to a service by passing
-it to {@link android.content.Context#bindService bindService()}. For example:</p>
+<p>With this {@link android.content.ServiceConnection}, the client can bind to a service
+ by passing
+it to {@link android.content.Context#bindService bindService()}, as shown in the following example:</p>
<pre>
Intent intent = new Intent(this, LocalService.class);
@@ -589,11 +607,21 @@
<ul>
<li>The first parameter of {@link android.content.Context#bindService bindService()} is an
-{@link android.content.Intent} that explicitly names the service to bind (thought the intent
-could be implicit).</li>
+{@link android.content.Intent} that explicitly names the service to bind.
+<p class="caution"><strong>Caution:</strong> If you use an intent to bind to a
+ {@link android.app.Service}, ensure that your app is secure by using an <a href="{@docRoot}guide/components/intents-filters.html#Types">explicit</a>
+intent. Using an implicit intent to start a service is a
+security hazard because you can't be certain what service will respond to the intent,
+and the user can't see which service starts. Beginning with Android 5.0 (API level 21),
+ the system
+throws an exception if you call {@link android.content.Context#bindService bindService()}
+with an implicit intent.</p>
+</li>
+
<li>The second parameter is the {@link android.content.ServiceConnection} object.</li>
<li>The third parameter is a flag indicating options for the binding. It should usually be {@link
-android.content.Context#BIND_AUTO_CREATE} in order to create the service if its not already alive.
+android.content.Context#BIND_AUTO_CREATE} in order to create the service if it's not already
+ alive.
Other possible values are {@link android.content.Context#BIND_DEBUG_UNBIND}
and {@link android.content.Context#BIND_NOT_FOREGROUND}, or {@code 0} for none.</li>
</ul>
@@ -606,10 +634,11 @@
<li>You should always trap {@link android.os.DeadObjectException} exceptions, which are thrown
when the connection has broken. This is the only exception thrown by remote methods.</li>
<li>Objects are reference counted across processes. </li>
- <li>You should usually pair the binding and unbinding during
-matching bring-up and tear-down moments of the client's lifecycle. For example:
+ <li>You usually pair the binding and unbinding during
+matching bring-up and tear-down moments of the client's lifecycle, as described in the
+ following examples:
<ul>
- <li>If you only need to interact with the service while your activity is visible, you
+ <li>If you need to interact with the service only while your activity is visible, you
should bind during {@link android.app.Activity#onStart onStart()} and unbind during {@link
android.app.Activity#onStop onStop()}.</li>
<li>If you want your activity to receive responses even while it is stopped in the
@@ -619,33 +648,34 @@
the service is in another process, then you increase the weight of the process and it becomes
more likely that the system will kill it.</li>
</ul>
- <p class="note"><strong>Note:</strong> You should usually <strong>not</strong> bind and unbind
+ <p class="note"><strong>Note:</strong> You <em>don't</em> usually bind and unbind
during your activity's {@link android.app.Activity#onResume onResume()} and {@link
-android.app.Activity#onPause onPause()}, because these callbacks occur at every lifecycle transition
+android.app.Activity#onPause onPause()}, because these callbacks occur at every
+ lifecycle transition
and you should keep the processing that occurs at these transitions to a minimum. Also, if
-multiple activities in your application bind to the same service and there is a transition between
-two of those activities, the service may be destroyed and recreated as the current activity unbinds
-(during pause) before the next one binds (during resume). (This activity transition for how
+multiple activities in your application bind to the same service and there is a
+ transition between
+two of those activities, the service may be destroyed and recreated as the current
+ activity unbinds
+(during pause) before the next one binds (during resume). This activity transition for how
activities coordinate their lifecycles is described in the <a
href="{@docRoot}guide/components/activities.html#CoordinatingActivities">Activities</a>
-document.)</p>
+document.</p>
</ul>
<p>For more sample code, showing how to bind to a service, see the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">
+{@code
RemoteService.java}</a> class in <a
href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>.</p>
-
-
-
-
-<h2 id="Lifecycle">Managing the Lifecycle of a Bound Service</h2>
+<h2 id="Lifecycle">Managing the lifecycle of a bound service</h2>
<p>When a service is unbound from all clients, the Android system destroys it (unless it was also
started with {@link android.app.Service#onStartCommand onStartCommand()}). As such, you don't have
to manage the lifecycle of your service if it's purely a bound
-service—the Android system manages it for you based on whether it is bound to any clients.</p>
+service—the Android system manages it for you based on whether it is bound to
+ any clients.</p>
<p>However, if you choose to implement the {@link android.app.Service#onStartCommand
onStartCommand()} callback method, then you must explicitly stop the service, because the
@@ -660,17 +690,11 @@
onRebind()} the next time a client binds to the service. {@link android.app.Service#onRebind
onRebind()} returns void, but the client still receives the {@link android.os.IBinder} in its
{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback.
-Below, figure 1 illustrates the logic for this kind of lifecycle.</p>
-
+The following figure illustrates the logic for this kind of lifecycle.</p>
<img src="{@docRoot}images/fundamentals/service_binding_tree_lifecycle.png" alt="" />
<p class="img-caption"><strong>Figure 1.</strong> The lifecycle for a service that is started
and also allows binding.</p>
-
<p>For more information about the lifecycle of a started service, see the <a
href="{@docRoot}guide/components/services.html#Lifecycle">Services</a> document.</p>
-
-
-
-
diff --git a/docs/html/guide/components/fundamentals.jd b/docs/html/guide/components/fundamentals.jd
index ed3ba7d..eaa82c8 100644
--- a/docs/html/guide/components/fundamentals.jd
+++ b/docs/html/guide/components/fundamentals.jd
@@ -6,28 +6,29 @@
<h2>In this document</h2>
<ol>
-<li><a href="#Components">App Components</a>
+<li><a href="#Components">App components</a>
<ol>
<li><a href="#ActivatingComponents">Activating components</a></li>
</ol>
</li>
-<li><a href="#Manifest">The Manifest File</a>
+<li><a href="#Manifest">The manifest file</a>
<ol>
<li><a href="#DeclaringComponents">Declaring components</a></li>
<li><a href="#DeclaringRequirements">Declaring app requirements</a></li>
</ol>
</li>
-<li><a href="#Resources">App Resources</a></li>
+<li><a href="#Resources">App resources</a></li>
</ol>
</div>
</div>
<p>Android apps are written in the Java programming language. The Android SDK tools compile
-your code—along with any data and resource files—into an APK: an <i>Android package</i>,
+your code along with any data and resource files into an APK, an <i>Android package</i>,
which is an archive file with an {@code .apk} suffix. One APK file contains all the contents
of an Android app and is the file that Android-powered devices use to install the app.</p>
-<p>Once installed on a device, each Android app lives in its own security sandbox: </p>
+<p>Each Android app lives in its own security sandbox, protected by
+ the following Android security features: </p>
<ul>
<li>The Android operating system is a multi-user Linux system in which each app is a
@@ -40,54 +41,61 @@
<li>Each process has its own virtual machine (VM), so an app's code runs in isolation from
other apps.</li>
-<li>By default, every app runs in its own Linux process. Android starts the process when any
-of the app's components need to be executed, then shuts down the process when it's no longer
+<li>By default, every app runs in its own Linux process. The Android system starts
+ the process when any
+of the app's components need to be executed, and then shuts down the process
+ when it's no longer
needed or when the system must recover memory for other apps.</li>
</ul>
-<p>In this way, the Android system implements the <em>principle of least privilege</em>. That is,
+<p>The Android system implements the <em>principle of least privilege</em>. That is,
each app, by default, has access only to the components that it requires to do its work and
no more. This creates a very secure environment in which an app cannot access parts of
-the system for which it is not given permission.</p>
-
-<p>However, there are ways for an app to share data with other apps and for an
+the system for which it is not given permission. However, there are ways for an app to share
+ data with other apps and for an
app to access system services:</p>
<ul>
<li>It's possible to arrange for two apps to share the same Linux user ID, in which case
they are able to access each other's files. To conserve system resources, apps with the
-same user ID can also arrange to run in the same Linux process and share the same VM (the
-apps must also be signed with the same certificate).</li>
+same user ID can also arrange to run in the same Linux process and share the same VM. The
+apps must also be signed with the same certificate.</li>
<li>An app can request permission to access device data such as the user's
-contacts, SMS messages, the mountable storage (SD card), camera, Bluetooth, and more. The user has
+contacts, SMS messages, the mountable storage (SD card), camera, and Bluetooth. The user has
to explicitly grant these permissions. For more information, see
<a href="{@docRoot}training/permissions/index.html">Working with System Permissions</a>.</li>
</ul>
-<p>That covers the basics regarding how an Android app exists within the system. The rest of
-this document introduces you to:</p>
+<p>The rest of this document introduces the following concepts:</p>
<ul>
<li>The core framework components that define your app.</li>
- <li>The manifest file in which you declare components and required device features for your
+ <li>The manifest file in which you declare the components and the required device
+ features for your
app.</li>
- <li>Resources that are separate from the app code and allow your app to
+ <li>Resources that are separate from the app code and that allow your app to
gracefully optimize its behavior for a variety of device configurations.</li>
</ul>
-<h2 id="Components">App Components</h2>
+<h2 id="Components">App components</h2>
<p>App components are the essential building blocks of an Android app. Each
component is a different point through which the system can enter your app. Not all
-components are actual entry points for the user and some depend on each other, but each one exists
-as its own entity and plays a specific role—each one is a unique building block that
-helps define your app's overall behavior.</p>
+components are actual entry points for the user and some depend on each other,
+ but each one exists
+as its own entity and plays a specific role.</p>
-<p>There are four different types of app components. Each type serves a distinct purpose
-and has a distinct lifecycle that defines how the component is created and destroyed.</p>
-
-<p>Here are the four types of app components:</p>
+<p>There are four different types of app components:
+<ul>
+<li>Activities.</li>
+<li>Services.</li>
+<li>Content providers.</li>
+<li>Broadcast receivers.</li>
+</ul></p>
+Each type serves a distinct purpose
+and has a distinct lifecycle that defines how the component is created and destroyed.
+ The following sections describe the four types of app components.</p>
<dl>
@@ -98,11 +106,12 @@
emails, another activity to compose an email, and another activity for reading emails. Although
the activities work together to form a cohesive user experience in the email app, each one
is independent of the others. As such, a different app can start any one of these
-activities (if the email app allows it). For example, a camera app can start the
-activity in the email app that composes new mail, in order for the user to share a picture.
+activities if the email app allows it. For example, a camera app can start the
+activity in the email app that composes new mail to allow the user to share a picture.
-<p>An activity is implemented as a subclass of {@link android.app.Activity} and you can learn more
-about it in the <a href="{@docRoot}guide/components/activities.html">Activities</a>
+<p>An activity is implemented as a subclass of {@link android.app.Activity}. You can learn more
+about {@link android.app.Activity} in the
+ <a href="{@docRoot}guide/components/activities.html">Activities</a>
developer guide.</p>
</dd>
@@ -111,13 +120,16 @@
<dd>A <i>service</i> is a component that runs in the background to perform long-running
operations or to perform work for remote processes. A service
-does not provide a user interface. For example, a service might play music in the background while
+does not provide a user interface. For example, a service might play music in the
+ background while
the user is in a different app, or it might fetch data over the network without
-blocking user interaction with an activity. Another component, such as an activity, can start the
+blocking user interaction with an activity. Another component, such as an activity,
+ can start the
service and let it run or bind to it in order to interact with it.
-<p>A service is implemented as a subclass of {@link android.app.Service} and you can learn more
-about it in the <a href="{@docRoot}guide/components/services.html">Services</a> developer
+<p>A service is implemented as a subclass of {@link android.app.Service}. You can learn more
+about {@link android.app.Service} in the <a href="{@docRoot}guide/components/services.html">
+Services</a> developer
guide.</p>
</dd>
@@ -125,12 +137,14 @@
<dt><b>Content providers</b></dt>
<dd>A <i>content provider</i> manages a shared set of app data. You can store the data in
-the file system, an SQLite database, on the web, or any other persistent storage location your
-app can access. Through the content provider, other apps can query or even modify
-the data (if the content provider allows it). For example, the Android system provides a content
+the file system, in a SQLite database, on the web, or on any other persistent storage
+ location that your
+app can access. Through the content provider, other apps can query or modify
+the data if the content provider allows it. For example, the Android system provides a content
provider that manages the user's contact information. As such, any app with the proper
-permissions can query part of the content provider (such as {@link
-android.provider.ContactsContract.Data}) to read and write information about a particular person.
+permissions can query part of the content provider, such as {@link
+android.provider.ContactsContract.Data}, to read and write information about
+ a particular person.
<p>Content providers are also useful for reading and writing data that is private to your
app and not shared. For example, the <a
@@ -148,15 +162,17 @@
<dt><b>Broadcast receivers</b></dt>
<dd>A <i>broadcast receiver</i> is a component that responds to system-wide broadcast
-announcements. Many broadcasts originate from the system—for example, a broadcast announcing
+announcements. Many broadcasts originate from the system—for example,
+ a broadcast announcing
that the screen has turned off, the battery is low, or a picture was captured.
Apps can also initiate broadcasts—for example, to let other apps know that
-some data has been downloaded to the device and is available for them to use. Although broadcast
+some data has been downloaded to the device and is available for them to use.
+ Although broadcast
receivers don't display a user interface, they may <a
href="{@docRoot}guide/topics/ui/notifiers/notifications.html">create a status bar notification</a>
to alert the user when a broadcast event occurs. More commonly, though, a broadcast receiver is
-just a "gateway" to other components and is intended to do a very minimal amount of work. For
-instance, it might initiate a service to perform some work based on the event.
+just a <em>gateway</em> to other components and is intended to do a very minimal amount of work.
+ For instance, it might initiate a service to perform some work based on the event.
<p>A broadcast receiver is implemented as a subclass of {@link android.content.BroadcastReceiver}
and each broadcast is delivered as an {@link android.content.Intent} object. For more information,
@@ -170,52 +186,59 @@
<p>A unique aspect of the Android system design is that any app can start another
app’s component. For example, if you want the user to capture a
photo with the device camera, there's probably another app that does that and your
-app can use it, instead of developing an activity to capture a photo yourself. You don't
+app can use it instead of developing an activity to capture a photo yourself. You don't
need to incorporate or even link to the code from the camera app.
Instead, you can simply start the activity in the camera app that captures a
photo. When complete, the photo is even returned to your app so you can use it. To the user,
it seems as if the camera is actually a part of your app.</p>
-<p>When the system starts a component, it starts the process for that app (if it's not
-already running) and instantiates the classes needed for the component. For example, if your
+<p>When the system starts a component, it starts the process for that app if it's not
+already running and instantiates the classes needed for the component. For example, if your
app starts the activity in the camera app that captures a photo, that activity
runs in the process that belongs to the camera app, not in your app's process.
Therefore, unlike apps on most other systems, Android apps don't have a single entry
-point (there's no {@code main()} function, for example).</p>
+point (there's no {@code main()} function).</p>
<p>Because the system runs each app in a separate process with file permissions that
restrict access to other apps, your app cannot directly activate a component from
-another app. The Android system, however, can. So, to activate a component in
-another app, you must deliver a message to the system that specifies your <em>intent</em> to
+another app. However, the Android system can. To activate a component in
+another app, deliver a message to the system that specifies your <em>intent</em> to
start a particular component. The system then activates the component for you.</p>
-<h3 id="ActivatingComponents">Activating Components</h3>
+<h3 id="ActivatingComponents">Activating components</h3>
<p>Three of the four component types—activities, services, and
broadcast receivers—are activated by an asynchronous message called an <em>intent</em>.
-Intents bind individual components to each other at runtime (you can think of them
-as the messengers that request an action from other components), whether the component belongs
+Intents bind individual components to each other at runtime. You can think of them
+as the messengers that request an action from other components, whether the component belongs
to your app or another.</p>
-<p>An intent is created with an {@link android.content.Intent} object, which defines a message to
-activate either a specific component or a specific <em>type</em> of component—an intent
-can be either explicit or implicit, respectively.</p>
+<p class="note"><strong>Note:</strong> If your app targets Android 5.0 (API level 21) or later,
+ use the {@link android.app.job.JobScheduler} to execute background
+ services. For more information about using this class, see the
+ {@link android.app.job.JobScheduler} reference documentation.</p>
-<p>For activities and services, an intent defines the action to perform (for example, to "view" or
-"send" something) and may specify the URI of the data to act on (among other things that the
-component being started might need to know). For example, an intent might convey a request for an
+<p>An intent is created with an {@link android.content.Intent} object, which defines a message to
+activate either a specific component (explicit intent) or a specific <em>type</em> of component
+ (implicit intent).</p>
+
+<p>For activities and services, an intent defines the action to perform (for example, to
+ <em>view</em> or
+<em>send</em> something) and may specify the URI of the data to act on, among other things that the
+component being started might need to know. For example, an intent might convey a request for an
activity to show an image or to open a web page. In some cases, you can start an
-activity to receive a result, in which case, the activity also returns
-the result in an {@link android.content.Intent} (for example, you can issue an intent to let
-the user pick a personal contact and have it returned to you—the return intent includes a
-URI pointing to the chosen contact).</p>
+activity to receive a result, in which case the activity also returns
+the result in an {@link android.content.Intent}. For example, you can issue an intent to let
+the user pick a personal contact and have it returned to you. The return intent includes a
+URI pointing to the chosen contact.</p>
<p>For broadcast receivers, the intent simply defines the
-announcement being broadcast (for example, a broadcast to indicate the device battery is low
-includes only a known action string that indicates "battery is low").</p>
+announcement being broadcast. For example, a broadcast to indicate the device battery is low
+includes only a known action string that indicates <em>battery is low</em>.</p>
-<p>The other component type, content provider, is not activated by intents. Rather, it is
+<p>Unlike activities, services, and broadcast receivers, content providers are not activated
+ by intents. Rather, they are
activated when targeted by a request from a {@link android.content.ContentResolver}. The content
resolver handles all direct transactions with the content provider so that the component that's
performing transactions with the provider doesn't need to and instead calls methods on the {@link
@@ -224,15 +247,19 @@
<p>There are separate methods for activating each type of component:</p>
<ul>
- <li>You can start an activity (or give it something new to do) by
+ <li>You can start an activity or give it something new to do by
passing an {@link android.content.Intent} to {@link android.content.Context#startActivity
startActivity()} or {@link android.app.Activity#startActivityForResult startActivityForResult()}
(when you want the activity to return a result).</li>
- <li>You can start a service (or give new instructions to an ongoing service) by
+
+
+ <li>With Android 5.0 (API level 21) and later, you can start a service with
+ {@link android.app.job.JobScheduler}. For earlier Android versions, you can start
+ a service (or give new instructions to an ongoing service) by
passing an {@link android.content.Intent} to {@link android.content.Context#startService
-startService()}. Or you can bind to the service by passing an {@link android.content.Intent} to
-{@link android.content.Context#bindService bindService()}.</li>
- <li>You can initiate a broadcast by passing an {@link android.content.Intent} to methods like
+startService()}. You can bind to the service by passing an {@link android.content.Intent} to
+{@link android.content.Context#bindService bindService()}. </li>
+ <li>You can initiate a broadcast by passing an {@link android.content.Intent} to methods such as
{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, {@link
android.content.Context#sendOrderedBroadcast(Intent, String) sendOrderedBroadcast()}, or {@link
android.content.Context#sendStickyBroadcast sendStickyBroadcast()}.</li>
@@ -242,35 +269,35 @@
<p>For more information about using intents, see the <a
href="{@docRoot}guide/components/intents-filters.html">Intents and
-Intent Filters</a> document. More information about activating specific components is also provided
-in the following documents: <a
-href="{@docRoot}guide/components/activities.html">Activities</a>, <a
-href="{@docRoot}guide/components/services.html">Services</a>, {@link
-android.content.BroadcastReceiver} and <a
-href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>.</p>
+Intent Filters</a> document.
+ The following documents provide more information about activating specifc components:
+ <a href="{@docRoot}guide/components/activities.html">Activities</a>,
+ <a href="{@docRoot}guide/components/services.html">Services
+ {@link android.content.BroadcastReceiver}, and
+ <a ref="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>.</p>
-
-<h2 id="Manifest">The Manifest File</h2>
+<h2 id="Manifest">The manifest file</h2>
<p>Before the Android system can start an app component, the system must know that the
-component exists by reading the app's {@code AndroidManifest.xml} file (the "manifest"
-file). Your app must declare all its components in this file, which must be at the root of
-the app project directory.</p>
+component exists by reading the app's <em>manifest file</em>, {@code AndroidManifest.xml}.
+ Your app must declare all its components in this file, which must be at the root of the
+ app project directory.</p>
<p>The manifest does a number of things in addition to declaring the app's components,
-such as:</p>
+such as the following:</p>
<ul>
- <li>Identify any user permissions the app requires, such as Internet access or
+ <li>Identifies any user permissions the app requires, such as Internet access or
read-access to the user's contacts.</li>
- <li>Declare the minimum <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">API Level</a>
+ <li>Declares the minimum
+ <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">API Level</a>
required by the app, based on which APIs the app uses.</li>
- <li>Declare hardware and software features used or required by the app, such as a camera,
+ <li>Declares hardware and software features used or required by the app, such as a camera,
bluetooth services, or a multitouch screen.</li>
- <li>API libraries the app needs to be linked against (other than the Android framework
+ <li>Declares API libraries the app needs to be linked against (other than the Android framework
APIs), such as the <a
-href="http://code.google.com/android/add-ons/google-apis/maps-overview.html">Google Maps
-library</a>.</li>
- <li>And more</li>
+href="http://code.google.com/android/add-ons/google-apis/maps-overview.html">
+Google Maps library</a>.</li>
+
</ul>
@@ -301,47 +328,59 @@
android.app.Activity} subclass and the {@code android:label} attribute specifies a string
to use as the user-visible label for the activity.</p>
-<p>You must declare all app components this way:</p>
+<p>You must declare all app components using the following elements:</p>
<ul>
<li><code><a
href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> elements
-for activities</li>
+for activities.</li>
<li><code><a
href="{@docRoot}guide/topics/manifest/service-element.html"><service></a></code> elements for
-services</li>
+services.</li>
<li><code><a
href="{@docRoot}guide/topics/manifest/receiver-element.html"><receiver></a></code> elements
-for broadcast receivers</li>
+for broadcast receivers.</li>
<li><code><a
href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> elements
-for content providers</li>
+for content providers.</li>
</ul>
<p>Activities, services, and content providers that you include in your source but do not declare
in the manifest are not visible to the system and, consequently, can never run. However,
broadcast
-receivers can be either declared in the manifest or created dynamically in code (as
-{@link android.content.BroadcastReceiver} objects) and registered with the system by calling
+receivers can be either declared in the manifest or created dynamically in code as
+{@link android.content.BroadcastReceiver} objects and registered with the system by calling
{@link android.content.Context#registerReceiver registerReceiver()}.</p>
<p>For more about how to structure the manifest file for your app, see <a
href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml File</a>
documentation. </p>
-
-
<h3 id="DeclaringComponentCapabilities">Declaring component capabilities</h3>
-<p>As discussed above, in <a href="#ActivatingComponents">Activating Components</a>, you can use an
-{@link android.content.Intent} to start activities, services, and broadcast receivers. You can do so
-by explicitly naming the target component (using the component class name) in the intent. However,
-the real power of intents lies in the concept of <em>implicit intents</em>. An implicit intent
-simply describes the type of action to perform (and, optionally, the data upon which you’d like to
-perform the action) and allows the system to find a component on the device that can perform the
-action and start it. If there are multiple components that can perform the action described by the
-intent, then the user selects which one to use.</p>
+<p>As discussed above, in <a href="#ActivatingComponents">Activating components</a>, you can use an
+{@link android.content.Intent} to start activities, services, and broadcast receivers.
-<p>The way the system identifies the components that can respond to an intent is by comparing the
+
+
+You can use an {@link android.content.Intent}
+ by explicitly naming the target component (using the component class name) in the intent.
+ You can also use an implicit intent, which
+describes the type of action to perform and, optionally, the data upon which you’d like to
+perform the action. The implicit intent allows the system to find a component on the device
+ that can perform the
+action and start it. If there are multiple components that can perform the action described by the
+intent, the user selects which one to use.</p>
+
+<p class="caution"><strong>Caution:</strong> If you use an intent to start a
+ {@link android.app.Service}, ensure that your app is secure by using an
+ <a href="{@docRoot}guide/components/intents-filters.html#Types">explicit</a>
+intent. Using an implicit intent to start a service is a
+security hazard because you cannot be certain what service will respond to the intent,
+and the user cannot see which service starts. Beginning with Android 5.0 (API level 21), the system
+throws an exception if you call {@link android.content.Context#bindService bindService()}
+with an implicit intent. Do not declare intent filters for your services. </p>
+
+<p>The system identifies the components that can respond to an intent by comparing the
intent received to the <i>intent filters</i> provided in the manifest file of other apps on
the device.</p>
@@ -351,8 +390,9 @@
adding an <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code
<intent-filter>}</a> element as a child of the component's declaration element.</p>
-<p>For example, if you've built an email app with an activity for composing a new email, you can
-declare an intent filter to respond to "send" intents (in order to send a new email) like this:</p>
+<p>For example, if you build an email app with an activity for composing a new email, you can
+declare an intent filter to respond to "send" intents (in order to send a new email),
+ as shown in the following example:</p>
<pre>
<manifest ... >
...
@@ -368,8 +408,9 @@
</manifest>
</pre>
-<p>Then, if another app creates an intent with the {@link
-android.content.Intent#ACTION_SEND} action and passes it to {@link android.app.Activity#startActivity
+<p>If another app creates an intent with the {@link
+android.content.Intent#ACTION_SEND} action and passes it to
+ {@link android.app.Activity#startActivity
startActivity()}, the system may start your activity so the user can draft and send an
email.</p>
@@ -382,7 +423,7 @@
<h3 id="DeclaringRequirements">Declaring app requirements</h3>
<p>There are a variety of devices powered by Android and not all of them provide the
-same features and capabilities. In order to prevent your app from being installed on devices
+same features and capabilities. To prevent your app from being installed on devices
that lack features needed by your app, it's important that you clearly define a profile for
the types of devices your app supports by declaring device and software requirements in your
manifest file. Most of these declarations are informational only and the system does not read
@@ -391,7 +432,7 @@
<p>For example, if your app requires a camera and uses APIs introduced in Android 2.1 (<a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">API Level</a> 7),
-you should declare these as requirements in your manifest file like this:</p>
+you must declare these as requirements in your manifest file as shown in the following example:</p>
<pre>
<manifest ... >
@@ -402,10 +443,10 @@
</manifest>
</pre>
-<p>Now, devices that do <em>not</em> have a camera and have an
-Android version <em>lower</em> than 2.1 cannot install your app from Google Play.</p>
-
-<p>However, you can also declare that your app uses the camera, but does not
+<p>With the declarations shown in the example, devices that do <em>not</em> have a
+ camera and have an
+Android version <em>lower</em> than 2.1 cannot install your app from Google Play.
+ However, you can declare that your app uses the camera, but does not
<em>require</em> it. In that case, your app must set the <a href=
"{@docRoot}guide/topics/manifest/uses-feature-element.html#required">{@code required}</a>
attribute to {@code "false"} and check at runtime whether
@@ -417,15 +458,15 @@
-<h2 id="Resources">App Resources</h2>
+<h2 id="Resources">App resources</h2>
<p>An Android app is composed of more than just code—it requires resources that are
separate from the source code, such as images, audio files, and anything relating to the visual
-presentation of the app. For example, you should define animations, menus, styles, colors,
+presentation of the app. For example, you can define animations, menus, styles, colors,
and the layout of activity user interfaces with XML files. Using app resources makes it easy
-to update various characteristics of your app without modifying code and—by providing
-sets of alternative resources—enables you to optimize your app for a variety of
-device configurations (such as different languages and screen sizes).</p>
+to update various characteristics of your app without modifying code. Providing
+sets of alternative resources enables you to optimize your app for a variety of
+device configurations, such as different languages and screen sizes.</p>
<p>For every resource that you include in your Android project, the SDK build tools define a unique
integer ID, which you can use to reference the resource from your app code or from
@@ -435,20 +476,22 @@
user interface.</p>
<p>One of the most important aspects of providing resources separate from your source code
-is the ability for you to provide alternative resources for different device
-configurations. For example, by defining UI strings in XML, you can translate the strings into other
-languages and save those strings in separate files. Then, based on a language <em>qualifier</em>
+is the ability to provide alternative resources for different device
+configurations. For example, by defining UI strings in XML, you can translate
+ the strings into other
+languages and save those strings in separate files. Then Android applies the
+ appropriate language strings
+to your UI based on a language <em>qualifier</em>
that you append to the resource directory's name (such as {@code res/values-fr/} for French string
-values) and the user's language setting, the Android system applies the appropriate language strings
-to your UI.</p>
+values) and the user's language setting.</p>
<p>Android supports many different <em>qualifiers</em> for your alternative resources. The
qualifier is a short string that you include in the name of your resource directories in order to
-define the device configuration for which those resources should be used. As another
-example, you should often create different layouts for your activities, depending on the
-device's screen orientation and size. For example, when the device screen is in portrait
+define the device configuration for which those resources should be used. For
+example, you should create different layouts for your activities, depending on the
+device's screen orientation and size. When the device screen is in portrait
orientation (tall), you might want a layout with buttons to be vertical, but when the screen is in
-landscape orientation (wide), the buttons should be aligned horizontally. To change the layout
+landscape orientation (wide), the buttons could be aligned horizontally. To change the layout
depending on the orientation, you can define two different layouts and apply the appropriate
qualifier to each layout's directory name. Then, the system automatically applies the appropriate
layout depending on the current device orientation.</p>
@@ -465,15 +508,15 @@
<dl>
<dt><a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent Filters</a>
</dt>
- <dd>Information about how to use the {@link android.content.Intent} APIs to
+ <dd>How to use the {@link android.content.Intent} APIs to
activate app components, such as activities and services, and how to make your app components
available for use by other apps.</dd>
<dt><a href="{@docRoot}guide/components/activities.html">Activities</a></dt>
- <dd>Information about how to create an instance of the {@link android.app.Activity} class,
+ <dd>How to create an instance of the {@link android.app.Activity} class,
which provides a distinct screen in your application with a user interface.</dd>
<dt><a
href="{@docRoot}guide/topics/resources/providing-resources.html">Providing Resources</a></dt>
- <dd>Information about how Android apps are structured to separate app resources from the
+ <dd>How Android apps are structured to separate app resources from the
app code, including how you can provide alternative resources for specific device
configurations.
</dd>
@@ -484,14 +527,13 @@
<dl>
<dt><a href="{@docRoot}guide/practices/compatibility.html"
>Device Compatibility</a></dt>
- <dd>Information about Android works on different types of devices and an introduction
+ <dd>How Android works on different types of devices and an introduction
to how you can optimize your app for each device or restrict your app's availability
to different devices.</dd>
<dt><a href="{@docRoot}guide/topics/security/permissions.html"
>System Permissions</a></dt>
- <dd>Information about how Android restricts app access to certain APIs with a permission
+ <dd>How Android restricts app access to certain APIs with a permission
system that requires the user's consent for your app to use those APIs.</dd>
</dl>
</div>
</div>
-
diff --git a/docs/html/guide/components/intents-filters.jd b/docs/html/guide/components/intents-filters.jd
index d1d8c78..8f41bc3 100644
--- a/docs/html/guide/components/intents-filters.jd
+++ b/docs/html/guide/components/intents-filters.jd
@@ -7,21 +7,21 @@
<h2>In this document</h2>
<ol>
- <li><a href="#Types">Intent Types</a></li>
- <li><a href="#Building">Building an Intent</a>
+ <li><a href="#Types">Intent types</a></li>
+ <li><a href="#Building">Building an intent</a>
<ol>
<li><a href="#ExampleExplicit">Example explicit intent</a></li>
<li><a href="#ExampleSend">Example implicit intent</a></li>
<li><a href="#ForceChooser">Forcing an app chooser</a></li>
</ol>
</li>
- <li><a href="#Receiving">Receiving an Implicit Intent</a>
+ <li><a href="#Receiving">Receiving an implicit intent</a>
<ol>
<li><a href="#ExampleFilters">Example filters</a></li>
</ol>
</li>
- <li><a href="#PendingIntent">Using a Pending Intent</a></li>
- <li><a href="#Resolution">Intent Resolution</a>
+ <li><a href="#PendingIntent">Using a pending intent</a></li>
+ <li><a href="#Resolution">Intent resolution</a>
<ol>
<li><a href="#ActionTest">Action test</a></li>
<li><a href="#CategoryTest">Category test</a></li>
@@ -46,13 +46,14 @@
<p>An {@link android.content.Intent} is a messaging object you can use to request an action
from another <a href="{@docRoot}guide/components/fundamentals.html#Components">app component</a>.
Although intents facilitate communication between components in several ways, there are three
-fundamental use-cases:</p>
+fundamental use cases:</p>
<ul>
-<li><b>To start an activity:</b>
+<li><b>Starting an activity</b>
<p>An {@link android.app.Activity} represents a single screen in an app. You can start a new
instance of an {@link android.app.Activity} by passing an {@link android.content.Intent}
-to {@link android.content.Context#startActivity startActivity()}. The {@link android.content.Intent}
+to {@link android.content.Context#startActivity startActivity()}.
+ The {@link android.content.Intent}
describes the activity to start and carries any necessary data.</p>
<p>If you want to receive a result from the activity when it finishes,
@@ -63,10 +64,16 @@
For more information, see the <a
href="{@docRoot}guide/components/activities.html">Activities</a> guide.</p></li>
-<li><b>To start a service:</b>
+<li><b>Starting a service</b>
<p>A {@link android.app.Service} is a component that performs operations in the background
-without a user interface. You can start a service to perform a one-time operation
-(such as download a file) by passing an {@link android.content.Intent}
+without a user interface. With Android 5.0 (API level 21) and later, you can start a service
+ with {@link android.app.job.JobScheduler}. For more information
+ about {@link android.app.job.JobScheduler}, see its
+ {@link android.app.job.JobScheduler API-reference documentation}.</p>
+<p>For versions earlier than Android 5.0 (API level 21), you can start a service by using
+methods of the {@link android.app.Service} class. You can start a service
+ to perform a one-time operation
+(such as downloading a file) by passing an {@link android.content.Intent}
to {@link android.content.Context#startService startService()}. The {@link android.content.Intent}
describes the service to start and carries any necessary data.</p>
@@ -75,7 +82,7 @@
android.content.Context#bindService bindService()}</code>. For more information, see the <a
href="{@docRoot}guide/components/services.html">Services</a> guide.</p></li>
-<li><b>To deliver a broadcast:</b>
+<li><b>Delivering a broadcast</b>
<p>A broadcast is a message that any app can receive. The system delivers various
broadcasts for system events, such as when the system boots up or the device starts charging.
You can deliver a broadcast to other apps by passing an {@link android.content.Intent}
@@ -89,7 +96,7 @@
-<h2 id="Types">Intent Types</h2>
+<h2 id="Types">Intent types</h2>
<p>There are two types of intents:</p>
@@ -97,7 +104,7 @@
<li><b>Explicit intents</b> specify the component to start by name (the
fully-qualified class name). You'll typically use an explicit intent to start a component in
your own app, because you know the class name of the activity or service you want to start. For
-example, start a new activity in response to a user action or start a service to download
+example, you can start a new activity in response to a user action or start a service to download
a file in the background.</li>
<li><b>Implicit intents</b> do not name a specific component, but instead declare a general action
@@ -106,12 +113,13 @@
app show a specified location on a map.</li>
</ul>
-<p>When you create an explicit intent to start an activity or service, the system immediately
+<p>Figure 1 shows how an intent is delivered to start an activity. When you create an
+ explicit intent to start an activity or service, the system immediately
starts the app component specified in the {@link android.content.Intent} object.</p>
<div class="figure" style="width:446px">
<img src="{@docRoot}images/components/intent-filters@2x.png" width="446" alt=""/>
-<p class="img-caption"><strong>Figure 1.</strong> Illustration of how an implicit intent is
+<p class="img-caption"><strong>Figure 1.</strong> How an implicit intent is
delivered through the system to start another activity: <b>[1]</b> <em>Activity A</em> creates an
{@link android.content.Intent} with an action description and passes it to {@link
android.content.Context#startActivity startActivity()}. <b>[2]</b> The Android System searches all
@@ -135,11 +143,12 @@
Likewise, if you do <em>not</em> declare any intent filters for an activity, then it can be started
only with an explicit intent.</p>
-<p class="caution"><strong>Caution:</strong> To ensure your app is secure, always use an explicit
+<p class="caution"><strong>Caution:</strong> To ensure that your app is secure, always
+ use an explicit
intent when starting a {@link android.app.Service} and do not
declare intent filters for your services. Using an implicit intent to start a service is a
-security hazard because you cannot be certain what service will respond to the intent,
-and the user cannot see which service starts. Beginning with Android 5.0 (API level 21), the system
+security hazard because you can't be certain what service will respond to the intent,
+and the user can't see which service starts. Beginning with Android 5.0 (API level 21), the system
throws an exception if you call {@link android.content.Context#bindService bindService()}
with an implicit intent.</p>
@@ -147,7 +156,7 @@
-<h2 id="Building">Building an Intent</h2>
+<h2 id="Building">Building an intent</h2>
<p>An {@link android.content.Intent} object carries information that the Android system uses
to determine which component to start (such as the exact component name or component
@@ -163,22 +172,23 @@
<dd>The name of the component to start.
<p>This is optional, but it's the critical piece of information that makes an intent
-<b>explicit</b>, meaning that the intent should be delivered only to the app component
-defined by the component name. Without a component name, the intent is <b>implicit</b> and the
+<em>explicit</em>, meaning that the intent should be delivered only to the app component
+defined by the component name. Without a component name, the intent is <em>implicit</em> and the
system decides which component should receive the intent based on the other intent information
-(such as the action, data, and category—described below). So if you need to start a specific
+(such as the action, data, and category—described below). If you need to start a specific
component in your app, you should specify the component name.</p>
-<p class="note"><strong>Note:</strong> When starting a {@link android.app.Service}, you should
-<strong>always specify the component name</strong>. Otherwise, you cannot be certain what service
+<p class="note"><strong>Note:</strong> When starting a {@link android.app.Service},
+ <em>always specify the component name</em>. Otherwise, you cannot be certain what service
will respond to the intent, and the user cannot see which service starts.</p>
<p>This field of the {@link android.content.Intent} is a
{@link android.content.ComponentName} object, which you can specify using a fully
-qualified class name of the target component, including the package name of the app. For example,
+qualified class name of the target component, including the package name of the app, for example,
{@code com.example.ExampleActivity}. You can set the component name with {@link
android.content.Intent#setComponent setComponent()}, {@link android.content.Intent#setClass
-setClass()}, {@link android.content.Intent#setClassName(String, String) setClassName()}, or with the
+setClass()}, {@link android.content.Intent#setClassName(String, String) setClassName()},
+ or with the
{@link android.content.Intent} constructor.</p>
</dd>
@@ -188,10 +198,10 @@
<p>In the case of a broadcast intent, this is the action that took place and is being reported.
The action largely determines how the rest of the intent is structured—particularly
-what is contained in the data and extras.
+the information that is contained in the data and extras.
<p>You can specify your own actions for use by intents within your app (or for use by other
-apps to invoke components in your app), but you should usually use action constants
+apps to invoke components in your app), but you usually specify action constants
defined by the {@link android.content.Intent} class or other framework classes. Here are some
common actions for starting an activity:</p>
@@ -203,7 +213,7 @@
view in a map app.</dd>
<dt>{@link android.content.Intent#ACTION_SEND}</dt>
- <dd>Also known as the "share" intent, you should use this in an intent with {@link
+ <dd>Also known as the <em>share</em> intent, you should use this in an intent with {@link
android.content.Context#startActivity startActivity()} when you have some data that the user can
share through another app, such as an email app or social sharing app.</dd>
</dl>
@@ -217,12 +227,13 @@
setAction()} or with an {@link android.content.Intent} constructor.</p>
<p>If you define your own actions, be sure to include your app's package name
-as a prefix. For example:</p>
+as a prefix, as shown in the following example:</p>
<pre>static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";</pre>
</dd>
<dt><b>Data</b></dt>
-<dd>The URI (a {@link android.net.Uri} object) that references the data to be acted on and/or the
+<dd>The URI (a {@link android.net.Uri} object) that references the data to
+ be acted on and/or the
MIME type of that data. The type of data supplied is generally dictated by the intent's action. For
example, if the action is {@link android.content.Intent#ACTION_EDIT}, the data should contain the
URI of the document to edit.
@@ -231,10 +242,11 @@
it's often important to specify the type of data (its MIME type) in addition to its URI.
For example, an activity that's able to display images probably won't be able
to play an audio file, even though the URI formats could be similar.
-So specifying the MIME type of your data helps the Android
+Specifying the MIME type of your data helps the Android
system find the best component to receive your intent.
However, the MIME type can sometimes be inferred from the URI—particularly when the data is a
-{@code content:} URI, which indicates the data is located on the device and controlled by a
+{@code content:} URI. A {@code content:} URI indicates the data is located on the device
+ and controlled by a
{@link android.content.ContentProvider}, which makes the data MIME type visible to the system.</p>
<p>To set only the data URI, call {@link android.content.Intent#setData setData()}.
@@ -243,7 +255,7 @@
android.content.Intent#setDataAndType setDataAndType()}.</p>
<p class="caution"><strong>Caution:</strong> If you want to set both the URI and MIME type,
-<strong>do not</strong> call {@link android.content.Intent#setData setData()} and
+<em>don't</em> call {@link android.content.Intent#setData setData()} and
{@link android.content.Intent#setType setType()} because they each nullify the value of the other.
Always use {@link android.content.Intent#setDataAndType setDataAndType()} to set both
URI and MIME type.</p>
@@ -258,7 +270,7 @@
<dl>
<dt>{@link android.content.Intent#CATEGORY_BROWSABLE}</dt>
<dd>The target activity allows itself to be started by a web browser to display data
- referenced by a link—such as an image or an e-mail message.
+ referenced by a link, such as an image or an e-mail message.
</dd>
<dt>{@link android.content.Intent#CATEGORY_LAUNCHER}</dt>
<dd>The activity is the initial activity of a task and is listed in
@@ -276,14 +288,14 @@
<p>These properties listed above (component name, action, data, and category) represent the
defining characteristics of an intent. By reading these properties, the Android system
-is able to resolve which app component it should start.</p>
-
-<p>However, an intent can carry additional information that does not affect
-how it is resolved to an app component. An intent can also supply:</p>
+is able to resolve which app component it should start. However, an intent can carry
+ additional information that does not affect
+how it is resolved to an app component. An intent can also supply the following information:</p>
<dl>
<dt><b>Extras</b></dt>
-<dd>Key-value pairs that carry additional information required to accomplish the requested action.
+<dd>Key-value pairs that carry additional information required to accomplish
+ the requested action.
Just as some actions use particular kinds of data URIs, some actions also use particular extras.
<p>You can add extra data with various {@link android.content.Intent#putExtra putExtra()} methods,
@@ -293,21 +305,22 @@
android.content.Intent#putExtras putExtras()}.</p>
<p>For example, when creating an intent to send an email with
-{@link android.content.Intent#ACTION_SEND}, you can specify the "to" recipient with the
-{@link android.content.Intent#EXTRA_EMAIL} key, and specify the "subject" with the
+{@link android.content.Intent#ACTION_SEND}, you can specify the <em>to</em> recipient with the
+{@link android.content.Intent#EXTRA_EMAIL} key, and specify the <em>subject</em> with the
{@link android.content.Intent#EXTRA_SUBJECT} key.</p>
<p>The {@link android.content.Intent} class specifies many {@code EXTRA_*} constants
for standardized data types. If you need to declare your own extra keys (for intents that
your app receives), be sure to include your app's package name
-as a prefix. For example:</p>
+as a prefix, as shown in the following example:</p>
<pre>static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";</pre>
</dd>
<dt><b>Flags</b></dt>
-<dd>Flags defined in the {@link android.content.Intent} class that function as metadata for the
+<dd>Flags are defined in the {@link android.content.Intent} class that function as metadata for the
intent. The flags may instruct the Android system how to launch an activity (for example, which
-<a href="{@docRoot}guide/components/tasks-and-back-stack.html">task</a> the activity should belong
+<a href="{@docRoot}guide/components/tasks-and-back-stack.html">task</a>
+ the activity should belong
to) and how to treat it after it's launched (for example, whether it belongs in the list of recent
activities).
@@ -354,7 +367,8 @@
to perform the action. Using an implicit intent is useful when your app cannot perform the
action, but other apps probably can and you'd like the user to pick which app to use.</p>
-<p>For example, if you have content you want the user to share with other people, create an intent
+<p>For example, if you have content that you want the user to share with other people,
+ create an intent
with the {@link android.content.Intent#ACTION_SEND} action
and add extras that specify the content to share. When you call
{@link android.content.Context#startActivity startActivity()} with that intent, the user can
@@ -362,13 +376,15 @@
<p class="caution"><strong>Caution:</strong> It's possible that a user won't have <em>any</em>
apps that handle the implicit intent you send to {@link android.content.Context#startActivity
-startActivity()}. If that happens, the call will fail and your app will crash. To verify
+startActivity()}. If that happens, the call fails and your app crashes. To verify
that an activity will receive the intent, call {@link android.content.Intent#resolveActivity
resolveActivity()} on your {@link android.content.Intent} object. If the result is non-null,
-then there is at least one app that can handle the intent and it's safe to call
+ there is at least one app that can handle the intent and it's safe to call
{@link android.content.Context#startActivity startActivity()}. If the result is null,
-you should not use the intent and, if possible, you should disable the feature that issues
-the intent.</p>
+ do not use the intent and, if possible, you should disable the feature that issues
+the intent. The following example shows how to verify that the intent resolves
+to an activity. This example doesn't use a URI, but the intent's data type
+is declared to specify the content carried by the extras.</p>
<pre>
@@ -384,8 +400,7 @@
}
</pre>
-<p class="note"><strong>Note:</strong> In this case, a URI is not used, but the intent's data type
-is declared to specify the content carried by the extras.</p>
+
<p>When {@link android.content.Context#startActivity startActivity()} is called, the system
@@ -393,7 +408,7 @@
intent with the {@link android.content.Intent#ACTION_SEND} action and that carries "text/plain"
data). If there's only one app that can handle it, that app opens immediately and is given the
intent. If multiple activities accept the intent, the system
-displays a dialog so the user can pick which app to use..</p>
+displays a dialog such as the one shown in Figure 2, so the user can pick which app to use.</p>
<div class="figure" style="width:200px">
@@ -405,23 +420,26 @@
<p>When there is more than one app that responds to your implicit intent,
the user can select which app to use and make that app the default choice for the
-action. This is nice when performing an action for which the user
-probably wants to use the same app from now on, such as when opening a web page (users
-often prefer just one web browser) .</p>
+action. The ability to select a default is helpful when performing an action for which the user
+probably wants to use the same app every time, such as when opening a web page (users
+often prefer just one web browser).</p>
<p>However, if multiple apps can respond to the intent and the user might want to use a different
app each time, you should explicitly show a chooser dialog. The chooser dialog asks the
-user to select which app to use for the action every time (the user cannot select a default app for
+user to select which app to use for the action (the user cannot select a default app for
the action). For example, when your app performs "share" with the {@link
android.content.Intent#ACTION_SEND} action, users may want to share using a different app depending
-on their current situation, so you should always use the chooser dialog, as shown in figure 2.</p>
+on their current situation, so you should always use the chooser dialog, as shown in Figure 2.</p>
<p>To show the chooser, create an {@link android.content.Intent} using {@link
android.content.Intent#createChooser createChooser()} and pass it to {@link
-android.app.Activity#startActivity startActivity()}. For example:</p>
+android.app.Activity#startActivity startActivity()}, as shown in the following example.
+ This example displays a dialog with a list of apps that respond to the intent passed to the {@link
+android.content.Intent#createChooser createChooser()} method and uses the supplied text as the
+dialog title.</p>
<pre>
Intent sendIntent = new Intent(Intent.ACTION_SEND);
@@ -439,26 +457,16 @@
}
</pre>
-<p>This displays a dialog with a list of apps that respond to the intent passed to the {@link
-android.content.Intent#createChooser createChooser()} method and uses the supplied text as the
-dialog title.</p>
-
-
-
-
-
-
-
-<h2 id="Receiving">Receiving an Implicit Intent</h2>
+<h2 id="Receiving">Receiving an implicit intent</h2>
<p>To advertise which implicit intents your app can receive, declare one or more intent filters for
each of your app components with an <a href=
-"{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a>
+"{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a>
element in your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest file</a>.
Each intent filter specifies the type of intents it accepts based on the intent's action,
-data, and category. The system will deliver an implicit intent to your app component only if the
+data, and category. The system delivers an implicit intent to your app component only if the
intent can pass through one of your intent filters.</p>
<p class="note"><strong>Note:</strong> An explicit intent is always delivered to its target,
@@ -471,28 +479,28 @@
in the {@link android.content.Intent} (such as to show the editor controls or not).</p>
<p>Each intent filter is defined by an <a
-href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a>
+href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a>
element in the app's manifest file, nested in the corresponding app component (such
-as an <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>
+as an <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>
element). Inside the <a
-href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a>,
+href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a>,
you can specify the type of intents to accept using one or more
of these three elements:</p>
<dl>
-<dt><a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a></dt>
+<dt><a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a></dt>
<dd>Declares the intent action accepted, in the {@code name} attribute. The value
must be the literal string value of an action, not the class constant.</dd>
-<dt><a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a></dt>
+<dt><a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a></dt>
<dd>Declares the type of data accepted, using one or more attributes that specify various
aspects of the data URI (<code>scheme</code>, <code>host</code>, <code>port</code>,
- <code>path</code>, etc.) and MIME type.</dd>
-<dt><a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a></dt>
+ <code>path</code>) and MIME type.</dd>
+<dt><a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a></dt>
<dd>Declares the intent category accepted, in the {@code name} attribute. The value
must be the literal string value of an action, not the class constant.
- <p class="note"><strong>Note:</strong> In order to receive implicit intents, you
- <strong>must include</strong> the
+ <p class="note"><strong>Note:</strong> To receive implicit intents, you
+ <em>must include</em> the
{@link android.content.Intent#CATEGORY_DEFAULT} category in the intent filter. The methods
{@link android.app.Activity#startActivity startActivity()} and
{@link android.app.Activity#startActivityForResult startActivityForResult()} treat all intents
@@ -515,12 +523,12 @@
</activity>
</pre>
-<p>It's okay to create a filter that includes more than one instance of
-<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a>,
-<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>, or
-<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a>.
-If you do, you simply need to be certain that the component can handle any and all combinations
-of those filter elements.</p>
+<p>You can create a filter that includes more than one instance of
+<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a>,
+<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>, or
+<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a>.
+If you do, you need to be certain that the component can handle any and all
+combinations of those filter elements.</p>
<p>When you want to handle multiple kinds of intents, but only in specific combinations of
action, data, and category type, then you need to create multiple intent filters.</p>
@@ -569,8 +577,8 @@
<h3 id="ExampleFilters">Example filters</h3>
-<p>To better understand some of the intent filter behaviors, look at the following snippet
-from the manifest file of a social-sharing app.</p>
+<p>To demonstrate some of the intent filter behaviors, here is an example
+from the manifest file of a social-sharing app:</p>
<pre>
<activity android:name="MainActivity">
@@ -607,9 +615,9 @@
indicates this is the main entry point and does not expect any intent data.</li>
<li>The {@link android.content.Intent#CATEGORY_LAUNCHER} category indicates that this activity's
icon should be placed in the system's app launcher. If the <a
- href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> element
+ href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> element
does not specify an icon with {@code icon}, then the system uses the icon from the <a
- href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>
+ href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>
element.</li>
</ul>
<p>These two must be paired together in order for the activity to appear in the app launcher.</p>
@@ -620,7 +628,7 @@
intent matching one of the two intent filters.</p>
<p class="note"><strong>Note:</strong> The MIME type,
-<a href="https://developers.google.com/panorama/android/">{@code
+<a href="https://developers.google.com/panorama/android/" class="external-link">{@code
application/vnd.google.panorama360+jpg}</a>, is a special data type that specifies
panoramic photos, which you can handle with the <a
href="{@docRoot}reference/com/google/android/gms/panorama/package-summary.html">Google
@@ -638,7 +646,7 @@
-<h2 id="PendingIntent">Using a Pending Intent</h2>
+<h2 id="PendingIntent">Using a pending intent</h2>
<p>A {@link android.app.PendingIntent} object is a wrapper around an {@link
android.content.Intent} object. The primary purpose of a {@link android.app.PendingIntent}
@@ -646,25 +654,25 @@
to use the contained {@link android.content.Intent} as if it were executed from your
app's own process.</p>
-<p>Major use cases for a pending intent include:</p>
+<p>Major use cases for a pending intent include the following:</p>
<ul>
- <li>Declare an intent to be executed when the user performs an action with your <a
+ <li>Declaring an intent to be executed when the user performs an action with your <a
href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Notification</a>
(the Android system's {@link android.app.NotificationManager}
executes the {@link android.content.Intent}).
- <li>Declare an intent to be executed when the user performs an action with your
+ <li>Declaring an intent to be executed when the user performs an action with your
<a href="{@docRoot}guide/topics/appwidgets/index.html">App Widget</a>
(the Home screen app executes the {@link android.content.Intent}).
- <li>Declare an intent to be executed at a specified time in the future (the Android
+ <li>Declaring an intent to be executed at a specified future time (the Android
system's {@link android.app.AlarmManager} executes the {@link android.content.Intent}).
</ul>
-<p>Because each {@link android.content.Intent} object is designed to be handled by a specific
+<p>Just as each {@link android.content.Intent} object is designed to be handled by a specific
type of app component (either an {@link android.app.Activity}, a {@link android.app.Service}, or
a {@link android.content.BroadcastReceiver}), so too must a {@link android.app.PendingIntent} be
-created with the same consideration. When using a pending intent, your app will not
+created with the same consideration. When using a pending intent, your app doesn't
execute the intent with a call such as {@link android.content.Context#startActivity
-startActivity()}. You must instead declare the intended component type when you create the
+startActivity()}. Instead, you must declare the intended component type when you create the
{@link android.app.PendingIntent} by calling the respective creator method:</p>
<ul>
@@ -677,14 +685,14 @@
</ul>
<p>Unless your app is <em>receiving</em> pending intents from other apps,
-the above methods to create a {@link android.app.PendingIntent} are the only
-{@link android.app.PendingIntent} methods you'll probably ever need.</p>
+the above methods to create a {@link android.app.PendingIntent} are probably the only
+{@link android.app.PendingIntent} methods you'll ever need.</p>
<p>Each method takes the current app {@link android.content.Context}, the
{@link android.content.Intent} you want to wrap, and one or more flags that specify
how the intent should be used (such as whether the intent can be used more than once).</p>
-<p>More information about using pending intents is provided with the documentation for each
+<p>For more information about using pending intents, see the documentation for each
of the respective use cases, such as in the <a
href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Notifications</a>
and <a href="{@docRoot}guide/topics/appwidgets/index.html">App Widgets</a> API guides.</p>
@@ -695,27 +703,27 @@
-<h2 id="Resolution">Intent Resolution</h2>
+<h2 id="Resolution">Intent resolution</h2>
<p>When the system receives an implicit intent to start an activity, it searches for the
-best activity for the intent by comparing the intent to intent filters based on three aspects:</p>
+best activity for the intent by comparing the it to intent filters based on three aspects:</p>
<ul>
- <li>The intent action
- <li>The intent data (both URI and data type)
- <li>The intent category
+ <li>Action.
+ <li>Data (both URI and data type).
+ <li>Category.
</ul>
-<p>The following sections describe how intents are matched to the appropriate component(s)
-in terms of how the intent filter is declared in an app's manifest file.</p>
+<p>The following sections describe how intents are matched to the appropriate components
+according to the intent filter declaration in an app's manifest file.</p>
<h3 id="ActionTest">Action test</h3>
<p>To specify accepted intent actions, an intent filter can declare zero or more
<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code
-<action>}</a> elements. For example:</p>
+<action>}</a> elements, as shown in the following example:</p>
<pre>
<intent-filter>
@@ -725,13 +733,13 @@
</intent-filter>
</pre>
-<p>To get through this filter, the action specified in the {@link android.content.Intent}
+<p>To pass this filter, the action specified in the {@link android.content.Intent}
must match one of the actions listed in the filter.</p>
<p>If the filter does not list any actions, there is nothing for an
intent to match, so all intents fail the test. However, if an {@link android.content.Intent}
-does not specify an action, it will pass the test (as long as the filter
-contains at least one action).</p>
+does not specify an action, it passes the test as long as the filter
+contains at least one action.</p>
@@ -739,7 +747,7 @@
<p>To specify accepted intent categories, an intent filter can declare zero or more
<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code
-<category>}</a> elements. For example:</p>
+<category>}</a> elements, as shown in the following example:</p>
<pre>
<intent-filter>
@@ -752,17 +760,17 @@
<p>For an intent to pass the category test, every category in the {@link android.content.Intent}
must match a category in the filter. The reverse is not necessary—the intent filter may
declare more categories than are specified in the {@link android.content.Intent} and the
-{@link android.content.Intent} will still pass. Therefore, an intent with no categories should
-always pass this test, regardless of what categories are declared in the filter.</p>
+{@link android.content.Intent} still passes. Therefore, an intent with no categories
+always passes this test, regardless of what categories are declared in the filter.</p>
<p class="note"><strong>Note:</strong>
-Android automatically applies the the {@link android.content.Intent#CATEGORY_DEFAULT} category
+Android automatically applies the {@link android.content.Intent#CATEGORY_DEFAULT} category
to all implicit intents passed to {@link
android.content.Context#startActivity startActivity()} and {@link
android.app.Activity#startActivityForResult startActivityForResult()}.
-So if you want your activity to receive implicit intents, it must
-include a category for {@code "android.intent.category.DEFAULT"} in its intent filters (as
-shown in the previous {@code <intent-filter>} example.</p>
+If you want your activity to receive implicit intents, it must
+include a category for {@code "android.intent.category.DEFAULT"} in its intent filters, as
+shown in the previous {@code <intent-filter>} example.</p>
@@ -770,7 +778,7 @@
<p>To specify accepted intent data, an intent filter can declare zero or more
<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code
-<data>}</a> elements. For example:</p>
+<data>}</a> elements, as shown in the following example:</p>
<pre>
<intent-filter>
@@ -781,15 +789,16 @@
</pre>
<p>Each <code><a href="{@docRoot}guide/topics/manifest/data-element.html"><data></a></code>
-element can specify a URI structure and a data type (MIME media type). There are separate
-attributes — {@code scheme}, {@code host}, {@code port},
-and {@code path} — for each part of the URI:
+element can specify a URI structure and a data type (MIME media type).
+ Each part of the URI is a separate
+attribute: {@code scheme}, {@code host}, {@code port},
+and {@code path}:
</p>
-<p style="margin-left: 2em">{@code <scheme>://<host>:<port>/<path>}</p>
+<p style="margin-left: 2em">{@code <scheme>://<host>:<port>/<path>}</p>
<p>
-For example:
+The following example shows possible values for these attributes:
</p>
<p style="margin-left: 2em">{@code content://com.example.project:200/folder/subfolder/etc}</p>
@@ -799,7 +808,7 @@
</p>
<p>Each of these attributes is optional in a <a
-href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> element,
+href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> element,
but there are linear dependencies:</p>
<ul>
<li>If a scheme is not specified, the host is ignored.</li>
@@ -842,17 +851,17 @@
either if its URI matches a URI in the filter or if it has a {@code content:}
or {@code file:} URI and the filter does not specify a URI. In other words,
a component is presumed to support {@code content:} and {@code file:} data if
-its filter lists <em>only</em> a MIME type.</p></li>
+its filter lists <em>only</em> a MIME type.</li>
</ol>
<p>
This last rule, rule (d), reflects the expectation
that components are able to get local data from a file or content provider.
-Therefore, their filters can list just a data type and do not need to explicitly
+Therefore, their filters can list just a data type and don't need to explicitly
name the {@code content:} and {@code file:} schemes.
-This is a typical case. A <a
-href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> element
-like the following, for example, tells Android that the component can get image data from a content
+The following example shows a typical case in which a <a
+href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> element
+ tells Android that the component can get image data from a content
provider and display it:
</p>
@@ -863,14 +872,15 @@
</intent-filter></pre>
<p>
-Because most available data is dispensed by content providers, filters that
-specify a data type but not a URI are perhaps the most common.
+Filters that
+specify a data type but not a URI are perhaps the most common because most available
+ data is dispensed by content providers.
</p>
<p>
-Another common configuration is filters with a scheme and a data type. For
+Another common configuration is a filter with a scheme and a data type. For
example, a <a
-href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>
+href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>
element like the following tells Android that
the component can retrieve video data from the network in order to perform the action:
</p>
@@ -894,7 +904,7 @@
<p>Your application can use intent matching in a similar way.
The {@link android.content.pm.PackageManager} has a set of {@code query...()}
-methods that return all components that can accept a particular intent, and
+methods that return all components that can accept a particular intent and
a similar series of {@code resolve...()} methods that determine the best
component to respond to an intent. For example,
{@link android.content.pm.PackageManager#queryIntentActivities
@@ -907,7 +917,3 @@
{@link android.content.pm.PackageManager#queryBroadcastReceivers
queryBroadcastReceivers()}, for broadcast receivers.
</p>
-
-
-
-
diff --git a/docs/html/guide/components/services.jd b/docs/html/guide/components/services.jd
index e646a17..a7ed718 100644
--- a/docs/html/guide/components/services.jd
+++ b/docs/html/guide/components/services.jd
@@ -5,11 +5,11 @@
<ol id="qv">
<h2>In this document</h2>
<ol>
-<li><a href="#Basics">The Basics</a></li>
+<li><a href="#Basics">The basics</a></li>
<ol>
<li><a href="#Declaring">Declaring a service in the manifest</a></li>
</ol>
-<li><a href="#CreatingAService">Creating a Started Service</a>
+<li><a href="#CreatingAService">Creating a started service</a>
<ol>
<li><a href="#ExtendingIntentService">Extending the IntentService class</a></li>
<li><a href="#ExtendingService">Extending the Service class</a></li>
@@ -17,10 +17,10 @@
<li><a href="#Stopping">Stopping a service</a></li>
</ol>
</li>
-<li><a href="#CreatingBoundService">Creating a Bound Service</a></li>
-<li><a href="#Notifications">Sending Notifications to the User</a></li>
-<li><a href="#Foreground">Running a Service in the Foreground</a></li>
-<li><a href="#Lifecycle">Managing the Lifecycle of a Service</a>
+<li><a href="#CreatingBoundService">Creating a bound service</a></li>
+<li><a href="#Notifications">Sending notifications to the user</a></li>
+<li><a href="#Foreground">Running a service in the foreground</a></li>
+<li><a href="#Lifecycle">Managing the lifecycle of a service</a>
<ol>
<li><a href="#LifecycleCallbacks">Implementing the lifecycle callbacks</a></li>
</ol>
@@ -48,70 +48,80 @@
</div>
-
<p>A {@link android.app.Service} is an application component that can perform
-long-running operations in the background and does not provide a user interface. Another
-application component can start a service and it will continue to run in the background even if the
+long-running operations in the background, and it does not provide a user interface. Another
+application component can start a service, and it continues to run in the background even if the
user switches to another application. Additionally, a component can bind to a service to
-interact with it and even perform interprocess communication (IPC). For example, a service might
+interact with it and even perform interprocess communication (IPC). For example, a service can
handle network transactions, play music, perform file I/O, or interact with a content provider, all
from the background.</p>
-<p>A service can essentially take two forms:</p>
+<p>These are the three different types of services:</p>
<dl>
+ <dt>Scheduled</dt>
+ <dd>A service is <em>scheduled</em> when an API such as the {@link android.app.job.JobScheduler},
+ introduced in Android 5.0 (API level 21), launches the service. You can use the
+ {@link android.app.job.JobScheduler} by registering jobs and specifying their requirements for
+ network and timing. The system then gracefully schedules the jobs for execution at the
+ appropriate times. The {@link android.app.job.JobScheduler} provides many methods to define
+ service-execution conditions.
+ <p class="note"><strong>Note:</strong> If your app targets Android 5.0 (API level 21), Google
+ recommends that you use the {@link android.app.job.JobScheduler} to execute background
+ services. For more information about using this class, see the
+ {@link android.app.job.JobScheduler} reference documentation.</p></dd>
<dt>Started</dt>
- <dd>A service is "started" when an application component (such as an activity) starts it by
-calling {@link android.content.Context#startService startService()}. Once started, a service
-can run in the background indefinitely, even if the component that started it is destroyed. Usually,
-a started service performs a single operation and does not return a result to the caller.
-For example, it might download or upload a file over the network. When the operation is done, the
-service should stop itself.</dd>
+ <dd>A service is <em>started</em> when an application component (such as an activity)
+ calls {@link android.content.Context#startService startService()}. After it's started, a
+ service can run in the background indefinitely, even if the component that started it is
+ destroyed. Usually, a started service performs a single operation and does not return a result to
+ the caller. For example, it can download or upload a file over the network. When the operation is
+ complete, the service should stop itself.</dd>
<dt>Bound</dt>
- <dd>A service is "bound" when an application component binds to it by calling {@link
-android.content.Context#bindService bindService()}. A bound service offers a client-server
-interface that allows components to interact with the service, send requests, get results, and even
-do so across processes with interprocess communication (IPC). A bound service runs only as long as
-another application component is bound to it. Multiple components can bind to the service at once,
-but when all of them unbind, the service is destroyed.</dd>
+ <dd>A service is <em>bound</em> when an application component binds to it by calling {@link
+ android.content.Context#bindService bindService()}. A bound service offers a client-server
+ interface that allows components to interact with the service, send requests, receive results,
+ and even do so across processes with interprocess communication (IPC). A bound service runs only
+ as long as another application component is bound to it. Multiple components can bind to the
+ service at once, but when all of them unbind, the service is destroyed.</dd>
</dl>
-<p>Although this documentation generally discusses these two types of services separately, your
-service can work both ways—it can be started (to run indefinitely) and also allow binding.
-It's simply a matter of whether you implement a couple callback methods: {@link
+<p>Although this documentation generally discusses started and bound services separately,
+your service can work both ways—it can be started (to run indefinitely) and also allow
+binding. It's simply a matter of whether you implement a couple of callback methods: {@link
android.app.Service#onStartCommand onStartCommand()} to allow components to start it and {@link
android.app.Service#onBind onBind()} to allow binding.</p>
<p>Regardless of whether your application is started, bound, or both, any application component
-can use the service (even from a separate application), in the same way that any component can use
+can use the service (even from a separate application) in the same way that any component can use
an activity—by starting it with an {@link android.content.Intent}. However, you can declare
-the service as private, in the manifest file, and block access from other applications. This is
-discussed more in the section about <a href="#Declaring">Declaring the service in the
+the service as <em>private</em> in the manifest file and block access from other applications.
+This is discussed more in the section about <a href="#Declaring">Declaring the service in the
manifest</a>.</p>
<p class="caution"><strong>Caution:</strong> A service runs in the
-main thread of its hosting process—the service does <strong>not</strong> create its own thread
-and does <strong>not</strong> run in a separate process (unless you specify otherwise). This means
-that, if your service is going to do any CPU intensive work or blocking operations (such as MP3
-playback or networking), you should create a new thread within the service to do that work. By using
-a separate thread, you will reduce the risk of Application Not Responding (ANR) errors and the
-application's main thread can remain dedicated to user interaction with your activities.</p>
+main thread of its hosting process; the service does <strong>not</strong> create its own
+thread and does <strong>not</strong> run in a separate process unless you specify otherwise. If
+your service is going to perform any CPU-intensive work or blocking operations, such as MP3
+playback or networking, you should create a new thread within the service to complete that work.
+By using a separate thread, you can reduce the risk of Application Not Responding (ANR) errors,
+and the application's main thread can remain dedicated to user interaction with your
+activities.</p>
-
-<h2 id="Basics">The Basics</h2>
+<h2 id="Basics">The basics</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<h3>Should you use a service or a thread?</h3>
- <p>A service is simply a component that can run in the background even when the user is not
-interacting with your application. Thus, you should create a service only if that is what you
+ <p>A service is simply a component that can run in the background, even when the user is not
+interacting with your application, so you should create a service only if that is what you
need.</p>
- <p>If you need to perform work outside your main thread, but only while the user is interacting
-with your application, then you should probably instead create a new thread and not a service. For
-example, if you want to play some music, but only while your activity is running, you might create
+ <p>If you must perform work outside of your main thread, but only while the user is interacting
+with your application, you should instead create a new thread. For example, if you want to
+play some music, but only while your activity is running, you might create
a thread in {@link android.app.Activity#onCreate onCreate()}, start running it in {@link
-android.app.Activity#onStart onStart()}, then stop it in {@link android.app.Activity#onStop
-onStop()}. Also consider using {@link android.os.AsyncTask} or {@link android.os.HandlerThread},
+android.app.Activity#onStart onStart()}, and stop it in {@link android.app.Activity#onStop
+onStop()}. Also consider using {@link android.os.AsyncTask} or {@link android.os.HandlerThread}
instead of the traditional {@link java.lang.Thread} class. See the <a
href="{@docRoot}guide/components/processes-and-threads.html#Threads">Processes and
Threading</a> document for more information about threads.</p>
@@ -121,78 +131,81 @@
</div>
</div>
-<p>To create a service, you must create a subclass of {@link android.app.Service} (or one
-of its existing subclasses). In your implementation, you need to override some callback methods that
-handle key aspects of the service lifecycle and provide a mechanism for components to bind to
-the service, if appropriate. The most important callback methods you should override are:</p>
+<p>To create a service, you must create a subclass of {@link android.app.Service} or use one
+of its existing subclasses. In your implementation, you must override some callback methods that
+handle key aspects of the service lifecycle and provide a mechanism that allows the components to
+bind to the service, if appropriate. These are the most important callback methods that you should
+override:</p>
<dl>
<dt>{@link android.app.Service#onStartCommand onStartCommand()}</dt>
- <dd>The system calls this method when another component, such as an activity,
-requests that the service be started, by calling {@link android.content.Context#startService
-startService()}. Once this method executes, the service is started and can run in the
+ <dd>The system invokes this method by calling {@link android.content.Context#startService
+startService()} when another component (such as an activity) requests that the service be started.
+When this method executes, the service is started and can run in the
background indefinitely. If you implement this, it is your responsibility to stop the service when
-its work is done, by calling {@link android.app.Service#stopSelf stopSelf()} or {@link
-android.content.Context#stopService stopService()}. (If you only want to provide binding, you don't
-need to implement this method.)</dd>
+its work is complete by calling {@link android.app.Service#stopSelf stopSelf()} or {@link
+android.content.Context#stopService stopService()}. If you only want to provide binding, you don't
+need to implement this method.</dd>
<dt>{@link android.app.Service#onBind onBind()}</dt>
- <dd>The system calls this method when another component wants to bind with the
-service (such as to perform RPC), by calling {@link android.content.Context#bindService
-bindService()}. In your implementation of this method, you must provide an interface that clients
-use to communicate with the service, by returning an {@link android.os.IBinder}. You must always
-implement this method, but if you don't want to allow binding, then you should return null.</dd>
+ <dd>The system invokes this method by calling {@link android.content.Context#bindService
+bindService()} when another component wants to bind with the service (such as to perform RPC).
+In your implementation of this method, you must provide an interface that clients
+use to communicate with the service by returning an {@link android.os.IBinder}. You must always
+implement this method; however, if you don't want to allow binding, you should return
+null.</dd>
<dt>{@link android.app.Service#onCreate()}</dt>
- <dd>The system calls this method when the service is first created, to perform one-time setup
-procedures (before it calls either {@link android.app.Service#onStartCommand onStartCommand()} or
+ <dd>The system invokes this method to perform one-time setup procedures when the service is
+initially created (before it calls either
+{@link android.app.Service#onStartCommand onStartCommand()} or
{@link android.app.Service#onBind onBind()}). If the service is already running, this method is not
called.</dd>
<dt>{@link android.app.Service#onDestroy()}</dt>
- <dd>The system calls this method when the service is no longer used and is being destroyed.
+ <dd>The system invokes this method when the service is no longer used and is being destroyed.
Your service should implement this to clean up any resources such as threads, registered
-listeners, receivers, etc. This is the last call the service receives.</dd>
+listeners, or receivers. This is the last call that the service receives.</dd>
</dl>
<p>If a component starts the service by calling {@link
android.content.Context#startService startService()} (which results in a call to {@link
-android.app.Service#onStartCommand onStartCommand()}), then the service
-remains running until it stops itself with {@link android.app.Service#stopSelf()} or another
+android.app.Service#onStartCommand onStartCommand()}), the service
+continues to run until it stops itself with {@link android.app.Service#stopSelf()} or another
component stops it by calling {@link android.content.Context#stopService stopService()}.</p>
<p>If a component calls
-{@link android.content.Context#bindService bindService()} to create the service (and {@link
-android.app.Service#onStartCommand onStartCommand()} is <em>not</em> called), then the service runs
-only as long as the component is bound to it. Once the service is unbound from all clients, the
-system destroys it.</p>
+{@link android.content.Context#bindService bindService()} to create the service and {@link
+android.app.Service#onStartCommand onStartCommand()} is <em>not</em> called, the service runs
+only as long as the component is bound to it. After the service is unbound from all of its clients,
+the system destroys it.</p>
-<p>The Android system will force-stop a service only when memory is low and it must recover system
+<p>The Android system force-stops a service only when memory is low and it must recover system
resources for the activity that has user focus. If the service is bound to an activity that has user
-focus, then it's less likely to be killed, and if the service is declared to <a
-href="#Foreground">run in the foreground</a> (discussed later), then it will almost never be killed.
-Otherwise, if the service was started and is long-running, then the system will lower its position
-in the list of background tasks over time and the service will become highly susceptible to
-killing—if your service is started, then you must design it to gracefully handle restarts
+focus, it's less likely to be killed; if the service is declared to <a
+href="#Foreground">run in the foreground</a>, it's rarely killed.
+If the service is started and is long-running, the system lowers its position
+in the list of background tasks over time, and the service becomes highly susceptible to
+killing—if your service is started, you must design it to gracefully handle restarts
by the system. If the system kills your service, it restarts it as soon as resources become
-available again (though this also depends on the value you return from {@link
-android.app.Service#onStartCommand onStartCommand()}, as discussed later). For more information
+available, but this also depends on the value that you return from {@link
+android.app.Service#onStartCommand onStartCommand()}. For more information
about when the system might destroy a service, see the <a
href="{@docRoot}guide/components/processes-and-threads.html">Processes and Threading</a>
document.</p>
-<p>In the following sections, you'll see how you can create each type of service and how to use
-it from other application components.</p>
-
-
+<p>In the following sections, you'll see how you can create the
+{@link android.content.Context#startService startService()} and
+{@link android.content.Context#bindService bindService()} service methods, as well as how to use
+them from other application components.</p>
<h3 id="Declaring">Declaring a service in the manifest</h3>
-<p>Like activities (and other components), you must declare all services in your application's
-manifest file.</p>
+<p>You must declare all services in your application's
+manifest file, just as you do for activities and other components.</p>
<p>To declare your service, add a <a
-href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element
+href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element
as a child of the <a
-href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>
-element. For example:</p>
+href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>
+element. Here is an example:</p>
<pre>
<manifest ... >
@@ -205,48 +218,44 @@
</pre>
<p>See the <a
-href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element
+href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element
reference for more information about declaring your service in the manifest.</p>
-<p>There are other attributes you can include in the <a
-href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element to
-define properties such as permissions required to start the service and the process in
+<p>There are other attributes that you can include in the <a
+href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element to
+define properties such as the permissions that are required to start the service and the process in
which the service should run. The <a
href="{@docRoot}guide/topics/manifest/service-element.html#nm">{@code android:name}</a>
-attribute is the only required attribute—it specifies the class name of the service. Once
-you publish your application, you should not change this name, because if you do, you risk breaking
+attribute is the only required attribute—it specifies the class name of the service. After
+you publish your application, leave this name unchanged to avoid the risk of breaking
code due to dependence on explicit intents to start or bind the service (read the blog post, <a
href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">Things
That Cannot Change</a>).
-<p>To ensure your app is secure, <strong>always use an explicit intent when starting or binding
-your {@link android.app.Service}</strong> and do not declare intent filters for the service. If
-it's critical that you allow for some amount of ambiguity as to which service starts, you can
-supply intent filters for your services and exclude the component name from the {@link
-android.content.Intent}, but you then must set the package for the intent with {@link
-android.content.Intent#setPackage setPackage()}, which provides sufficient disambiguation for the
-target service.</p>
+<p class="caution"><strong>Caution</strong>: To ensure that your app is secure, always use an
+explicit intent when starting a {@link android.app.Service} and do not declare intent filters for
+your services. Using an implicit intent to start a service is a security hazard because you cannot
+be certain of the service that will respond to the intent, and the user cannot see which service
+starts. Beginning with Android 5.0 (API level 21), the system throws an exception if you call
+{@link android.content.Context#bindService bindService()} with an implicit intent.</p>
-<p>Additionally, you can ensure that your service is available to only your app by
+<p>You can ensure that your service is available to only your app by
including the <a
href="{@docRoot}guide/topics/manifest/service-element.html#exported">{@code android:exported}</a>
-attribute and setting it to {@code "false"}. This effectively stops other apps from starting your
+attribute and setting it to {@code false}. This effectively stops other apps from starting your
service, even when using an explicit intent.</p>
-
-
-
-<h2 id="CreatingStartedService">Creating a Started Service</h2>
+<h2 id="CreatingStartedService">Creating a started service</h2>
<p>A started service is one that another component starts by calling {@link
-android.content.Context#startService startService()}, resulting in a call to the service's
+android.content.Context#startService startService()}, which results in a call to the service's
{@link android.app.Service#onStartCommand onStartCommand()} method.</p>
<p>When a service is started, it has a lifecycle that's independent of the
-component that started it and the service can run in the background indefinitely, even if
+component that started it. The service can run in the background indefinitely, even if
the component that started it is destroyed. As such, the service should stop itself when its job
-is done by calling {@link android.app.Service#stopSelf stopSelf()}, or another component can stop it
-by calling {@link android.content.Context#stopService stopService()}.</p>
+is complete by calling {@link android.app.Service#stopSelf stopSelf()}, or another component can
+stop it by calling {@link android.content.Context#stopService stopService()}.</p>
<p>An application component such as an activity can start the service by calling {@link
android.content.Context#startService startService()} and passing an {@link android.content.Intent}
@@ -254,65 +263,65 @@
this {@link android.content.Intent} in the {@link android.app.Service#onStartCommand
onStartCommand()} method.</p>
-<p>For instance, suppose an activity needs to save some data to an online database. The activity can
-start a companion service and deliver it the data to save by passing an intent to {@link
+<p>For instance, suppose an activity needs to save some data to an online database. The activity
+can start a companion service and deliver it the data to save by passing an intent to {@link
android.content.Context#startService startService()}. The service receives the intent in {@link
-android.app.Service#onStartCommand onStartCommand()}, connects to the Internet and performs the
-database transaction. When the transaction is done, the service stops itself and it is
+android.app.Service#onStartCommand onStartCommand()}, connects to the Internet, and performs the
+database transaction. When the transaction is complete, the service stops itself and is
destroyed.</p>
<p class="caution"><strong>Caution:</strong> A service runs in the same process as the application
-in which it is declared and in the main thread of that application, by default. So, if your service
+in which it is declared and in the main thread of that application by default. If your service
performs intensive or blocking operations while the user interacts with an activity from the same
-application, the service will slow down activity performance. To avoid impacting application
-performance, you should start a new thread inside the service.</p>
+application, the service slows down activity performance. To avoid impacting application
+performance, start a new thread inside the service.</p>
<p>Traditionally, there are two classes you can extend to create a started service:</p>
+
<dl>
<dt>{@link android.app.Service}</dt>
- <dd>This is the base class for all services. When you extend this class, it's important that
-you create a new thread in which to do all the service's work, because the service uses your
-application's main thread, by default, which could slow the performance of any activity your
+ <dd>This is the base class for all services. When you extend this class, it's important to
+create a new thread in which the service can complete all of its work; the service uses your
+application's main thread by default, which can slow the performance of any activity that your
application is running.</dd>
<dt>{@link android.app.IntentService}</dt>
- <dd>This is a subclass of {@link android.app.Service} that uses a worker thread to handle all
-start requests, one at a time. This is the best option if you don't require that your service
-handle multiple requests simultaneously. All you need to do is implement {@link
+ <dd>This is a subclass of {@link android.app.Service} that uses a worker thread to handle all of
+the start requests, one at a time. This is the best option if you don't require that your service
+handle multiple requests simultaneously. Implement {@link
android.app.IntentService#onHandleIntent onHandleIntent()}, which receives the intent for each
-start request so you can do the background work.</dd>
+start request so that you can complete the background work.</dd>
</dl>
<p>The following sections describe how you can implement your service using either one for these
classes.</p>
-
<h3 id="ExtendingIntentService">Extending the IntentService class</h3>
-<p>Because most started services don't need to handle multiple requests simultaneously
-(which can actually be a dangerous multi-threading scenario), it's probably best if you
+<p>Because most of the started services don't need to handle multiple requests simultaneously
+(which can actually be a dangerous multi-threading scenario), it's best that you
implement your service using the {@link android.app.IntentService} class.</p>
-<p>The {@link android.app.IntentService} does the following:</p>
+<p>The {@link android.app.IntentService} class does the following:</p>
<ul>
- <li>Creates a default worker thread that executes all intents delivered to {@link
-android.app.Service#onStartCommand onStartCommand()} separate from your application's main
+ <li>It creates a default worker thread that executes all of the intents that are delivered to
+{@link android.app.Service#onStartCommand onStartCommand()}, separate from your application's main
thread.</li>
<li>Creates a work queue that passes one intent at a time to your {@link
android.app.IntentService#onHandleIntent onHandleIntent()} implementation, so you never have to
worry about multi-threading.</li>
- <li>Stops the service after all start requests have been handled, so you never have to call
+ <li>Stops the service after all of the start requests are handled, so you never have to call
{@link android.app.Service#stopSelf}.</li>
- <li>Provides default implementation of {@link android.app.IntentService#onBind onBind()} that
-returns null.</li>
+ <li>Provides a default implementation of {@link android.app.IntentService#onBind onBind()}
+ that returns null.</li>
<li>Provides a default implementation of {@link android.app.IntentService#onStartCommand
onStartCommand()} that sends the intent to the work queue and then to your {@link
android.app.IntentService#onHandleIntent onHandleIntent()} implementation.</li>
</ul>
-<p>All this adds up to the fact that all you need to do is implement {@link
-android.app.IntentService#onHandleIntent onHandleIntent()} to do the work provided by the
-client. (Though, you also need to provide a small constructor for the service.)</p>
+<p>To complete the work that is provided by the client, implement {@link
+android.app.IntentService#onHandleIntent onHandleIntent()}.
+However, you also need to provide a small constructor for the service.</p>
<p>Here's an example implementation of {@link android.app.IntentService}:</p>
@@ -352,12 +361,12 @@
<p>If you decide to also override other callback methods, such as {@link
android.app.IntentService#onCreate onCreate()}, {@link
android.app.IntentService#onStartCommand onStartCommand()}, or {@link
-android.app.IntentService#onDestroy onDestroy()}, be sure to call the super implementation, so
+android.app.IntentService#onDestroy onDestroy()}, be sure to call the super implementation so
that the {@link android.app.IntentService} can properly handle the life of the worker thread.</p>
<p>For example, {@link android.app.IntentService#onStartCommand onStartCommand()} must return
-the default implementation (which is how the intent gets delivered to {@link
-android.app.IntentService#onHandleIntent onHandleIntent()}):</p>
+the default implementation, which is how the intent is delivered to {@link
+android.app.IntentService#onHandleIntent onHandleIntent()}:</p>
<pre>
@Override
@@ -369,22 +378,21 @@
<p>Besides {@link android.app.IntentService#onHandleIntent onHandleIntent()}, the only method
from which you don't need to call the super class is {@link android.app.IntentService#onBind
-onBind()} (but you only need to implement that if your service allows binding).</p>
+onBind()}. You need to implement this only if your service allows binding.</p>
<p>In the next section, you'll see how the same kind of service is implemented when extending
-the base {@link android.app.Service} class, which is a lot more code, but which might be
+the base {@link android.app.Service} class, which uses more code, but might be
appropriate if you need to handle simultaneous start requests.</p>
-
<h3 id="ExtendingService">Extending the Service class</h3>
-<p>As you saw in the previous section, using {@link android.app.IntentService} makes your
+<p>Using {@link android.app.IntentService} makes your
implementation of a started service very simple. If, however, you require your service to
-perform multi-threading (instead of processing start requests through a work queue), then you
+perform multi-threading (instead of processing start requests through a work queue), you
can extend the {@link android.app.Service} class to handle each intent.</p>
-<p>For comparison, the following example code is an implementation of the {@link
-android.app.Service} class that performs the exact same work as the example above using {@link
+<p>For comparison, the following example code shows an implementation of the {@link
+android.app.Service} class that performs the same work as the previous example using {@link
android.app.IntentService}. That is, for each start request, it uses a worker thread to perform the
job and processes only one request at a time.</p>
@@ -460,20 +468,20 @@
<p>However, because you handle each call to {@link android.app.Service#onStartCommand
onStartCommand()} yourself, you can perform multiple requests simultaneously. That's not what
-this example does, but if that's what you want, then you can create a new thread for each
-request and run them right away (instead of waiting for the previous request to finish).</p>
+this example does, but if that's what you want, you can create a new thread for each
+request and run them right away instead of waiting for the previous request to finish.</p>
<p>Notice that the {@link android.app.Service#onStartCommand onStartCommand()} method must return an
integer. The integer is a value that describes how the system should continue the service in the
-event that the system kills it (as discussed above, the default implementation for {@link
-android.app.IntentService} handles this for you, though you are able to modify it). The return value
+event that the system kills it. The default implementation for {@link
+android.app.IntentService} handles this for you, but you are able to modify it. The return value
from {@link android.app.Service#onStartCommand onStartCommand()} must be one of the following
constants:</p>
<dl>
<dt>{@link android.app.Service#START_NOT_STICKY}</dt>
<dd>If the system kills the service after {@link android.app.Service#onStartCommand
-onStartCommand()} returns, <em>do not</em> recreate the service, unless there are pending
+onStartCommand()} returns, <em>do not</em> recreate the service unless there are pending
intents to deliver. This is the safest option to avoid running your service when not necessary
and when your application can simply restart any unfinished jobs.</dd>
<dt>{@link android.app.Service#START_STICKY}</dt>
@@ -481,9 +489,9 @@
onStartCommand()} returns, recreate the service and call {@link
android.app.Service#onStartCommand onStartCommand()}, but <em>do not</em> redeliver the last intent.
Instead, the system calls {@link android.app.Service#onStartCommand onStartCommand()} with a
-null intent, unless there were pending intents to start the service, in which case,
+null intent unless there are pending intents to start the service. In that case,
those intents are delivered. This is suitable for media players (or similar services) that are not
-executing commands, but running indefinitely and waiting for a job.</dd>
+executing commands but are running indefinitely and waiting for a job.</dd>
<dt>{@link android.app.Service#START_REDELIVER_INTENT}</dt>
<dd>If the system kills the service after {@link android.app.Service#onStartCommand
onStartCommand()} returns, recreate the service and call {@link
@@ -494,35 +502,35 @@
<p>For more details about these return values, see the linked reference documentation for each
constant.</p>
-
-
-<h3 id="StartingAService">Starting a Service</h3>
+<h3 id="StartingAService">Starting a service</h3>
<p>You can start a service from an activity or other application component by passing an
{@link android.content.Intent} (specifying the service to start) to {@link
android.content.Context#startService startService()}. The Android system calls the service's {@link
android.app.Service#onStartCommand onStartCommand()} method and passes it the {@link
-android.content.Intent}. (You should never call {@link android.app.Service#onStartCommand
-onStartCommand()} directly.)</p>
+android.content.Intent}.
+
+<p class="note"><strong>Note</strong>: Never call
+{@link android.app.Service#onStartCommand onStartCommand()} directly.</p>
<p>For example, an activity can start the example service in the previous section ({@code
HelloService}) using an explicit intent with {@link android.content.Context#startService
-startService()}:</p>
+startService()}, as shown here:</p>
<pre>
Intent intent = new Intent(this, HelloService.class);
startService(intent);
</pre>
-<p>The {@link android.content.Context#startService startService()} method returns immediately and
+<p>The {@link android.content.Context#startService startService()} method returns immediately, and
the Android system calls the service's {@link android.app.Service#onStartCommand
onStartCommand()} method. If the service is not already running, the system first calls {@link
-android.app.Service#onCreate onCreate()}, then calls {@link android.app.Service#onStartCommand
-onStartCommand()}.</p>
+android.app.Service#onCreate onCreate()}, and then it calls
+{@link android.app.Service#onStartCommand onStartCommand()}.</p>
-<p>If the service does not also provide binding, the intent delivered with {@link
+<p>If the service does not also provide binding, the intent that is delivered with {@link
android.content.Context#startService startService()} is the only mode of communication between the
-application component and the service. However, if you want the service to send a result back, then
+application component and the service. However, if you want the service to send a result back,
the client that starts the service can create a {@link android.app.PendingIntent} for a broadcast
(with {@link android.app.PendingIntent#getBroadcast getBroadcast()}) and deliver it to the service
in the {@link android.content.Intent} that starts the service. The service can then use the
@@ -533,109 +541,102 @@
the service (with {@link android.app.Service#stopSelf stopSelf()} or {@link
android.content.Context#stopService stopService()}) is required to stop it.</p>
-
<h3 id="Stopping">Stopping a service</h3>
<p>A started service must manage its own lifecycle. That is, the system does not stop or
destroy the service unless it must recover system memory and the service
-continues to run after {@link android.app.Service#onStartCommand onStartCommand()} returns. So,
-the service must stop itself by calling {@link android.app.Service#stopSelf stopSelf()} or another
+continues to run after {@link android.app.Service#onStartCommand onStartCommand()} returns. The
+service must stop itself by calling {@link android.app.Service#stopSelf stopSelf()}, or another
component can stop it by calling {@link android.content.Context#stopService stopService()}.</p>
<p>Once requested to stop with {@link android.app.Service#stopSelf stopSelf()} or {@link
android.content.Context#stopService stopService()}, the system destroys the service as soon as
possible.</p>
-<p>However, if your service handles multiple requests to {@link
-android.app.Service#onStartCommand onStartCommand()} concurrently, then you shouldn't stop the
-service when you're done processing a start request, because you might have since received a new
+<p>If your service handles multiple requests to {@link
+android.app.Service#onStartCommand onStartCommand()} concurrently, you shouldn't stop the
+service when you're done processing a start request, as you might have received a new
start request (stopping at the end of the first request would terminate the second one). To avoid
this problem, you can use {@link android.app.Service#stopSelf(int)} to ensure that your request to
stop the service is always based on the most recent start request. That is, when you call {@link
android.app.Service#stopSelf(int)}, you pass the ID of the start request (the <code>startId</code>
delivered to {@link android.app.Service#onStartCommand onStartCommand()}) to which your stop request
-corresponds. Then if the service received a new start request before you were able to call {@link
-android.app.Service#stopSelf(int)}, then the ID will not match and the service will not stop.</p>
+corresponds. Then, if the service receives a new start request before you are able to call {@link
+android.app.Service#stopSelf(int)}, the ID does not match and the service does not stop.</p>
-<p class="caution"><strong>Caution:</strong> It's important that your application stops its services
-when it's done working, to avoid wasting system resources and consuming battery power. If necessary,
-other components can stop the service by calling {@link
+<p class="caution"><strong>Caution:</strong> To avoid wasting system resources and consuming
+battery power, ensure that your application stops its services when it's done working.
+If necessary, other components can stop the service by calling {@link
android.content.Context#stopService stopService()}. Even if you enable binding for the service,
-you must always stop the service yourself if it ever received a call to {@link
+you must always stop the service yourself if it ever receives a call to {@link
android.app.Service#onStartCommand onStartCommand()}.</p>
<p>For more information about the lifecycle of a service, see the section below about <a
href="#Lifecycle">Managing the Lifecycle of a Service</a>.</p>
-
-
-<h2 id="CreatingBoundService">Creating a Bound Service</h2>
+<h2 id="CreatingBoundService">Creating a bound service</h2>
<p>A bound service is one that allows application components to bind to it by calling {@link
-android.content.Context#bindService bindService()} in order to create a long-standing connection
-(and generally does not allow components to <em>start</em> it by calling {@link
-android.content.Context#startService startService()}).</p>
+android.content.Context#bindService bindService()} to create a long-standing connection.
+It generally doesn't allow components to <em>start</em> it by calling {@link
+android.content.Context#startService startService()}.</p>
-<p>You should create a bound service when you want to interact with the service from activities
+<p>Create a bound service when you want to interact with the service from activities
and other components in your application or to expose some of your application's functionality to
-other applications, through interprocess communication (IPC).</p>
+other applications through interprocess communication (IPC).</p>
-<p>To create a bound service, you must implement the {@link
+<p>To create a bound service, implement the {@link
android.app.Service#onBind onBind()} callback method to return an {@link android.os.IBinder} that
defines the interface for communication with the service. Other application components can then call
{@link android.content.Context#bindService bindService()} to retrieve the interface and
begin calling methods on the service. The service lives only to serve the application component that
-is bound to it, so when there are no components bound to the service, the system destroys it
-(you do <em>not</em> need to stop a bound service in the way you must when the service is started
-through {@link android.app.Service#onStartCommand onStartCommand()}).</p>
+is bound to it, so when there are no components bound to the service, the system destroys it.
+You do <em>not</em> need to stop a bound service in the same way that you must when the service is
+started through {@link android.app.Service#onStartCommand onStartCommand()}.</p>
-<p>To create a bound service, the first thing you must do is define the interface that specifies
-how a client can communicate with the service. This interface between the service
+<p>To create a bound service, you must define the interface that specifies how a client can
+communicate with the service. This interface between the service
and a client must be an implementation of {@link android.os.IBinder} and is what your service must
return from the {@link android.app.Service#onBind
-onBind()} callback method. Once the client receives the {@link android.os.IBinder}, it can begin
+onBind()} callback method. After the client receives the {@link android.os.IBinder}, it can begin
interacting with the service through that interface.</p>
-<p>Multiple clients can bind to the service at once. When a client is done interacting with the
-service, it calls {@link android.content.Context#unbindService unbindService()} to unbind. Once
-there are no clients bound to the service, the system destroys the service.</p>
+<p>Multiple clients can bind to the service simultaneously. When a client is done interacting with
+the service, it calls {@link android.content.Context#unbindService unbindService()} to unbind.
+When there are no clients bound to the service, the system destroys the service.</p>
-<p>There are multiple ways to implement a bound service and the implementation is more
-complicated than a started service, so the bound service discussion appears in a separate
-document about <a
+<p>There are multiple ways to implement a bound service, and the implementation is more
+complicated than a started service. For these reasons, the bound service discussion appears in a
+separate document about <a
href="{@docRoot}guide/components/bound-services.html">Bound Services</a>.</p>
+<h2 id="Notifications">Sending notifications to the user</h2>
-
-<h2 id="Notifications">Sending Notifications to the User</h2>
-
-<p>Once running, a service can notify the user of events using <a
+<p>When a service is running, it can notify the user of events using <a
href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Toast Notifications</a> or <a
href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>.</p>
-<p>A toast notification is a message that appears on the surface of the current window for a
-moment then disappears, while a status bar notification provides an icon in the status bar with a
+<p>A toast notification is a message that appears on the surface of the current window for only a
+moment before disappearing. A status bar notification provides an icon in the status bar with a
message, which the user can select in order to take an action (such as start an activity).</p>
-<p>Usually, a status bar notification is the best technique when some background work has completed
-(such as a file completed
-downloading) and the user can now act on it. When the user selects the notification from the
-expanded view, the notification can start an activity (such as to view the downloaded file).</p>
+<p>Usually, a status bar notification is the best technique to use when background work such as
+a file download has completed, and the user can now act on it. When the user
+selects the notification from the expanded view, the notification can start an activity
+(such as to display the downloaded file).</p>
<p>See the <a
href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Toast Notifications</a> or <a
href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
developer guides for more information.</p>
+<h2 id="Foreground">Running a service in the foreground</h2>
-
-<h2 id="Foreground">Running a Service in the Foreground</h2>
-
-<p>A foreground service is a service that's considered to be something the
-user is actively aware of and thus not a candidate for the system to kill when low on memory. A
+<p>A foreground service is a service that the
+user is actively aware of and is not a candidate for the system to kill when low on memory. A
foreground service must provide a notification for the status bar, which is placed under the
-"Ongoing" heading, which means that the notification cannot be dismissed unless the service is
-either stopped or removed from the foreground.</p>
+<em>Ongoing</em> heading. This means that the notification cannot be dismissed unless the service
+is either stopped or removed from the foreground.</p>
<p>For example, a music player that plays music from a service should be set to run in the
foreground, because the user is explicitly aware
@@ -643,9 +644,9 @@
the user to launch an activity to interact with the music player.</p>
<p>To request that your service run in the foreground, call {@link
-android.app.Service#startForeground startForeground()}. This method takes two parameters: an integer
-that uniquely identifies the notification and the {@link
-android.app.Notification} for the status bar. For example:</p>
+android.app.Service#startForeground startForeground()}. This method takes two parameters: an
+integer that uniquely identifies the notification and the {@link
+android.app.Notification} for the status bar. Here is an example:</p>
<pre>
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
@@ -657,30 +658,27 @@
startForeground(ONGOING_NOTIFICATION_ID, notification);
</pre>
-<p class="caution"><strong>Caution:</strong> The integer ID you give to {@link
+<p class="caution"><strong>Caution:</strong> The integer ID that you give to {@link
android.app.Service#startForeground startForeground()} must not be 0.</p>
-
<p>To remove the service from the foreground, call {@link
-android.app.Service#stopForeground stopForeground()}. This method takes a boolean, indicating
+android.app.Service#stopForeground stopForeground()}. This method takes a boolean, which indicates
whether to remove the status bar notification as well. This method does <em>not</em> stop the
-service. However, if you stop the service while it's still running in the foreground, then the
+service. However, if you stop the service while it's still running in the foreground, the
notification is also removed.</p>
<p>For more information about notifications, see <a
href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Creating Status Bar
Notifications</a>.</p>
+<h2 id="Lifecycle">Managing the lifecycle of a service</h2>
+<p>The lifecycle of a service is much simpler than that of an activity. However, it's even more
+important that you pay close attention to how your service is created and destroyed because a
+service can run in the background without the user being aware.</p>
-<h2 id="Lifecycle">Managing the Lifecycle of a Service</h2>
-
-<p>The lifecycle of a service is much simpler than that of an activity. However, it's even more important
-that you pay close attention to how your service is created and destroyed, because a service
-can run in the background without the user being aware.</p>
-
-<p>The service lifecycle—from when it's created to when it's destroyed—can follow two
-different paths:</p>
+<p>The service lifecycle—from when it's created to when it's destroyed—can follow
+either of these two paths:</p>
<ul>
<li>A started service
@@ -689,27 +687,26 @@
stop itself by calling {@link
android.app.Service#stopSelf() stopSelf()}. Another component can also stop the
service by calling {@link android.content.Context#stopService
-stopService()}. When the service is stopped, the system destroys it..</p></li>
+stopService()}. When the service is stopped, the system destroys it.</p></li>
<li>A bound service
<p>The service is created when another component (a client) calls {@link
android.content.Context#bindService bindService()}. The client then communicates with the service
through an {@link android.os.IBinder} interface. The client can close the connection by calling
{@link android.content.Context#unbindService unbindService()}. Multiple clients can bind to
-the same service and when all of them unbind, the system destroys the service. (The service
-does <em>not</em> need to stop itself.)</p></li>
+the same service and when all of them unbind, the system destroys the service. The service
+does <em>not</em> need to stop itself.</p></li>
</ul>
-<p>These two paths are not entirely separate. That is, you can bind to a service that was already
-started with {@link android.content.Context#startService startService()}. For example, a background
-music service could be started by calling {@link android.content.Context#startService
+<p>These two paths are not entirely separate. You can bind to a service that is already
+started with {@link android.content.Context#startService startService()}. For example, you can
+start a background music service by calling {@link android.content.Context#startService
startService()} with an {@link android.content.Intent} that identifies the music to play. Later,
possibly when the user wants to exercise some control over the player or get information about the
current song, an activity can bind to the service by calling {@link
-android.content.Context#bindService bindService()}. In cases like this, {@link
+android.content.Context#bindService bindService()}. In cases such as this, {@link
android.content.Context#stopService stopService()} or {@link android.app.Service#stopSelf
-stopSelf()} does not actually stop the service until all clients unbind. </p>
-
+stopSelf()} doesn't actually stop the service until all of the clients unbind.</p>
<h3 id="LifecycleCallbacks">Implementing the lifecycle callbacks</h3>
@@ -763,20 +760,30 @@
startService()} and the diagram on the right shows the lifecycle when the service is created
with {@link android.content.Context#bindService bindService()}.</p>
-<p>By implementing these methods, you can monitor two nested loops of the service's lifecycle: </p>
+<p>Figure 2 illustrates the typical callback methods for a service. Although the figure separates
+services that are created by {@link android.content.Context#startService startService()} from those
+created by {@link android.content.Context#bindService bindService()}, keep
+in mind that any service, no matter how it's started, can potentially allow clients to bind to it.
+A service that was initially started with {@link android.app.Service#onStartCommand
+onStartCommand()} (by a client calling {@link android.content.Context#startService startService()})
+can still receive a call to {@link android.app.Service#onBind onBind()} (when a client calls
+{@link android.content.Context#bindService bindService()}).</p>
+
+<p>By implementing these methods, you can monitor these two nested loops of the service's
+lifecycle:</p>
<ul>
-<li>The <strong>entire lifetime</strong> of a service happens between the time {@link
-android.app.Service#onCreate onCreate()} is called and the time {@link
+<li>The <strong>entire lifetime</strong> of a service occurs between the time that {@link
+android.app.Service#onCreate onCreate()} is called and the time that {@link
android.app.Service#onDestroy} returns. Like an activity, a service does its initial setup in
{@link android.app.Service#onCreate onCreate()} and releases all remaining resources in {@link
-android.app.Service#onDestroy onDestroy()}. For example, a
-music playback service could create the thread where the music will be played in {@link
-android.app.Service#onCreate onCreate()}, then stop the thread in {@link
+android.app.Service#onDestroy onDestroy()}. For example, a
+music playback service can create the thread where the music is played in {@link
+android.app.Service#onCreate onCreate()}, and then it can stop the thread in {@link
android.app.Service#onDestroy onDestroy()}.
-<p>The {@link android.app.Service#onCreate onCreate()} and {@link android.app.Service#onDestroy
-onDestroy()} methods are called for all services, whether
+<p class="note"><strong>Note</strong>: The {@link android.app.Service#onCreate onCreate()}
+and {@link android.app.Service#onDestroy onDestroy()} methods are called for all services, whether
they're created by {@link android.content.Context#startService startService()} or {@link
android.content.Context#bindService bindService()}.</p></li>
@@ -784,8 +791,8 @@
android.app.Service#onStartCommand onStartCommand()} or {@link android.app.Service#onBind onBind()}.
Each method is handed the {@link
android.content.Intent} that was passed to either {@link android.content.Context#startService
-startService()} or {@link android.content.Context#bindService bindService()}, respectively.
-<p>If the service is started, the active lifetime ends the same time that the entire lifetime
+startService()} or {@link android.content.Context#bindService bindService()}.
+<p>If the service is started, the active lifetime ends at the same time that the entire lifetime
ends (the service is still active even after {@link android.app.Service#onStartCommand
onStartCommand()} returns). If the service is bound, the active lifetime ends when {@link
android.app.Service#onUnbind onUnbind()} returns.</p>
@@ -795,26 +802,16 @@
<p class="note"><strong>Note:</strong> Although a started service is stopped by a call to
either {@link android.app.Service#stopSelf stopSelf()} or {@link
android.content.Context#stopService stopService()}, there is not a respective callback for the
-service (there's no {@code onStop()} callback). So, unless the service is bound to a client,
+service (there's no {@code onStop()} callback). Unless the service is bound to a client,
the system destroys it when the service is stopped—{@link
android.app.Service#onDestroy onDestroy()} is the only callback received.</p>
-<p>Figure 2 illustrates the typical callback methods for a service. Although the figure separates
-services that are created by {@link android.content.Context#startService startService()} from those
-created by {@link android.content.Context#bindService bindService()}, keep
-in mind that any service, no matter how it's started, can potentially allow clients to bind to it.
-So, a service that was initially started with {@link android.app.Service#onStartCommand
-onStartCommand()} (by a client calling {@link android.content.Context#startService startService()})
-can still receive a call to {@link android.app.Service#onBind onBind()} (when a client calls
-{@link android.content.Context#bindService bindService()}).</p>
-
<p>For more information about creating a service that provides binding, see the <a
href="{@docRoot}guide/components/bound-services.html">Bound Services</a> document,
which includes more information about the {@link android.app.Service#onRebind onRebind()}
callback method in the section about <a
-href="{@docRoot}guide/components/bound-services.html#Lifecycle">Managing the Lifecycle of
-a Bound Service</a>.</p>
-
+href="{@docRoot}guide/components/bound-services.html#Lifecycle">Managing the lifecycle of
+a bound service</a>.</p>
<!--
<h2>Beginner's Path</h2>
diff --git a/docs/html/guide/topics/renderscript/reference/rs_allocation_create.jd b/docs/html/guide/topics/renderscript/reference/rs_allocation_create.jd
new file mode 100644
index 0000000..2defca3
--- /dev/null
+++ b/docs/html/guide/topics/renderscript/reference/rs_allocation_create.jd
@@ -0,0 +1,1060 @@
+page.title=RenderScript Allocation Creation Functions
+
+@jd:body
+
+<div class='renderscript'>
+<h2>Overview</h2>
+<p> The functions below are used to create allocations from within a script.
+These functions can be called directly or indirectly from an invokable
+function. It is an error if any control flow can result in calling these functions
+from a RenderScript kernel function.
+</p>
+<h2>Summary</h2>
+<table class='jd-sumtable'><tbody>
+ <tr><th colspan='2'>Functions</th></tr>
+ <tr class='alt-color api apilevel-1'>
+ <td class='jd-linkcol'>
+ <a href='rs_allocation_create.html#android_rs:rsCreateAllocation'>rsCreateAllocation</a>
+ </td>
+ <td class='jd-descrcol' width='100%'>
+ Creates an <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_allocation>rs_allocation</a> object of given <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_type>rs_type</a>
+ </td>
+ </tr>
+ <tr class='alt-color api apilevel-1'>
+ <td class='jd-linkcol'>
+ <a href='rs_allocation_create.html#android_rs:rsCreateElement'>rsCreateElement</a>
+ </td>
+ <td class='jd-descrcol' width='100%'>
+ Creates an <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_element>rs_element</a> object of the specified data type
+ </td>
+ </tr>
+ <tr class='alt-color api apilevel-1'>
+ <td class='jd-linkcol'>
+ <a href='rs_allocation_create.html#android_rs:rsCreatePixelElement'>rsCreatePixelElement</a>
+ </td>
+ <td class='jd-descrcol' width='100%'>
+ Creates an <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_element>rs_element</a> object of the specified data type and data kind
+ </td>
+ </tr>
+ <tr class='alt-color api apilevel-1'>
+ <td class='jd-linkcol'>
+ <a href='rs_allocation_create.html#android_rs:rsCreateType'>rsCreateType</a>
+ </td>
+ <td class='jd-descrcol' width='100%'>
+ Creates an <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_type>rs_type</a> object with the specified <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_element>rs_element</a> and shape attributes
+ </td>
+ </tr>
+ <tr class='alt-color api apilevel-1'>
+ <td class='jd-linkcol'>
+ <a href='rs_allocation_create.html#android_rs:rsCreateVectorElement'>rsCreateVectorElement</a>
+ </td>
+ <td class='jd-descrcol' width='100%'>
+ Creates an <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_element>rs_element</a> object of the specified data type and vector width
+ </td>
+ </tr>
+</tbody></table>
+<h2>Functions</h2>
+<a name='android_rs:rsCreateAllocation'></a>
+<div class='jd-details'>
+ <h4 class='jd-details-title'>
+ <span class='sympad'>rsCreateAllocation</span>
+ <span class='normal'>: Creates an <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_allocation>rs_allocation</a> object of given <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_type>rs_type</a></span>
+ </h4>
+ <div class='jd-details-descr'>
+ <table class='jd-tagtable'><tbody>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation(<a href='rs_object_types.html#android_rs:rs_type'>rs_type</a> type);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation(<a href='rs_object_types.html#android_rs:rs_type'>rs_type</a> type, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> usage);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_char4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_double4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_float4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_half4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_int4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_long4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_short4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uchar4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_uint4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ulong4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort2(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort3(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> rsCreateAllocation_ushort4(<a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata'> <h5 class='jd-tagtitle'>Parameters</h5>
+ <table class='jd-tagtable'><tbody>
+ <tr><th>type</th><td>Type of the allocation</td></tr>
+ <tr><th>usage</th><td>How the allocation should be used. A valid value is either of the following or their combination:<br>RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE,<br>RS_ALLOCATION_USAGE_SCRIPT.</td></tr>
+ <tr><th>mipmap</th><td>A boolean flag indicating if the allocation is mipmapped and has multiple levels of detail (LoD).</td></tr>
+ <tr><th>dimX</th><td>Size on dimension x. Must be non zero.</td></tr>
+ <tr><th>dimY</th><td>Size on dimension y. 0 for single-dimension allocations.</td></tr>
+ <tr><th>dimZ</th><td>Size on dimension z. 0 for single-dimension and two-dimension allocations.</td></tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata jd-tagdescr'>
+<p> Creates an rs_allocation object of the given rs_type and for the specified usages.
+</p>
+
+<p> RS_ALLOCATION_USAGE_SCRIPT and RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE are the
+ only supported usage flags for Allocations created from within a RenderScript
+ script.
+</p>
+
+<p> You can also use rsCreateAllocation_<type><width> wrapper functions to directly
+ create allocations of scalar and vector numerical types without creating
+ intermediate rs_element or rs_type objects.
+</p>
+
+<p> For example, rsCreateAllocation_int4() returns an Allocation of int4 data type of
+ specified dimensions.
+</p>
+ </div>
+</div>
+
+<a name='android_rs:rsCreateElement'></a>
+<div class='jd-details'>
+ <h4 class='jd-details-title'>
+ <span class='sympad'>rsCreateElement</span>
+ <span class='normal'>: Creates an <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_element>rs_element</a> object of the specified data type</span>
+ </h4>
+ <div class='jd-details-descr'>
+ <table class='jd-tagtable'><tbody>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_element'>rs_element</a> rsCreateElement(<a href='rs_object_types.html#android_rs:rs_data_type'>rs_data_type</a> data_type);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata'> <h5 class='jd-tagtitle'>Parameters</h5>
+ <table class='jd-tagtable'><tbody>
+ <tr><th>data_type</th><td>Data type of the Element</td></tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata jd-tagdescr'>
+<p> Creates an rs_element object of the specified data type. The data kind of
+ the element will be set to RS_KIND_USER and vector width will be set to 1,
+ indicating non-vector.
+</p>
+ </div>
+</div>
+
+<a name='android_rs:rsCreatePixelElement'></a>
+<div class='jd-details'>
+ <h4 class='jd-details-title'>
+ <span class='sympad'>rsCreatePixelElement</span>
+ <span class='normal'>: Creates an <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_element>rs_element</a> object of the specified data type and data kind</span>
+ </h4>
+ <div class='jd-details-descr'>
+ <table class='jd-tagtable'><tbody>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_element'>rs_element</a> rsCreatePixelElement(<a href='rs_object_types.html#android_rs:rs_data_type'>rs_data_type</a> data_type, <a href='rs_object_types.html#android_rs:rs_data_kind'>rs_data_kind</a> data_kind);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata'> <h5 class='jd-tagtitle'>Parameters</h5>
+ <table class='jd-tagtable'><tbody>
+ <tr><th>data_type</th><td>Data type of the Element</td></tr>
+ <tr><th>data_kind</th><td>Data kind of the Element</td></tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata jd-tagdescr'>
+<p> Creates an rs_element object of the specified data type and data kind. The
+ vector width of the rs_element object will be set to 1, indicating non-vector.
+</p>
+ </div>
+</div>
+
+<a name='android_rs:rsCreateType'></a>
+<div class='jd-details'>
+ <h4 class='jd-details-title'>
+ <span class='sympad'>rsCreateType</span>
+ <span class='normal'>: Creates an <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_type>rs_type</a> object with the specified <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_element>rs_element</a> and shape attributes</span>
+ </h4>
+ <div class='jd-details-descr'>
+ <table class='jd-tagtable'><tbody>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_type'>rs_type</a> rsCreateType(<a href='rs_object_types.html#android_rs:rs_element'>rs_element</a> element, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_type'>rs_type</a> rsCreateType(<a href='rs_object_types.html#android_rs:rs_element'>rs_element</a> element, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_type'>rs_type</a> rsCreateType(<a href='rs_object_types.html#android_rs:rs_element'>rs_element</a> element, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_type'>rs_type</a> rsCreateType(<a href='rs_object_types.html#android_rs:rs_element'>rs_element</a> element, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimX, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimY, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> dimZ, bool mipmaps, bool faces, <a href='rs_object_types.html#android_rs:rs_yuv_format'>rs_yuv_format</a> yuv_format);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata'> <h5 class='jd-tagtitle'>Parameters</h5>
+ <table class='jd-tagtable'><tbody>
+ <tr><th>element</th><td>An <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_element>rs_element</a> object that specifies the cell data type of an allocation.</td></tr>
+ <tr><th>dimX</th><td>Size on dimension x. Must be non zero.</td></tr>
+ <tr><th>dimY</th><td>Size on dimension y. 0 for single-dimension allocations.</td></tr>
+ <tr><th>dimZ</th><td>Size on dimension z. 0 for single-dimension and two-dimension allocations.</td></tr>
+ <tr><th>mipmaps</th><td>A boolean flag indicating if the allocation is mipmapped and has multiple levels of detail (LoD).</td></tr>
+ <tr><th>faces</th><td>A boolean flag indicating if the allocation is a cubemap that has cube faces.</td></tr>
+ <tr><th>yuv_format</th><td>Tye YUV layout.</td></tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata jd-tagdescr'>
+<p> Creates an rs_type object with the specified element and shape attributes.
+</p>
+
+<p> dimX specifies the size of the X dimension.
+</p>
+
+<p> dimY, if present and non-zero, indicates that the Y dimension is present and
+ indicates its size.
+</p>
+
+<p> dimZ, if present and non-zero, indicates that the Z dimension is present and
+ indicates its size.
+</p>
+
+<p> mipmaps indicates the presence of level of detail (LOD).
+</p>
+
+<p> faces indicates the presence of cubemap faces.
+</p>
+
+<p> yuv_format indicates the associated YUV format (or RS_YUV_NONE).
+</p>
+ </div>
+</div>
+
+<a name='android_rs:rsCreateVectorElement'></a>
+<div class='jd-details'>
+ <h4 class='jd-details-title'>
+ <span class='sympad'>rsCreateVectorElement</span>
+ <span class='normal'>: Creates an <a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_element>rs_element</a> object of the specified data type and vector width</span>
+ </h4>
+ <div class='jd-details-descr'>
+ <table class='jd-tagtable'><tbody>
+ <tr>
+ <td><a href='rs_object_types.html#android_rs:rs_element'>rs_element</a> rsCreateVectorElement(<a href='rs_object_types.html#android_rs:rs_data_type'>rs_data_type</a> data_type, <a href='rs_value_types.html#android_rs:uint32_t'>uint32_t</a> vector_width);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata'> <h5 class='jd-tagtitle'>Parameters</h5>
+ <table class='jd-tagtable'><tbody>
+ <tr><th>data_type</th><td>Data type of the Element</td></tr>
+ <tr><th>vector_width</th><td>Vector width</td></tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata jd-tagdescr'>
+<p> Creates an rs_element object of the specified data type and vector width.
+ Value of vector_width must be 2, 3 or 4. The data kind of the rs_element object will
+ be set to RS_KIND_USER.
+</p>
+ </div>
+</div>
+
+</div>
diff --git a/docs/html/guide/topics/renderscript/reference/rs_for_each.jd b/docs/html/guide/topics/renderscript/reference/rs_for_each.jd
index 9ba5614..8b19ba6e 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_for_each.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_for_each.jd
@@ -1,10 +1,10 @@
-page.title=RenderScript Kernel Invocation Functions and Types
+page.title=RenderScript Kernel Launch Functions and Types
@jd:body
<div class='renderscript'>
<h2>Overview</h2>
-<p> The <a href='rs_for_each.html#android_rs:rsForEach'>rsForEach</a>() function can be used to invoke the root kernel of a script.
+<p> The <a href='rs_for_each.html#android_rs:rsForEach'>rsForEach</a>() and <a href='rs_for_each.html#android_rs:rsForEachWithOptions'>rsForEachWithOptions</a>() functions are used to launch foreach kernels.
</p>
<p> The other functions are used to get the characteristics of the invocation of
@@ -24,6 +24,14 @@
</tr>
<tr class='alt-color api apilevel-1'>
<td class='jd-linkcol'>
+ <a href='rs_for_each.html#android_rs:rs_kernel'>rs_kernel</a>
+ </td>
+ <td class='jd-descrcol' width='100%'>
+ Handle to a kernel function
+ </td>
+ </tr>
+ <tr class='alt-color api apilevel-1'>
+ <td class='jd-linkcol'>
<a href='rs_for_each.html#android_rs:rs_kernel_context'>rs_kernel_context</a>
</td>
<td class='jd-descrcol' width='100%'>
@@ -46,7 +54,15 @@
<a href='rs_for_each.html#android_rs:rsForEach'>rsForEach</a>
</td>
<td class='jd-descrcol' width='100%'>
- Invoke the root kernel of a script
+ Launches a kernel
+ </td>
+ </tr>
+ <tr class='alt-color api apilevel-1'>
+ <td class='jd-linkcol'>
+ <a href='rs_for_each.html#android_rs:rsForEachWithOptions'>rsForEachWithOptions</a>
+ </td>
+ <td class='jd-descrcol' width='100%'>
+ Launches a kernel with options
</td>
</tr>
<tr class='alt-color api apilevel-1'>
@@ -198,6 +214,21 @@
</div>
</div>
+<a name='android_rs:rs_kernel'></a>
+<div class='jd-details'>
+ <h4 class='jd-details-title'>
+ <span class='sympad'>rs_kernel</span>
+ <span class='normal'>: Handle to a kernel function</span>
+ </h4>
+ <div class='jd-details-descr'>
+<p>A typedef of: void* Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+</p>
+<p> An opaque type for a function that is defined with the kernel attribute. A value
+ of this type can be used in a <a href='rs_for_each.html#android_rs:rsForEach'>rsForEach</a> call to launch a kernel.
+</p>
+ </div>
+</div>
+
<a name='android_rs:rs_kernel_context'></a>
<div class='jd-details'>
<h4 class='jd-details-title'>
@@ -249,7 +280,7 @@
the cells.
</p>
-<p> The Start fields are inclusive and the End fields are exclusive. E.g. to iterate
+<p> The Start fields are inclusive and the End fields are exclusive. For example, to iterate
over cells 4, 5, 6, and 7 in the X dimension, set xStart to 4 and xEnd to 8.
</p>
</div>
@@ -260,14 +291,20 @@
<div class='jd-details'>
<h4 class='jd-details-title'>
<span class='sympad'>rsForEach</span>
- <span class='normal'>: Invoke the root kernel of a script</span>
+ <span class='normal'>: Launches a kernel</span>
</h4>
<div class='jd-details-descr'>
<table class='jd-tagtable'><tbody>
<tr>
+ <td>void rsForEach(<a href='rs_for_each.html#android_rs:rs_kernel'>rs_kernel</a> kernel, ... ...);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ <tr>
<td>void rsForEach(<a href='rs_object_types.html#android_rs:rs_script'>rs_script</a> script, <a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> input, <a href='rs_object_types.html#android_rs:rs_allocation'>rs_allocation</a> output);
</td>
- <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 14</a>
+ <td> <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 14 - 23</a>
</td>
</tr>
<tr>
@@ -300,35 +337,89 @@
<table class='jd-tagtable'><tbody>
<tr><th>script</th><td>Script to call.</td></tr>
<tr><th>input</th><td>Allocation to source data from.</td></tr>
- <tr><th>output</th><td>Allocation to write date into.</td></tr>
+ <tr><th>output</th><td>Allocation to write data into.</td></tr>
<tr><th>usrData</th><td>User defined data to pass to the script. May be NULL.</td></tr>
<tr><th>sc</th><td>Extra control information used to select a sub-region of the allocation to be processed or suggest a walking strategy. May be NULL.</td></tr>
<tr><th>usrDataLen</th><td>Size of the userData structure. This will be used to perform a shallow copy of the data if necessary.</td></tr>
+ <tr><th>kernel</th><td>Function designator of the kernel function to call, which must be defined with the kernel attribute.</td></tr>
+ <tr><th>...</th><td>Input and output allocations</td></tr>
</tbody></table>
</div>
<div class='jd-tagdata jd-tagdescr'>
-<p> Invoke the kernel named "root" of the specified script. Like other kernels, this root()
-function will be invoked repeatedly over the cells of the specificed allocation, filling
-the output allocation with the results.
+<p> Runs the kernel over zero or more input allocations. They are passed after the
+<a href='rs_for_each.html#android_rs:rs_kernel'>rs_kernel</a> argument. If the specified kernel returns a value, an output allocation
+must be specified as the last argument. All input allocations,
+and the output allocation if it exists, must have the same dimensions.
</p>
-<p> When rsForEach is called, the root script is launched immediately. rsForEach returns
-only when the script has completed and the output allocation is ready to use.
+<p> This is a synchronous function. A call to this function only returns after all
+the work has completed. If the kernel
+function returns any value, the call waits until all results have been written
+to the output allocation.
</p>
-<p> The rs_script argument is typically initialized using a global variable set from Java.
+<p> Up to API level 23, the kernel is implicitly specified as the kernel named
+"root" in the specified script, and only a single input allocation can be used.
+Starting in API level 24, an arbitrary kernel function can be used,
+as specified by the kernel argument.
+The kernel must be defined in the current script. In addition, more than one
+input can be used.
</p>
-<p> The kernel can be invoked with just an input allocation or just an output allocation.
-This can be done by defining an rs_allocation variable and not initializing it. E.g.<code><br/>
-rs_script gCustomScript;<br/>
-void specializedProcessing(rs_allocation in) {<br/>
- rs_allocation ignoredOut;<br/>
- rsForEach(gCustomScript, in, ignoredOut);<br/>
-}<br/></code>
+<p> For example,<code><br/>
+float __attribute__((kernel)) square(float a) {<br/>
+ return a * a;<br/>
+}<br/>
+<br/>
+void compute(rs_allocation ain, rs_allocation aout) {<br/>
+ rsForEach(square, ain, aout);<br/>
+}<br/>
+<br/></code>
+</p>
+ </div>
+</div>
+
+<a name='android_rs:rsForEachWithOptions'></a>
+<div class='jd-details'>
+ <h4 class='jd-details-title'>
+ <span class='sympad'>rsForEachWithOptions</span>
+ <span class='normal'>: Launches a kernel with options</span>
+ </h4>
+ <div class='jd-details-descr'>
+ <table class='jd-tagtable'><tbody>
+ <tr>
+ <td>void rsForEachWithOptions(<a href='rs_for_each.html#android_rs:rs_kernel'>rs_kernel</a> kernel, <a href='rs_for_each.html#android_rs:rs_script_call_t'>rs_script_call_t</a>* options, ... ...);
+</td>
+ <td> Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 24</a>
+ </td>
+ </tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata'> <h5 class='jd-tagtitle'>Parameters</h5>
+ <table class='jd-tagtable'><tbody>
+ <tr><th>kernel</th><td>Function designator to a function that is defined with the kernel attribute.</td></tr>
+ <tr><th>options</th><td>Launch options</td></tr>
+ <tr><th>...</th><td>Input and output allocations</td></tr>
+ </tbody></table>
+ </div>
+ <div class='jd-tagdata jd-tagdescr'>
+<p> Launches kernel in a way similar to <a href='rs_for_each.html#android_rs:rsForEach'>rsForEach</a>. However, instead of processing
+all cells in the input, this function only processes cells in the subspace of
+the index space specified in options. With the index space explicitly specified
+by options, no input or output allocation is required for a kernel launch using
+this API. If allocations are passed in, they must match the number of arguments
+and return value expected by the kernel function. The output allocation is
+present if and only if the kernel has a non-void return value.
</p>
-<p> If both input and output allocations are specified, they must have the same dimensions.
+<p> For example,<code><br/>
+ rs_script_call_t opts = {0};<br/>
+ opts.xStart = 0;<br/>
+ opts.xEnd = dimX;<br/>
+ opts.yStart = 0;<br/>
+ opts.yEnd = dimY / 2;<br/>
+ rsForEachWithOptions(foo, &opts, out, out);<br/>
+</code>
</p>
</div>
</div>
@@ -359,7 +450,7 @@
</p>
<p> You can access the kernel context by adding a special parameter named "context" of
-type rs_kernel_context to your kernel function. E.g.<br/>
+type rs_kernel_context to your kernel function. For example,<br/>
<code>short RS_KERNEL myKernel(short value, uint32_t x, rs_kernel_context context) {<br/>
// The current index in the common x, y, z dimensions are accessed by<br/>
// adding these variables as arguments. For the more rarely used indices<br/>
@@ -644,7 +735,7 @@
</p>
<p> You can access it by adding a special parameter named "context" of
-type rs_kernel_context to your kernel function. E.g.<br/>
+type rs_kernel_context to your kernel function. For example,<br/>
<code>int4 RS_KERNEL myKernel(int4 value, rs_kernel_context context) {<br/>
uint32_t size = rsGetDimX(context); //...<br/></code>
</p>
diff --git a/docs/html/samples/new/index.jd b/docs/html/samples/new/index.jd
index a7ffa8c..4d6262ed 100644
--- a/docs/html/samples/new/index.jd
+++ b/docs/html/samples/new/index.jd
@@ -5,7 +5,7 @@
<p>The following code samples were recently published. You can
download them in the Android SDK Manager under the <b>Samples for SDK</b>
-component for Android 6.0 (API 23).</p>
+component for Android 7.1 (API 25).</p>
<p class="note">
<strong>Note:</strong> The downloadable projects are designed
@@ -14,115 +14,67 @@
<!-- NOTE TO EDITORS: add most recent samples first -->
-<h3 id="ActiveNotification">
- <a href="{@docRoot}samples/ActiveNotifications/index.html">Active
- Notification</a>
-</h3>
-<p>
- This sample demonstrates how to use the {@link
- android.app.NotificationManager} to tell you how many notifications your app
- is currently showing.
-</p>
+<h3 id="app-shortcuts">App shortcuts sample</h3>
-<h3 id="AutomaticBackup">
- <a href="{@docRoot}samples/AutoBackupForApps/index.html">Auto Backup for
- Apps</a>
-</h3>
-
-<p>
- Android 6.0 (API level 23) introduces automatic backup for app settings. This
- sample demonstrates how to add filtering rules to an app to manage settings
- backup.
-</p>
-
-<h3 id="Camera2Raw">
- <a href="{@docRoot}samples/Camera2Raw/index.html">Camera 2 Raw</a>
-</h3>
-
-<p>
- This sample demonstrates how to use the
- <a href="{@docRoot}reference/android/hardware/camera2/package-summary.html">
- <code>Camera2</code></a> API to capture RAW camera buffers and save them as
- DNG files.
-</p>
-
-<h3 id="ConfirmCredential">
- <a href="{@docRoot}samples/ConfirmCredential/index.html">Confirm
- Credential</a>
-</h3>
-
-<p>
- This sample demonstrates how to use device credentials as an authentication method in your app.
-</p>
-
-<h3 id="DeviceOwner">
- <a href="{@docRoot}samples/DeviceOwner/index.html">Device Owner</a>
-</h3>
-
-<p>
- This sample demonstrates how to use the device owner features to manage and
- configure a device.
-</p>
-
-<h3 id="DirectShare">
- <a href="{@docRoot}samples/DirectShare/index.html">Direct Share</a>
-</h3>
-
-<p>
- This sample demonstrates how to provide the
- <a href="{@docRoot}about/versions/marshmallow/android-6.0.html#direct-share">Direct
- Share</a> feature. The app shows some options directly in the list of share
- intent candidates.
-</p>
-
-<h3 id="FingerprintDialog">
- <a href="{@docRoot}samples/FingerprintDialog/index.html">Fingerprint
- Dialog</a>
-</h3>
-
-<p>
- This sample demonstrates how to recognize registered fingerprints to
- authenticate your app's user.
-</p>
-
-<h3 id="MidiScope">
- <a href="{@docRoot}samples/MidiScope/index.html">MidiScope</a>
-</h3>
+<!-- TBA
+<img src="sample-img.png" style="float: left; padding-right: 0.5em"
+ width="xxx"/>
+-->
<p>
This sample demonstrates how to use the <a href=
- "{@docRoot}reference/android/media/midi/package-summary.html">MIDI API</a> to
- receive and process MIDI signals coming from an attached input device.
+ "/preview/app-shortcuts.html">app shortcuts API</a> introduced in Android 7.1
+ (API level 25). This API allows an application to define a set of intents
+ which are displayed when a user long-presses on the app's launcher icon.
+ Examples are given for registering links both statically in XML, as well as
+ dynamically at runtime.
</p>
-<h3 id="MidiSynth">
- <a href="{@docRoot}samples/MidiSynth/index.html">MidiSynth</a>
-</h3>
-
<p>
- This sample demonstrates how to use the <a href=
- "{@docRoot}reference/android/media/midi/package-summary.html">MIDI API</a> to
- receive and play MIDI messages coming from an attached input device.
+ <a href="/samples/AppShortcuts/index.html">App shortcuts sample</a>
</p>
-<h3 id="NfcProvisioning">
- <a href="{@docRoot}samples/NfcProvisioning/index.html">NFC Provisioning</a>
-</h3>
+<h3 id="img-kbd-app">Image keyboard app sample</h3>
+
+<!-- TBA
+<img src="sample-img.png" style="float: left; padding-right: 0.5em"
+ width="xxx"/>
+-->
<p>
- This sample demonstrates how to use NFC to provision other devices with a
- specific device owner.
+ This sample demonstrates how to implement the <a href=
+ "/reference/android/view/inputmethod/InputConnection.html#commitContent(android.view.inputmethod.InputContentInfo,%20int,%20android.os.Bundle)">
+ Commit Content API</a>, using the <a href=
+ "/topic/libraries/support-library/index.html">Android Support Library</a>.
+ This API provides a universal way for IMEs to send images and other rich
+ content directly to a text editor in an app, allowing users to compose
+ content using custom emojis, stickers, or other rich content provided by
+ other applications.
</p>
-<h3 id="RuntimePermissions">
- <a href=
- "{@docRoot}samples/RuntimePermissions/index.html">RuntimePermissions</a>
-</h3>
+<p>
+ <a href="/samples/CommitContentSampleApp/index.html">Image keyboard app sample</a>
+</p>
+
+<h3 id="img-kbd-ime">Image keyboard IME sample</h3>
+
+<!-- TBA
+<img src="sample-img.png" style="float: left; padding-right: 0.5em"
+ width="xxx"/>
+-->
<p>
- This sample shows runtime permissions available in Android 6.0 (API level 23)
- and higher. Display the log on screen to follow the execution. If executed on
- an Android 6.0 device, the app displays an additional option to access
- contacts using an 6.0-only optional permission.
+ This sample demonstrates how to write a <a href=
+ "/preview/image-keyboard.html">custom image keyboard</a> using the <a href=
+ "/reference/android/view/inputmethod/InputConnection.html#commitContent(android.view.inputmethod.InputContentInfo,%20int,%20android.os.Bundle)">
+ Commit Content API</a> and the <a href=
+ "/topic/libraries/support-library/index.html">Android Support Library</a>.
+ This keyboard will be displayed inside compatible apps (also using the Commit
+ Content API), allowing users to insert emojis, stickers, or other rich
+ content into text editors.
+</p>
+
+<p>
+ <a href="/samples/CommitContentSampleIME/index.html">Image keyboard IME sample</a>
</p>
diff --git a/docs/html/training/articles/security-tips.jd b/docs/html/training/articles/security-tips.jd
index abf6711..9796d9a 100644
--- a/docs/html/training/articles/security-tips.jd
+++ b/docs/html/training/articles/security-tips.jd
@@ -6,34 +6,32 @@
<div id="tb">
<h2>In this document</h2>
<ol class="nolist">
- <li><a href="#StoringData">Storing Data</a></li>
- <li><a href="#Permissions">Using Permissions</a></li>
- <li><a href="#Networking">Using Networking</a></li>
- <li><a href="#InputValidation">Performing Input Validation</a></li>
- <li><a href="#UserData">Handling User Data</a></li>
+ <li><a href="#StoringData">Storing data</a></li>
+ <li><a href="#Permissions">Using permissions</a></li>
+ <li><a href="#Networking">Using networking</a></li>
+ <li><a href="#InputValidation">Performing input validation</a></li>
+ <li><a href="#UserData">Handling user data</a></li>
<li><a href="#WebView">Using WebView</a></li>
- <li><a href="#Crypto">Using Cryptography</a></li>
- <li><a href="#IPC">Using Interprocess Communication</a></li>
- <li><a href="#DynamicCode">Dynamically Loading Code</a></li>
- <li><a href="#Dalvik">Security in a Virtual Machine</a></li>
- <li><a href="#Native">Security in Native Code</a></li>
+ <li><a href="#Crypto">Using cryptography</a></li>
+ <li><a href="#IPC">Using interprocess communication</a></li>
+ <li><a href="#DynamicCode">Dynamically loading code</a></li>
+ <li><a href="#Dalvik">Security in a virtual machine</a></li>
+ <li><a href="#Native">Security in native code</a></li>
</ol>
<h2>See also</h2>
<ul>
<li><a href="http://source.android.com/tech/security/index.html">Android
-Security Overview</a></li>
+ Security Overview</a></li>
<li><a href="{@docRoot}guide/topics/security/permissions.html">Permissions</a></li>
</ul>
</div></div>
-<p>Android has security features built
-into the operating system that significantly reduce the frequency and impact of
-application security issues. The system is designed so you can typically build your apps with
-default system and file permissions and avoid difficult decisions about security.</p>
+<p>Android has built-in security features that significantly reduce the frequency and impact of
+application security issues. The system is designed so that you can typically build your apps with
+the default system and file permissions and avoid difficult decisions about security.</p>
-<p>Some of the core security features that help you build secure apps
-include:
+<p>The following core security features help you build secure apps:
<ul>
<li>The Android Application Sandbox, which isolates your app data and code execution
from other apps.</li>
@@ -43,47 +41,54 @@
<li>Technologies like ASLR, NX, ProPolice, safe_iop, OpenBSD dlmalloc, OpenBSD
calloc, and Linux mmap_min_addr to mitigate risks associated with common memory
management errors.</li>
-<li>An encrypted filesystem that can be enabled to protect data on lost or
+<li>An encrypted file system that can be enabled to protect data on lost or
stolen devices.</li>
<li>User-granted permissions to restrict access to system features and user data.</li>
<li>Application-defined permissions to control application data on a per-app basis.</li>
</ul>
-<p>Nevertheless, it is important that you be familiar with the Android
+<p>It is important that you be familiar with the Android
security best practices in this document. Following these practices as general coding habits
-will reduce the likelihood of inadvertently introducing security issues that
+ reduces the likelihood of inadvertently introducing security issues that
adversely affect your users.</p>
-<h2 id="StoringData">Storing Data</h2>
+<h2 id="StoringData">Storing data</h2>
<p>The most common security concern for an application on Android is whether the data
that you save on the device is accessible to other apps. There are three fundamental
ways to save data on the device:</p>
+<ul>
+<li>Internal storage.</li>
+<li>External storage.</li>
+<li>Content providers.</li>
+</ul>
+
+The following paragraphs describe the security issues associated with each approach.
+
<h3 id="InternalStorage">Using internal storage</h3>
<p>By default, files that you create on <a
href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal
-storage</a> are accessible only to your app. This
-protection is implemented by Android and is sufficient for most
-applications.</p>
+storage</a> are accessible only to your app.
+ Android implements this protection, and it's sufficient for most applications.</p>
-<p>You should generally avoid using the {@link android.content.Context#MODE_WORLD_WRITEABLE} or
+<p>Generally, avoid the {@link android.content.Context#MODE_WORLD_WRITEABLE} or
{@link android.content.Context#MODE_WORLD_READABLE} modes for
<acronym title="Interprocess Communication">IPC</acronym> files because they do not provide
the ability to limit data access to particular applications, nor do they
-provide any control on data format. If you want to share your data with other
-app processes, you might instead consider using a
+provide any control of data format. If you want to share your data with other
+app processes, instead consider using a
<a href="{@docRoot}guide/topics/providers/content-providers.html">content provider</a>, which
offers read and write permissions to other apps and can make
dynamic permission grants on a case-by-case basis.</p>
-<p>To provide additional protection for sensitive data, you might
-choose to encrypt local files using a key that is not directly accessible to the
-application. For example, a key can be placed in a {@link java.security.KeyStore}
-and protected with a user password that is not stored on the device. While this
+<p>To provide additional protection for sensitive data, you can
+ encrypt local files using a key that is not directly accessible to the
+application. For example, you can place a key in a {@link java.security.KeyStore}
+and protect it with a user password that is not stored on the device. While this
does not protect data from a root compromise that can monitor the user
inputting the password, it can provide protection for a lost device without <a
href="http://source.android.com/tech/encryption/index.html">file system
@@ -94,14 +99,14 @@
<p>Files created on <a
href="{@docRoot}guide/topics/data/data-storage.html#filesExternal">external
-storage</a>, such as SD Cards, are globally readable and writable. Because
+storage</a>, such as SD cards, are globally readable and writable. Because
external storage can be removed by the user and also modified by any
-application, you should not store sensitive information using
+application, don't store sensitive information using
external storage.</p>
-<p>As with data from any untrusted source, you should <a href="#InputValidation">perform input
-validation</a> when handling data from external storage.
-We strongly recommend that you not store executables or
+<p>You should <a href="#InputValidation">Perform input validation</a> when handling
+data from external storage as you would with data from any untrusted source.
+You should not store executables or
class files on external storage prior to dynamic loading. If your app
does retrieve executable files from external storage, the files should be signed and
cryptographically verified prior to dynamic loading.</p>
@@ -117,22 +122,22 @@
href="{@docRoot}guide/topics/manifest/provider-element.html#exported">
android:exported=false</a></code> in the application manifest. Otherwise, set the <code><a
href="{@docRoot}guide/topics/manifest/provider-element.html#exported">android:exported</a></code>
-attribute {@code "true"} to allow other apps to access the stored data.
+attribute to {@code true} to allow other apps to access the stored data.
</p>
<p>When creating a {@link android.content.ContentProvider}
-that will be exported for use by other applications, you can specify a single
+that is exported for use by other applications, you can specify a single
<a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn">permission
-</a> for reading and writing, or distinct permissions for reading and writing
-within the manifest. We recommend that you limit your permissions to those
+</a> for reading and writing, or you can specify distinct permissions for reading and writing.
+You should limit your permissions to those
required to accomplish the task at hand. Keep in mind that it’s usually
easier to add permissions later to expose new functionality than it is to take
-them away and break existing users.</p>
+them away and impact existing users.</p>
<p>If you are using a content provider
for sharing data between only your own apps, it is preferable to use the
<a href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">{@code
-android:protectionLevel}</a> attribute set to {@code "signature"} protection.
+android:protectionLevel}</a> attribute set to {@code signature} protection.
Signature permissions do not require user confirmation,
so they provide a better user experience and more controlled access to the
content provider data when the apps accessing the data are
@@ -148,7 +153,7 @@
that activates the component. The scope of these permissions can be further
limited by the <code><a
href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html">
-<grant-uri-permission element></a></code>.</p>
+<grant-uri-permission></a></code> element.</p>
<p>When accessing a content provider, use parameterized query methods such as
{@link android.content.ContentProvider#query(Uri,String[],String,String[],String) query()},
@@ -158,11 +163,11 @@
sufficient if the <code>selection</code> argument is built by concatenating user data
prior to submitting it to the method.</p>
-<p>Do not have a false sense of security about the write permission. Consider
-that the write permission allows SQL statements which make it possible for some
+<p>Don't have a false sense of security about the write permission.
+ The write permission allows SQL statements that make it possible for some
data to be confirmed using creative <code>WHERE</code> clauses and parsing the
-results. For example, an attacker might probe for presence of a specific phone
-number in a call-log by modifying a row only if that phone number already
+results. For example, an attacker might probe for the presence of a specific phone
+number in a call log by modifying a row only if that phone number already
exists. If the content provider data has predictable structure, the write
permission may be equivalent to providing both reading and writing.</p>
@@ -172,7 +177,7 @@
-<h2 id="Permissions">Using Permissions</h2>
+<h2 id="Permissions">Using permissions</h2>
<p>Because Android sandboxes applications from each other, applications must explicitly
share resources and data. They do this by declaring the permissions they need for additional
@@ -180,25 +185,25 @@
the camera.</p>
-<h3 id="RequestingPermissions">Requesting Permissions</h3>
+<h3 id="RequestingPermissions">Requesting permissions</h3>
-<p>We recommend minimizing the number of permissions that your app requests.
-Not having access to sensitive permissions reduces the risk of
-inadvertently misusing those permissions, can improve user adoption, and makes
+<p>You should minimize the number of permissions that your app requests.
+Restricting access to sensitive permissions reduces the risk of
+inadvertently misusing those permissions, improves user adoption, and makes
your app less vulnerable for attackers. Generally,
-if a permission is not required for your app to function, do not request it.</p>
+if a permission is not required for your app to function, don't request it.</p>
<p>If it's possible to design your application in a way that does not require
any permissions, that is preferable. For example, rather than requesting access
to device information to create a unique identifier, create a <a
href="{@docRoot}reference/java/util/UUID.html">GUID</a> for your application
-(see the section about <a href="#UserData">Handling User Data</a>). Or, rather than
+(see the section about <a href="#UserData">Handling user data</a>). Or, rather than
using external storage (which requires permission), store data
on the internal storage.</p>
<p>In addition to requesting permissions, your application can use the <a
-href="{@docRoot}guide/topics/manifest/permission-element.html">{@code <permissions>}</a>
-to protect IPC that is security sensitive and will be exposed to other
+href="{@docRoot}guide/topics/manifest/permission-element.html">{@code <permission>}</a>
+ element to protect IPC that is security sensitive and is exposed to other
applications, such as a {@link android.content.ContentProvider}.
In general, we recommend using access controls
other than user confirmed permissions where possible because permissions can
@@ -211,13 +216,14 @@
data over IPC that is available only because your app has permission to access
that data. The clients of your app's IPC interface may not have that same
data-access permission. More details on the frequency and potential effects
-of this issue appear in <a class="external-link"
-href="https://www.usenix.org/legacy/event/sec11/tech/full_papers/Felt.pdf"> this
-research paper</a>, published at USENIX.
+of this issue appear in the research paper <a
+href="https://www.usenix.org/legacy/event/sec11/tech/full_papers/Felt.pdf" class="external-link">
+Permission Re-Delegation: Attacks and Defenses
+</a>, published at USENIX.
-<h3 id="CreatingPermissions">Creating Permissions</h3>
+<h3 id="CreatingPermissions">Creating permissions</h3>
<p>Generally, you should strive to define as few permissions as possible while
satisfying your security requirements. Creating a new permission is relatively
@@ -228,18 +234,18 @@
<p>If you must create a new permission, consider whether you can accomplish
your task with a <a
-href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">"signature"
+href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">signature
protection level</a>. Signature permissions are transparent
-to the user and only allow access by applications signed by the same developer
-as application performing the permission check.</p>
+to the user and allow access only by applications signed by the same developer
+as the application performing the permission check.</p>
<p>If you create a permission with the <a
-href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">"dangerous"
+href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">dangerous
protection level</a>, there are a number of complexities
that you need to consider:
<ul>
<li>The permission must have a string that concisely expresses to a user the
-security decision they will be required to make.</li>
+security decision they are required to make.</li>
<li>The permission string must be localized to many different languages.</li>
<li>Users may choose not to install an application because a permission is
confusing or perceived as risky.</li>
@@ -247,28 +253,28 @@
has not been installed.</li>
</ul>
-<p>Each of these poses a significant non-technical challenge for you as the developer
+<p>Each of these poses a significant nontechnical challenge for you as the developer
while also confusing your users,
-which is why we discourage the use of the "dangerous" permission level.</p>
+which is why we discourages the use of the <em>dangerous</em> permission level.</p>
-<h2 id="Networking">Using Networking</h2>
+<h2 id="Networking">Using networking</h2>
-<p>Network transactions are inherently risky for security, because it involves transmitting
+<p>Network transactions are inherently risky for security, because they involve transmitting
data that is potentially private to the user. People are increasingly aware of the privacy
concerns of a mobile device, especially when the device performs network transactions,
so it's very important that your app implement all best practices toward keeping the user's
data secure at all times.</p>
-<h3 id="IPNetworking">Using IP Networking</h3>
+<h3 id="IPNetworking">Using IP networking</h3>
<p>Networking on Android is not significantly different from other Linux
environments. The key consideration is making sure that appropriate protocols
are used for sensitive data, such as {@link javax.net.ssl.HttpsURLConnection} for
-secure web traffic. We prefer use of HTTPS over HTTP anywhere that HTTPS is
+secure web traffic. You should use HTTPS over HTTP anywhere that HTTPS is
supported on the server, because mobile devices frequently connect on networks
that are not secured, such as public Wi-Fi hotspots.</p>
@@ -278,32 +284,32 @@
wireless networks using Wi-Fi, the use of secure networking is strongly
encouraged for all applications that communicate over the network.</p>
-<p>We have seen some applications use <a
-href="http://en.wikipedia.org/wiki/Localhost">localhost</a> network ports for
-handling sensitive IPC. We discourage this approach since these interfaces are
-accessible by other applications on the device. Instead, you should use an Android IPC
-mechanism where authentication is possible such as with a {@link android.app.Service}. (Even
-worse than using loopback is to bind to INADDR_ANY since then your application
-may receive requests from anywhere.)</p>
+<p>Some applications use <a
+href="http://en.wikipedia.org/wiki/Localhost" class="external-link">localhost</a> network ports for
+handling sensitive IPC. You should not use this approach because these interfaces are
+accessible by other applications on the device. Instead, use an Android IPC
+mechanism where authentication is possible, such as with a {@link android.app.Service}.
+Binding to INADDR_ANY is worse than using loopback because then your application
+may receive requests from anywhere.</p>
-<p>Also, one common issue that warrants repeating is to make sure that you do
-not trust data downloaded from HTTP or other insecure protocols. This includes
+<p>Make sure that you don't
+ trust data downloaded from HTTP or other insecure protocols. This includes
validation of input in {@link android.webkit.WebView} and
any responses to intents issued against HTTP.</p>
-<h3>Using Telephony Networking</h3>
+<h3>Using telephony networking</h3>
<p>The <acronym title="Short Message Service">SMS</acronym> protocol was primarily designed for
user-to-user communication and is not well-suited for apps that want to transfer data.
-Due to the limitations of SMS, we strongly recommend the use of <a
+Due to the limitations of SMS, you should use <a
href="{@docRoot}google/gcm/index.html">Google Cloud Messaging</a> (GCM)
and IP networking for sending data messages from a web server to your app on a user device.</p>
<p>Beware that SMS is neither encrypted nor strongly
-authenticated on either the network or the device. In particular, any SMS receiver
-should expect that a malicious user may have sent the SMS to your application—Do
-not rely on unauthenticated SMS data to perform sensitive commands.
+authenticated on either the network or the device. In particular, any SMS receiver
+should expect that a malicious user may have sent the SMS to your application. Don't
+ rely on unauthenticated SMS data to perform sensitive commands.
Also, you should be aware that SMS may be subject to spoofing and/or
interception on the network. On the Android-powered device itself, SMS
messages are transmitted as broadcast intents, so they may be read or captured
@@ -314,32 +320,32 @@
-<h2 id="InputValidation">Performing Input Validation</h2>
+<h2 id="InputValidation">Performing input validation</h2>
<p>Insufficient input validation is one of the most common security problems
-affecting applications, regardless of what platform they run on. Android does
-have platform-level countermeasures that reduce the exposure of applications to
-input validation issues and you should use those features where possible. Also
-note that selection of type-safe languages tends to reduce the likelihood of
+affecting applications, regardless of what platform they run on. Android
+has platform-level countermeasures that reduce the exposure of applications to
+input validation issues, and you should use those features where possible. Also
+note that the selection of type-safe languages tends to reduce the likelihood of
input validation issues.</p>
-<p>If you are using native code, then any data read from files, received over
+<p>If you are using native code, any data read from files, received over
the network, or received from an IPC has the potential to introduce a security
issue. The most common problems are <a
-href="http://en.wikipedia.org/wiki/Buffer_overflow">buffer overflows</a>, <a
-href="http://en.wikipedia.org/wiki/Double_free#Use_after_free">use after
+href="http://en.wikipedia.org/wiki/Buffer_overflow" class="external-link">buffer overflows</a>, <a
+href="http://en.wikipedia.org/wiki/Double_free#Use_after_free" class="external-link">use after
free</a>, and <a
-href="http://en.wikipedia.org/wiki/Off-by-one_error">off-by-one errors</a>.
+href="http://en.wikipedia.org/wiki/Off-by-one_error" class="external-link">off-by-one errors</a>.
Android provides a number of technologies like <acronym
title="Address Space Layout Randomization">ASLR</acronym> and <acronym
title="Data Execution Prevention">DEP</acronym> that reduce the
-exploitability of these errors, but they do not solve the underlying problem.
-You can prevent these vulneratbilities by careful handling pointers and managing
+exploitability of these errors, but they don't solve the underlying problem.
+You can prevent these vulnerabilities by carefully handling pointers and managing
buffers.</p>
-<p>Dynamic, string based languages such as JavaScript and SQL are also subject
+<p>Dynamic, string-based languages such as JavaScript and SQL are also subject
to input validation problems due to escape characters and <a
-href="http://en.wikipedia.org/wiki/Code_injection">script injection</a>.</p>
+href="http://en.wikipedia.org/wiki/Code_injection" class="external-link">script injection</a>.</p>
<p>If you are using data within queries that are submitted to an SQL database or a
content provider, SQL injection may be an issue. The best defense is to use
@@ -348,60 +354,59 @@
Limiting permissions to read-only or write-only can also reduce the potential
for harm related to SQL injection.</p>
-<p>If you cannot use the security features above, we strongly recommend the use
-of well-structured data formats and verifying that the data conforms to the
+<p>If you can't use the security features above, you should make sure to use
+well-structured data formats and verify that the data conforms to the
expected format. While blacklisting of characters or character-replacement can
-be an effective strategy, these techniques are error-prone in practice and
+be an effective strategy, these techniques are error prone in practice and
should be avoided when possible.</p>
-<h2 id="UserData">Handling User Data</h2>
+<h2 id="UserData">Handling user data</h2>
<p>In general, the best approach for user data security is to minimize the use of APIs that access
sensitive or personal user data. If you have access to user data and can avoid
-storing or transmitting the information, do not store or transmit the data.
-Finally, consider if there is a way that your application logic can be
+storing or transmitting it, don't store or transmit the data.
+Consider if there is a way that your application logic can be
implemented using a hash or non-reversible form of the data. For example, your
-application might use the hash of an an email address as a primary key, to
+application might use the hash of an email address as a primary key to
avoid transmitting or storing the email address. This reduces the chances of
inadvertently exposing data, and it also reduces the chance of attackers
attempting to exploit your application.</p>
<p>If your application accesses personal information such as passwords or
-usernames, keep in mind that some jurisdictions may require you to provide a
-privacy policy explaining your use and storage of that data. So following the
+user names, keep in mind that some jurisdictions may require you to provide a
+privacy policy explaining your use and storage of that data. Following the
security best practice of minimizing access to user data may also simplify
compliance.</p>
<p>You should also consider whether your application might be inadvertently
exposing personal information to other parties such as third-party components
for advertising or third-party services used by your application. If you don't
-know why a component or service requires a personal information, don’t
+know why a component or service requires personal information, don’t
provide it. In general, reducing the access to personal information by your
-application will reduce the potential for problems in this area.</p>
+application reduces the potential for problems in this area.</p>
-<p>If access to sensitive data is required, evaluate whether that information
-must be transmitted to a server, or whether the operation can be performed on
-the client. Consider running any code using sensitive data on the client to
-avoid transmitting user data.</p>
-
-<p>Also, make sure that you do not inadvertently expose user data to other
-application on the device through overly permissive IPC, world writable files,
-or network sockets. This is a special case of leaking permission-protected data,
+<p>If your app requires access to sensitive data, evaluate whether you need to
+ transmit it to a server or you can run the operation on
+the client. Consider running any code using sensitive data on the client to
+avoid transmitting user data. Also, make sure that you do not inadvertently expose user
+ data to other
+applications on the device through overly permissive IPC, world-writable files,
+or network sockets. Overly permissive IPC is a special case of leaking permission-protected data,
discussed in the <a href="#RequestingPermissions">Requesting Permissions</a> section.</p>
<p>If a <acronym title="Globally Unique Identifier">GUID</acronym>
-is required, create a large, unique number and store it. Do not
-use phone identifiers such as the phone number or IMEI which may be associated
+is required, create a large, unique number and store it. Don't
+use phone identifiers such as the phone number or IMEI, which may be associated
with personal information. This topic is discussed in more detail in the <a
-href="http://android-developers.blogspot.com/2011/03/identifying-app-installations.html">Android
-Developer Blog</a>.</p>
+href="http://android-developers.blogspot.com/2011/03/identifying-app-installations.html"
+>Android Developer Blog</a>.</p>
<p>Be careful when writing to on-device logs.
-In Android, logs are a shared resource, and are available
+In Android, logs are a shared resource and are available
to an application with the {@link android.Manifest.permission#READ_LOGS} permission.
Even though the phone log data
is temporary and erased on reboot, inappropriate logging of user information
@@ -414,19 +419,23 @@
<h2 id="WebView">Using WebView</h2>
-<p>Because {@link android.webkit.WebView} consumes web content that can include HTML and JavaScript,
+<p>Because {@link android.webkit.WebView} consumes web content that can include HTML
+ and JavaScript,
improper use can introduce common web security issues such as <a
-href="http://en.wikipedia.org/wiki/Cross_site_scripting">cross-site-scripting</a>
+href="http://en.wikipedia.org/wiki/Cross_site_scripting" class="external-link">
+cross-site-scripting</a>
(JavaScript injection). Android includes a number of mechanisms to reduce
-the scope of these potential issues by limiting the capability of {@link android.webkit.WebView} to
+the scope of these potential issues by limiting the capability of
+ {@link android.webkit.WebView} to
the minimum functionality required by your application.</p>
-<p>If your application does not directly use JavaScript within a {@link android.webkit.WebView}, do
-<em>not</em> call {@link android.webkit.WebSettings#setJavaScriptEnabled setJavaScriptEnabled()}.
+<p>If your application doesn't directly use JavaScript within a {@link android.webkit.WebView},
+ <em>do not</em> call
+ {@link android.webkit.WebSettings#setJavaScriptEnabled setJavaScriptEnabled()}.
Some sample code uses this method, which you might repurpose in production
application, so remove that method call if it's not required. By default,
{@link android.webkit.WebView} does
-not execute JavaScript so cross-site-scripting is not possible.</p>
+not execute JavaScript, so cross-site-scripting is not possible.</p>
<p>Use {@link android.webkit.WebView#addJavascriptInterface
addJavaScriptInterface()} with
@@ -441,55 +450,55 @@
<p>If your application accesses sensitive data with a
{@link android.webkit.WebView}, you may want to use the
{@link android.webkit.WebView#clearCache clearCache()} method to delete any files stored
-locally. Server-side
-headers like <code>no-cache</code> can also be used to indicate that an application should
+locally. You can also use server-side
+headers such as <code>no-cache</code> to indicate that an application should
not cache particular content.</p>
<p>Devices running platforms older than Android 4.4 (API level 19)
use a version of {@link android.webkit webkit} that has a number of security issues.
As a workaround, if your app is running on these devices, it
-should confirm that {@link android.webkit.WebView} objects display only trusted
-content. You should also use the updatable security {@link
-java.security.Provider Provider} object to make sure your app isn’t exposed to
-potential vulnerabilities in SSL, as described in <a
+must confirm that {@link android.webkit.WebView} objects display only trusted
+content. To make sure your app isn’t exposed to
+potential vulnerabilities in SSL, use the updatable security {@link
+java.security.Provider Provider} object as described in <a
href="{@docRoot}training/articles/security-gms-provider.html">Updating Your
Security Provider to Protect Against SSL Exploits</a>. If your application must
render content from the open web, consider providing your own renderer so
you can keep it up to date with the latest security patches.</p>
-<h3 id="Credentials">Handling Credentials</h3>
+<h3 id="Credentials">Handling credentials</h3>
-<p>In general, we recommend minimizing the frequency of asking for user
-credentials—to make phishing attacks more conspicuous, and less likely to be
-successful. Instead use an authorization token and refresh it.</p>
+<p>To make phishing attacks more conspicuous and less likely to be
+successful, minimize the frequency of asking for user
+credentials. Instead use an authorization token and refresh it.</p>
-<p>Where possible, username and password should not be stored on the device.
-Instead, perform initial authentication using the username and password
-supplied by the user, and then use a short-lived, service-specific
+<p>Where possible, don't store user names and passwords on the device.
+Instead, perform initial authentication using the user name and password
+ supplied by the user, and then use a short-lived, service-specific
authorization token.</p>
-<p>Services that will be accessible to multiple applications should be accessed
+<p>Services that are accessible to multiple applications should be accessed
using {@link android.accounts.AccountManager}. If possible, use the
-{@link android.accounts.AccountManager} class to invoke a cloud-based service and do not store
+{@link android.accounts.AccountManager} class to invoke a cloud-based service and don't store
passwords on the device.</p>
<p>After using {@link android.accounts.AccountManager} to retrieve an
-{@link android.accounts.Account}, {@link android.accounts.Account#CREATOR}
-before passing in any credentials, so that you do not inadvertently pass
+{@link android.accounts.Account}, use {@link android.accounts.Account#CREATOR}
+before passing in any credentials so that you do not inadvertently pass
credentials to the wrong application.</p>
-<p>If credentials are to be used only by applications that you create, then you
-can verify the application which accesses the {@link android.accounts.AccountManager} using
+<p>If credentials are used only by applications that you create, you
+can verify the application that accesses the {@link android.accounts.AccountManager} using
{@link android.content.pm.PackageManager#checkSignatures checkSignature()}.
-Alternatively, if only one application will use the credential, you might use a
+Alternatively, if only one application uses the credential, you might use a
{@link java.security.KeyStore} for storage.</p>
-<h2 id="Crypto">Using Cryptography</h2>
+<h2 id="Crypto">Using cryptography</h2>
<p>In addition to providing data isolation, supporting full-filesystem
encryption, and providing secure communications channels, Android provides a
@@ -500,21 +509,21 @@
retrieve a file from a known location, a simple HTTPS URI may be adequate and
requires no knowledge of cryptography. If you need a secure
tunnel, consider using {@link javax.net.ssl.HttpsURLConnection} or
-{@link javax.net.ssl.SSLSocket}, rather than writing your own protocol.</p>
+{@link javax.net.ssl.SSLSocket} rather than writing your own protocol.</p>
-<p>If you do find yourself needing to implement your own protocol, we strongly
-recommend that you <em>not</em> implement your own cryptographic algorithms. Use
+<p>If you do need to implement your own protocol, you should <em>not</em>
+implement your own cryptographic algorithms. Use
existing cryptographic algorithms such as those in the implementation of AES or
RSA provided in the {@link javax.crypto.Cipher} class.</p>
<p>Use a secure random number generator, {@link java.security.SecureRandom},
-to initialize any cryptographic keys, {@link javax.crypto.KeyGenerator}.
+to initialize any cryptographic keys generated by {@link javax.crypto.KeyGenerator}.
Use of a key that is not generated with a secure random
-number generator significantly weakens the strength of the algorithm, and may
+number generator significantly weakens the strength of the algorithm and may
allow offline attacks.</p>
-<p>If you need to store a key for repeated use, use a mechanism like
- {@link java.security.KeyStore} that
+<p>If you need to store a key for repeated use, use a mechanism, such as
+ {@link java.security.KeyStore}, that
provides a mechanism for long term storage and retrieval of cryptographic
keys.</p>
@@ -522,10 +531,10 @@
-<h2 id="IPC">Using Interprocess Communication</h2>
+<h2 id="IPC">Using interprocess communication</h2>
<p>Some apps attempt to implement IPC using traditional Linux
-techniques such as network sockets and shared files. We strongly encourage you to instead
+techniques such as network sockets and shared files. However, you should instead
use Android system functionality for IPC such as {@link android.content.Intent},
{@link android.os.Binder} or {@link android.os.Messenger} with a {@link
android.app.Service}, and {@link android.content.BroadcastReceiver}.
@@ -535,19 +544,19 @@
<p>Many of the security elements are shared across IPC mechanisms.
If your IPC mechanism is not intended for use by other applications, set the
-{@code android:exported} attribute to {@code "false"} in the component's manifest element,
+{@code android:exported} attribute to {@code false} in the component's manifest element,
such as for the <a
-href="{@docRoot}guide/topics/manifest/service-element.html#exported">{@code <service>}</a>
+href="{@docRoot}guide/topics/manifest/service-element.html#exported">{@code <service>}</a>
element. This is useful for applications that consist of multiple processes
-within the same UID, or if you decide late in development that you do not
-actually want to expose functionality as IPC but you don’t want to rewrite
+within the same UID or if you decide late in development that you don't
+actually want to expose functionality as IPC, but you don’t want to rewrite
the code.</p>
-<p>If your IPC is intended to be accessible to other applications, you can
+<p>If your IPC is accessible to other applications, you can
apply a security policy by using the <a
-href="{@docRoot}guide/topics/manifest/permission-element.html">{@code <permission>}</a>
+href="{@docRoot}guide/topics/manifest/permission-element.html">{@code <permission>}</a>
element. If IPC is between your own separate apps that are signed with the same key,
-it is preferable to use {@code "signature"} level permission in the <a
+it is preferable to use {@code signature} level permission in the <a
href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">{@code
android:protectionLevel}</a>.</p>
@@ -556,31 +565,42 @@
<h3>Using intents</h3>
-<p>Intents are the preferred mechanism for asynchronous IPC in Android.
+<p>For activities and broadcast receivers, intents are the preferred mechanism for
+ asynchronous IPC in Android.
Depending on your application requirements, you might use {@link
android.content.Context#sendBroadcast sendBroadcast()}, {@link
android.content.Context#sendOrderedBroadcast sendOrderedBroadcast()},
or an explicit intent to a specific application component.</p>
-<p>Note that ordered broadcasts can be “consumed” by a recipient, so they
-may not be delivered to all applications. If you are sending an intent that must be delivered
-to a specific receiver, then you must use an explicit intent that declares the receiver
-by nameintent.</p>
+<p class="caution"><strong>Caution:</strong> If you use an intent to bind to a
+ {@link android.app.Service}, ensure that your app is secure by using an
+ <a href="{@docRoot}guide/components/intents-filters.html#Types">explicit</a>
+intent. Using an implicit intent to start a service is a
+security hazard because you can't be certain what service will respond to the intent,
+and the user can't see which service starts. Beginning with Android 5.0 (API level 21),
+ the system
+throws an exception if you call {@link android.content.Context#bindService bindService()}
+with an implicit intent.</p>
-<p>Senders of an intent can verify that the recipient has a permission
-specifying a non-Null permission with the method call. Only applications with that
-permission will receive the intent. If data within a broadcast intent may be
+<p>Note that ordered broadcasts can be <em>consumed</em> by a recipient, so they
+may not be delivered to all applications. If you are sending an intent that must be delivered
+to a specific receiver, you must use an explicit intent that declares the receiver
+by name.</p>
+
+<p>Senders of an intent can verify that the recipient has permission
+ by specifying a non-null permission with the method call. Only applications with that
+permission receive the intent. If data within a broadcast intent may be
sensitive, you should consider applying a permission to make sure that
-malicious applications cannot register to receive those messages without
-appropriate permissions. In those circumstances, you may also consider
+malicious applications can't register to receive those messages without
+appropriate permissions. In those circumstances, you may also consider
invoking the receiver directly, rather than raising a broadcast.</p>
<p class="note"><strong>Note:</strong> Intent filters should not be considered
-a security feature—components
+a security feature. Components
can be invoked with explicit intents and may not have data that would conform to the intent
-filter. You should perform input validation within your intent receiver to
+filter. To
confirm that it is properly formatted for the invoked receiver, service, or
-activity.</p>
+activity, perform input validation within your intent receiver.</p>
@@ -589,26 +609,32 @@
<p>A {@link android.app.Service} is often used to supply functionality for other applications to
use. Each service class must have a corresponding <a
-href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> declaration in its
+href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a>
+ declaration in its
manifest file.</p>
<p>By default, services are not exported and cannot be invoked by any other
-application. However, if you add any intent filters to the service declaration, then it is exported
+application. However, if you add any intent filters to the service declaration, it is exported
by default. It's best if you explicitly declare the <a
href="{@docRoot}guide/topics/manifest/service-element.html#exported">{@code
android:exported}</a> attribute to be sure it behaves as you'd like.
Services can also be protected using the <a
href="{@docRoot}guide/topics/manifest/service-element.html#prmsn">{@code android:permission}</a>
-attribute. By doing so, other applications will need to declare
+attribute. By doing so, other applications need to declare
a corresponding <code><a
href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a>
</code> element in their own manifest to be
able to start, stop, or bind to the service.</p>
+<p class="note"><strong>Note:</strong> If your app targets Android 5.0 (API level 21) or later,
+ you should use the {@link android.app.job.JobScheduler} to execute background
+ services. For more information about {@link android.app.job.JobScheduler}, see its
+ {@link android.app.job.JobScheduler API-reference documentation}.</p>
+
<p>A service can protect individual IPC calls into it with permissions, by
calling {@link android.content.Context#checkCallingPermission
checkCallingPermission()} before executing
-the implementation of that call. We generally recommend using the
+the implementation of that call. You should use the
declarative permissions in the manifest, since those are less prone to
oversight.</p>
@@ -620,24 +646,24 @@
preferred mechanism for RPC-style IPC in Android. They provide a well-defined
interface that enables mutual authentication of the endpoints, if required.</p>
-<p>We strongly encourage designing interfaces in a manner that does not require
-interface specific permission checks. {@link android.os.Binder} and
+<p>You should design your app interfaces in a manner that does not require
+interface-specific permission checks. {@link android.os.Binder} and
{@link android.os.Messenger} objects are not declared within the
application manifest, and therefore you cannot apply declarative permissions
directly to them. They generally inherit permissions declared in the
application manifest for the {@link android.app.Service} or {@link
android.app.Activity} within which they are
implemented. If you are creating an interface that requires authentication
-and/or access controls, those controls must be
-explicitly added as code in the {@link android.os.Binder} or {@link android.os.Messenger}
+and/or access controls, you must explicitly add those controls
+ as code in the {@link android.os.Binder} or {@link android.os.Messenger}
interface.</p>
-<p>If providing an interface that does require access controls, use {@link
+<p>If you are providing an interface that does require access controls, use {@link
android.content.Context#checkCallingPermission checkCallingPermission()}
to verify whether the
caller has a required permission. This is especially important
before accessing a service on behalf of the caller, as the identify of your
-application is passed to other interfaces. If invoking an interface provided
+application is passed to other interfaces. If you are invoking an interface provided
by a {@link android.app.Service}, the {@link
android.content.Context#bindService bindService()}
invocation may fail if you do not have permission to access the given service.
@@ -660,8 +686,8 @@
is intended for use by other applications, you
may want to apply security permissions to receivers using the <code><a
href="{@docRoot}guide/topics/manifest/receiver-element.html">
-<receiver></a></code> element within the application manifest. This will
-prevent applications without appropriate permissions from sending an intent to
+<receiver></a></code> element within the application manifest. This
+prevents applications without appropriate permissions from sending an intent to
the {@link android.content.BroadcastReceiver}.</p>
@@ -671,57 +697,58 @@
-<h2 id="DynamicCode">Dynamically Loading Code</h2>
+<h2 id="DynamicCode">Dynamically loading code</h2>
<p>We strongly discourage loading code from outside of your application APK.
Doing so significantly increases the likelihood of application compromise due
-to code injection or code tampering. It also adds complexity around version
-management and application testing. Finally, it can make it impossible to
+to code injection or code tampering. It also adds complexity around version
+management and application testing. It can also make it impossible to
verify the behavior of an application, so it may be prohibited in some
environments.</p>
<p>If your application does dynamically load code, the most important thing to
-keep in mind about dynamically loaded code is that it runs with the same
-security permissions as the application APK. The user made a decision to
-install your application based on your identity, and they are expecting that
+keep in mind about dynamically-loaded code is that it runs with the same
+security permissions as the application APK. The user makes a decision to
+install your application based on your identity, and the user expects that
you provide any code run within the application, including code that is
dynamically loaded.</p>
<p>The major security risk associated with dynamically loading code is that the
code needs to come from a verifiable source. If the modules are included
-directly within your APK, then they cannot be modified by other applications.
+directly within your APK, they cannot be modified by other applications.
This is true whether the code is a native library or a class being loaded using
-{@link dalvik.system.DexClassLoader}. We have seen many instances of applications
-attempting to load code from insecure locations, such as downloaded from the
-network over unencrypted protocols or from world writable locations such as
+{@link dalvik.system.DexClassLoader}. Many applications
+attempt to load code from insecure locations, such as downloaded from the
+network over unencrypted protocols or from world-writable locations such as
external storage. These locations could allow someone on the network to modify
-the content in transit, or another application on a users device to modify the
-content on the device, respectively.</p>
+the content in transit or another application on a user's device to modify the
+content on the device.</p>
-<h2 id="Dalvik">Security in a Virtual Machine</h2>
+<h2 id="Dalvik">Security in a virtual machine</h2>
<p>Dalvik is Android's runtime virtual machine (VM). Dalvik was built specifically for Android,
but many of the concerns regarding secure code in other virtual machines also apply to Android.
In general, you shouldn't concern yourself with security issues relating to the virtual machine.
-Your application runs in a secure sandbox environment, so other processes on the system cannnot
+Your application runs in a secure sandbox environment, so other processes on the system can't
access your code or private data.</p>
-<p>If you're interested in diving deeper on the subject of virtual machine security,
-we recommend that you familiarize yourself with some
+<p>If you're interested in learning more about virtual machine security,
+ familiarize yourself with some
existing literature on the subject. Two of the more popular resources are:
<ul>
-<li><a href="http://www.securingjava.com/toc.html">
-http://www.securingjava.com/toc.html</a></li>
+<li><a href="http://www.securingjava.com/toc.html" class="external-link">
+Securing Java</a></li>
<li><a
-href="https://www.owasp.org/index.php/Java_Security_Resources">
-https://www.owasp.org/index.php/Java_Security_Resources</a></li>
+href="https://www.owasp.org/index.php/Category:Java#tab=Related_3rd_Party_Projects"
+ class="external-link">
+Related 3rd party Projects</a></li>
</ul></p>
-<p>This document is focused on the areas which are Android specific or
+<p>This document focuses on areas that are Android specific or
different from other VM environments. For developers experienced with VM
programming in other environments, there are two broad issues that may be
different about writing apps for Android:
@@ -742,21 +769,19 @@
-<h2 id="Native">Security in Native Code</h2>
+<h2 id="Native">Security in native code</h2>
-<p>In general, we encourage developers to use the Android SDK for
+<p>In general, you should use the Android SDK for
application development, rather than using native code with the
<a href="{@docRoot}tools/sdk/ndk/index.html">Android NDK</a>. Applications built
with native code are more complex, less portable, and more like to include
-common memory corruption errors such as buffer overflows.</p>
+common memory-corruption errors such as buffer overflows.</p>
-<p>Android is built using the Linux kernel and being familiar with Linux
-development security best practices is especially useful if you are going to
-use native code. Linux security practices are beyond the scope of this document,
-but one of the most popular resources is “Secure Programming for
-Linux and Unix HOWTO”, available at <a
-href="http://www.dwheeler.com/secure-programs">
-http://www.dwheeler.com/secure-programs</a>.</p>
+<p>Android is built using the Linux kernel, and being familiar with Linux
+development security best practices is especially useful if you are
+using native code. Linux security practices are beyond the scope of this document,
+but one of the most popular resources is <a href="http://www.dwheeler.com/secure-programs"
+ class="external-link">Secure Programming HOWTO - Creating Secure Software</a>.</p>
<p>An important difference between Android and most Linux environments is the
Application Sandbox. On Android, all applications run in the Application
@@ -765,6 +790,5 @@
every application is given a unique <acronym title="User Identifier">UID</acronym>
with very limited permissions. This is discussed in more detail in the <a
href="http://source.android.com/tech/security/index.html">Android Security
-Overview</a> and you should be familiar with application permissions even if
+Overview</a>, and you should be familiar with application permissions even if
you are using native code.</p>
-
diff --git a/docs/html/training/location/display-address.jd b/docs/html/training/location/display-address.jd
index daa6fd3..088e926 100644
--- a/docs/html/training/location/display-address.jd
+++ b/docs/html/training/location/display-address.jd
@@ -7,11 +7,11 @@
<h2>This lesson teaches you how to</h2>
<ol>
- <li><a href="#connect">Get a Geographic Location</a></li>
- <li><a href="#fetch-address">Define an Intent Service to Fetch the
- Address</a></li>
- <li><a href="#start-intent">Start the Intent Service</a></li>
- <li><a href="#result-receiver">Receive the Geocoding Results</a></li>
+ <li><a href="#connect">Get a geographic location</a></li>
+ <li><a href="#fetch-address">Define an intent service to fetch the
+ address</a></li>
+ <li><a href="#start-intent">Start the intent service</a></li>
+ <li><a href="#result-receiver">Receive the geocoding results</a></li>
</ol>
<h2>You should also read</h2>
@@ -58,7 +58,7 @@
convert a geographic location to an address. The method returns an estimated
street address corresponding to a given latitude and longitude.</p>
-<h2 id="connect">Get a Geographic Location</h2>
+<h2 id="connect">Get a geographic location</h2>
<p>The last known location of the device is a useful starting point for the
address lookup feature. The lesson on
@@ -69,12 +69,12 @@
<a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html">fused
location provider</a> to find the latest location of the device.</p>
-<p>To access the fused location provider, you need to create an instance of the
+<p>To access the fused location provider, create an instance of the
Google Play services API client. To learn how to connect your client, see
<a href="{@docRoot}training/location/retrieve-current.html#play-services">Connect
to Google Play Services</a>.</p>
-<p>In order for the fused location provider to retrieve a precise street
+<p>To enable the fused location provider to retrieve a precise street
address, set the location permission in your app manifest to
{@code ACCESS_FINE_LOCATION}, as shown in the following example:</p>
@@ -86,12 +86,12 @@
</manifest>
</pre>
-<h2 id="fetch-address">Define an Intent Service to Fetch the Address</h2>
+<h2 id="fetch-address">Define an intent service to fetch the address</h2>
<p>The {@link android.location.Geocoder#getFromLocation getFromLocation()}
method provided by the {@link android.location.Geocoder} class accepts a
- latitude and longitude, and returns a list of addresses. The method is
- synchronous, and may take a long time to do its work, so you should not call
+ latitude and longitude and returns a list of addresses. The method is
+ synchronous and may take a long time to do its work, so you should not call
it from the main, user interface (UI) thread of your app.</p>
<p>The {@link android.app.IntentService IntentService} class provides a
@@ -100,23 +100,23 @@
Note that the {@link android.os.AsyncTask AsyncTask} class also allows you to
perform background operations, but it's designed for short operations. An
{@link android.os.AsyncTask AsyncTask} shouldn't keep a reference to the UI if
- the activity is recreated, for example when the device is rotated. In
+ the activity is re-created, such as when the device is rotated. In
contrast, an {@link android.app.IntentService IntentService} doesn't need to
be cancelled when the activity is rebuilt.</p>
<p>Define a {@code FetchAddressIntentService} class that extends
{@link android.app.IntentService}. This class is your address lookup service.
- The intent service handles an intent asynchronously on a worker thread, and
+ The intent service handles an intent asynchronously on a worker thread and
stops itself when it runs out of work. The intent extras provide the data
needed by the service, including a {@link android.location.Location} object
- for conversion to an address, and a {@link android.os.ResultReceiver} object
+ for conversion to an address and a {@link android.os.ResultReceiver} object
to handle the results of the address lookup. The service uses a {@link
- android.location.Geocoder} to fetch the address for the location, and sends
+ android.location.Geocoder} to fetch the address for the location and sends
the results to the {@link android.os.ResultReceiver}.</p>
-<h3>Define the Intent Service in your App Manifest</h3>
+<h3>Define the intent service in your app manifest</h3>
-<p>Add an entry to your app manifest defining the intent service:</p>
+<p>Add an entry to your app manifest that defines the intent service, as shown here:</p>
<pre>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -131,26 +131,26 @@
</manifest>
</pre>
-<p class="note"><strong>Note:</strong> The {@code <service>} element in
- the manifest doesn't need to include an intent filter, because your main
+<p class="note"><strong>Note:</strong> The {@code <service>} element in
+ the manifest doesn't need to include an intent filter because your main
activity creates an explicit intent by specifying the name of the class to use
for the intent.</p>
-<h3>Create a Geocoder</h3>
+<h3>Create a geocoder</h3>
<p>The process of converting a geographic location to an address is called
- <em>reverse geocoding</em>. To perform the main work of the intent service,
- that is, your reverse geocoding request, implement
+ <em>reverse geocoding</em>. To perform the main work of the intent service (your reverse
+ geocoding request), implement
{@link android.app.IntentService#onHandleIntent onHandleIntent()} within the
{@code FetchAddressIntentService} class. Create a
{@link android.location.Geocoder} object to handle the reverse geocoding.</p>
<p>A locale represents a specific geographical or linguistic region. Locale
- objects are used to adjust the presentation of information, such as numbers or
- dates, to suit the conventions in the region represented by the locale. Pass a
+ objects adjust the presentation of information, such as numbers or
+ dates, to suit the conventions in the region that is represented by the locale. Pass a
<a href="{@docRoot}reference/java/util/Locale.html">{@code Locale}</a> object
- to the {@link android.location.Geocoder} object, to ensure that the resulting
- address is localized to the user's geographic region.</p>
+ to the {@link android.location.Geocoder} object to ensure that the resulting
+ address is localized to the user's geographic region. Here is an example:</p>
<pre>
@Override
@@ -162,7 +162,7 @@
<h3 id="retrieve-street-address">Retrieve the street address data</h3>
-<p>The next step is to retrieve the street address from the geocoder, handle
+<p>You can now retrieve the street address from the geocoder, handle
any errors that may occur, and send the results back to the activity that
requested the address. To report the results of the geocoding
process, you need two numeric constants that indicate success or failure.
@@ -185,32 +185,34 @@
<p>To get a street address corresponding to a geographical location, call
{@link android.location.Geocoder#getFromLocation getFromLocation()},
- passing it the latitude and longitude from the location object, and the
- maximum number of addresses you want returned. In this case, you want just one
- address. The geocoder returns an array of addresses. If no addresses were
+ passing it the latitude and longitude from the location object and the
+ maximum number of addresses that you want returned. In this case, you want just one
+ address. The geocoder returns an array of addresses. If no addresses are
found to match the given location, it returns an empty list. If there is no
backend geocoding service available, the geocoder returns null.</p>
-<p>Check for the following errors as shown in the code sample below. If an error
- occurs, place the corresponding error message in the {@code errorMessage}
- variable, so you can send it back to the requesting activity:</p>
+<p>Check for the following errors, as shown in the code sample below:</p>
<ul>
- <li><strong>No location data provided</strong> - The intent extras do not
- include the {@link android.location.Location} object required for reverse
+ <li><strong>No location data provided</strong> – The intent extras do not
+ include the {@link android.location.Location} object that is required for reverse
geocoding.</li>
- <li><strong>Invalid latitude or longitude used</strong> - The latitude
- and/or longitude values provided in the {@link android.location.Location}
+ <li><strong>Invalid latitude or longitude used</strong> – The latitude
+ and/or longitude values that are provided in the {@link android.location.Location}
object are invalid.</li>
- <li><strong>No geocoder available</strong> - The background geocoding service
- is not available, due to a network error or IO exception.</li>
- <li><strong>Sorry, no address found</strong> - The geocoder could not find an
+ <li><strong>No geocoder available</strong> – The background geocoding service
+ is not available due to a network error or IO exception.</li>
+ <li><strong>Sorry, no address found</strong> – The geocoder can't find an
address for the given latitude/longitude.</li>
</ul>
+<p>If an error
+ occurs, place the corresponding error message in the {@code errorMessage}
+ variable so that you can send it back to the requesting activity.
+
<p>To get the individual lines of an address object, use the
{@link android.location.Address#getAddressLine getAddressLine()}
- method provided by the {@link android.location.Address} class. Then join the
+ method that is provided by the {@link android.location.Address} class. Join the
lines into a list of address fragments ready to return to the activity that
requested the address.</p>
@@ -220,7 +222,7 @@
results consist of the previously-mentioned numeric success/failure code and
a string. In the case of a successful reverse geocoding, the string contains
the address. In the case of a failure, the string contains the error message,
- as shown in the code sample below:</p>
+ as shown in this code sample:</p>
<pre>
@Override
@@ -280,18 +282,18 @@
<h3 id="return-address">Return the address to the requestor</h3>
-<p>The final thing the intent service must do is send the address back to a
+<p>The final action that the intent service must complete is sending the address back to a
{@link android.os.ResultReceiver} in the activity that started the service.
The {@link android.os.ResultReceiver} class allows you to send a
numeric result code as well as a message containing the result data. The
numeric code is useful for reporting the success or failure of the geocoding
request. In the case of a successful reverse geocoding, the message contains
the address. In the case of a failure, the message contains some text
- describing the reason for failure.</p>
+ describing the reason for the failure.</p>
<p>You have already retrieved the address from the geocoder, trapped any errors
- that may occur, and called the {@code deliverResultToReceiver()} method. Now
- you need to define the {@code deliverResultToReceiver()} method that sends
+ that may occur, and called the {@code deliverResultToReceiver()} method, so now
+ you must define the {@code deliverResultToReceiver()} method that sends
a result code and message bundle to the result receiver.</p>
<p>For the result code, use the value that you've passed to the
@@ -299,7 +301,7 @@
To construct the message bundle, concatenate the {@code RESULT_DATA_KEY}
constant from your {@code Constants} class (defined in
<a href="#retrieve-street-address">Retrieve the street address data</a>) and
- the value in the {@code message} parameter passed to the
+ the value in the {@code message} parameter that is passed to the
{@code deliverResultToReceiver()} method, as shown in the following sample:
</p>
@@ -315,26 +317,26 @@
}
</pre>
-<h2 id="start-intent">Start the Intent Service</h2>
+<h2 id="start-intent">Start the intent service</h2>
<p>The intent service, as defined in the previous section, runs in the
- background and is responsible for fetching the address corresponding to a
+ background and fetches the address corresponding to a
given geographic location. When you start the service, the Android framework
- instantiates and starts the service if it isn't already running, and creates a
- process if needed. If the service is already running then it remains running.
+ instantiates and starts the service if it isn't already running, and it creates a
+ process if needed. If the service is already running, it remains running.
Because the service extends {@link android.app.IntentService IntentService},
- it shuts down automatically when all intents have been processed.</p>
+ it shuts down automatically after all intents are processed.</p>
-<p>Start the service from your app's main activity,
+<p>Start the service from your app's main activity
and create an {@link android.content.Intent} to pass data to the service. You
- need an <em>explicit</em> intent, because you want only your service
+ need an <em>explicit</em> intent because you want only your service
to respond to the intent. For more information, see
<a href="{@docRoot}guide/components/intents-filters.html#Types">Intent
Types</a>.</p>
<p>To create an explicit intent, specify the name of the
class to use for the service: {@code FetchAddressIntentService.class}.
- Pass two pieces of information in the intent extras:</p>
+ Pass this information in the intent extras:</p>
<ul>
<li>A {@link android.os.ResultReceiver} to handle the results of the address
@@ -362,6 +364,12 @@
}
</pre>
+<p class="caution"><strong>Caution</strong>: To ensure that your app is secure, always use an
+explicit intent when starting a {@link android.app.Service} and do not declare intent filters for
+your services. Using an implicit intent to start a service is a security hazard because you cannot
+be certain of the service that will respond to the intent, and the user cannot see which service
+starts.</p>
+
<p>Call the above {@code startIntentService()} method when the
user takes an action that requires a geocoding address lookup. For example,
the user may press a <em>Fetch address</em> button on your app's UI. Before
@@ -391,7 +399,7 @@
app's UI. The following code snippet shows the call to the
{@code startIntentService()} method in the
<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">{@code onConnected()}</a>
- callback provided by the Google API Client:</p>
+ callback that is provided by the Google API Client:</p>
<pre>
public class MainActivity extends ActionBarActivity implements
@@ -420,9 +428,9 @@
}
</pre>
-<h2 id="result-receiver">Receive the Geocoding Results</h2>
+<h2 id="result-receiver">Receive the geocoding results</h2>
-<p>The intent service has handled the geocoding request, and uses a
+<p>After the intent service handles the geocoding request, it uses a
{@link android.os.ResultReceiver} to return the results to the activity that
made the request. In the activity that makes the request, define an
{@code AddressResultReceiver} that extends {@link android.os.ResultReceiver}
@@ -430,14 +438,14 @@
<p>The result includes a numeric result code (<code>resultCode</code>) as well
as a message containing the result data (<code>resultData</code>). If the
- reverse geocoding process was successful, the <code>resultData</code> contains
+ reverse geocoding process is successful, the <code>resultData</code> contains
the address. In the case of a failure, the <code>resultData</code> contains
- text describing the reason for failure. For details of the possible errors,
+ text describing the reason for the failure. For details of the possible errors,
see <a href="#return-address">Return the address to the requestor</a>.</p>
<p>Override the
{@link android.os.ResultReceiver#onReceiveResult onReceiveResult()} method
- to handle the results delivered to the result receiver, as shown in the
+ to handle the results that are delivered to the result receiver, as shown in the
following code sample:</p>
<pre>
diff --git a/docs/html/training/location/geofencing.jd b/docs/html/training/location/geofencing.jd
index ce6ad55..046e99e 100644
--- a/docs/html/training/location/geofencing.jd
+++ b/docs/html/training/location/geofencing.jd
@@ -332,22 +332,39 @@
<p>This section outlines recommendations for using geofencing with the location
APIs for Android.</p>
-<h3>Reduce power consumption</h3>
+<h3>
+ Reduce power consumption
+</h3>
-<p>You can use the following techniques to optimize power consumption in your apps that use geofencing:</p>
+<p>
+ You can use the following techniques to optimize power consumption in your
+ apps that use geofencing:
+</p>
<ul>
-<li><p>Set the <a href="{@docRoot}android/reference/com/google/android/gms/location/Geofence.Builder.html#setNotificationResponsiveness(int)">
-notification responsiveness</a> to a higher value. Doing so improves power consumption by
-increasing the latency of geofence alerts. For example, if you set a responsiveness value of five
-minutes your app only checks for an entrance or exit alert once every five minutes.
-Setting lower values does not necessarily mean that users will be notified within that time period
-(for example, if you set a value of 5 seconds it may take a bit longer than that to receive the
-alert).</p></li>
-<li><p>Use a larger geofence radius for locations where a user spends a significant amount of time,
-such as home or work. While a larger radius doesn't directly reduce power consumption, it reduces
-the frequency at which the app checks for entrance or exit, effectively lowering overall power
-consumption.</p></li>
+ <li>
+ <p>
+ Set the <a href=
+ "https://developers.google.com/android/reference/com/google/android/gms/location/Geofence.Builder.html#setNotificationResponsiveness(int)">
+ notification responsiveness</a> to a higher value. Doing so improves
+ power consumption by increasing the latency of geofence alerts. For
+ example, if you set a responsiveness value of five minutes your app only
+ checks for an entrance or exit alert once every five minutes. Setting
+ lower values does not necessarily mean that users will be notified
+ within that time period (for example, if you set a value of 5 seconds it
+ may take a bit longer than that to receive the alert).
+ </p>
+ </li>
+
+ <li>
+ <p>
+ Use a larger geofence radius for locations where a user spends a
+ significant amount of time, such as home or work. While a larger radius
+ doesn't directly reduce power consumption, it reduces the frequency at
+ which the app checks for entrance or exit, effectively lowering overall
+ power consumption.
+ </p>
+ </li>
</ul>
<h3>Choose the optimal radius for your geofence</h3>
diff --git a/docs/html/training/run-background-service/index.jd b/docs/html/training/run-background-service/index.jd
index 22f3fc8..c48c681 100644
--- a/docs/html/training/run-background-service/index.jd
+++ b/docs/html/training/run-background-service/index.jd
@@ -35,16 +35,22 @@
<!-- ------------------------------------------------------------------------------------------- -->
<p>
Unless you specify otherwise, most of the operations you do in an app run in the foreground on
- a special thread called the UI thread. This can cause problems, because long-running operations
- will interfere with the responsiveness of your user interface. This annoys your users, and can
+ a special thread called the UI thread. Long-running foreground operations can cause problems
+ and interfere with the responsiveness of your user interface, which annoys your users and can
even cause system errors. To avoid this, the Android framework offers several classes that
- help you off-load operations onto a separate thread running in the background. The most useful
- of these is {@link android.app.IntentService}.
+ help you off-load operations onto a separate thread that runs in the background. The most
+ useful of these is {@link android.app.IntentService}.
</p>
<p>
This class describes how to implement an {@link android.app.IntentService}, send it work
requests, and report its results to other components.
</p>
+
+<p class="note"><strong>Note:</strong> If your app targets Android 5.0 (API level 21),
+ you should use {@link android.app.job.JobScheduler} to execute background
+ services. For more information about this class,
+ see the {@link android.app.job.JobScheduler} reference documentation.</p>
+
<h2>Lessons</h2>
<dl>
<dt>
diff --git a/docs/html/training/tv/tif/tvinput.jd b/docs/html/training/tv/tif/tvinput.jd
index 1a53398..2153ef8 100644
--- a/docs/html/training/tv/tif/tvinput.jd
+++ b/docs/html/training/tv/tif/tvinput.jd
@@ -10,9 +10,8 @@
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
- <li><a href="#manifest">Declare Your TV Input Service in the Manifest</a></li>
- <li><a href="#tvinput">Define Your TV Input Service</a></li>
- <li><a href="#setup">Define Your Setup Activity</a></li>
+ <li><a href="#TIFCompanion">Create a TV Input Service Using the TIF Companion Library</a></li>
+ <li><a href="#NoTIFCompanion">Create a TV Input Service Using the TIF Framework</a></li>
</ol>
<h2>You should also read</h2>
<ul>
@@ -30,14 +29,253 @@
</div>
<p>A TV input service represents a media stream source, and lets you present your media content in a
-linear, broadcast TV fashion as channels and programs. With the TV input service, you can provide
+linear, broadcast TV fashion as channels and programs. With a TV input service, you can provide
parental controls, program guide information, and content ratings. The TV input service works
-with the Android system TV app, developed for the device and immutable by third-party apps, which
-ultimately controls and presents content on the TV. See
+with the Android system TV app. This app ultimately controls and presents channel content
+on the TV. The system TV app is developed specifically for the device and immutable
+by third-party apps. For more information about the TV Input Framework (TIF)
+architecture and its components, see
<a class="external-link" href="http://source.android.com/devices/tv/index.html">
-TV Input Framework</a> for more information about the framework architecture and its components.</p>
+TV Input Framework</a>.</p>
-<p>To develop a TV input service, you implement the following components:</p>
+<h2 id="TIFCompanion">Create a TV Input Service Using the TIF Companion Library</h2>
+
+<p>
+The TIF Companion Library is a framework that provides extensible
+implementations of common TV input service features. Use the TIF Companion
+Library to quickly and easily create your own TV input service that follows
+best practices for Android TV.
+</p>
+
+<h3 id="build">Update your build.gradle file</h3>
+
+<p>
+To get started using the TIF Companion Library, add the following line to your
+app's <code>build.gradle</code> file:
+</p>
+
+<pre>
+compile 'com.google.android.media.tv.companionlibrary:1.0.0'
+</pre>
+
+<p>The TIF Companion Library is not currently part of the Android
+framework. It is distributed as part of the <a class="external-link"
+href="https://github.com/googlesamples/androidtv-sample-inputs">
+TV Input Service sample app</a>, and not with the Android SDK.
+</p>
+
+<h3 id="manifest">Declare your TV input service in the manifest</h3>
+
+<p>Your app must provide a {@link android.media.tv.TvInputService}-compatible
+service that the system uses to access your app. The TIF
+Companion Library provides the <code>BaseTvInputService</code> class, which
+provides a default implementation of {@link android.media.tv.TvInputService}
+that you can customize. Create a subclass of <code>BaseTvInputService</code>,
+and declare the subclass in your manifest as a service.</p>
+
+<p>Within the manifest declaration, specify the
+{@link android.Manifest.permission#BIND_TV_INPUT} permission to allow the
+service to connect the TV input to the system. A system service
+performs the binding and has the
+{@link android.Manifest.permission#BIND_TV_INPUT} permission.
+The system TV app sends requests to TV input services
+via the {@link android.media.tv.TvInputManager} interface.</p>
+
+<p>In your service declaration, include an intent filter that specifies
+{@link android.media.tv.TvInputService} as the action to perform with the
+intent. Also declare the service metadata as a separate XML resource. The
+service declaration, intent filter, and service metadata declaration are shown
+in the following example:</p>
+
+<pre>
+<service android:name=".rich.RichTvInputService"
+ android:label="@string/rich_input_label"
+ android:permission="android.permission.BIND_TV_INPUT">
+ <!-- Required filter used by the system to launch our account service. -->
+ <intent-filter>
+ <action android:name="android.media.tv.TvInputService" />
+ </intent-filter>
+ <!-- An XML file which describes this input. This provides pointers to
+ the RichTvInputSetupActivity to the system/TV app. -->
+ <meta-data
+ android:name="android.media.tv.input"
+ android:resource="@xml/richtvinputservice" />
+</service>
+</pre>
+
+<p>Define the service metadata in a separate XML file. The service
+metadata XML file must include a setup interface that describes the TV input's
+initial configuration and channel scan. The metadata file should also contain a
+flag stating whether or not users are able to record content. For more
+information on how to support recording content in your app, see
+<a href="{@docRoot}preview/features/tv-recording-api.html">TV Recording</a>.
+</p>
+
+<p>The service metadata file is located in the XML resources directory
+for your app and must match the name of the resource you declared in the
+manifest. Using the manifest entries from the previous example, you would
+create the XML file at <code>res/xml/richtvinputservice.xml</code>, with the
+following contents:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
+ android:canRecord="true"
+ android:setupActivity="com.example.android.sampletvinput.rich.RichTvInputSetupActivity" />
+</pre>
+
+<h3 id="setup">Define channels and create your setup activity</h3>
+
+<p>Your TV input service must define at least one channel that users
+access via the system TV app. You should register your channels
+in the system database, and provide a setup activity that the system
+invokes when it cannot find a channel for your app.</p>
+
+<p>First, enable your app to read from and write to the system Electronic
+Programming Guide (EPG), whose data includes channels and programs available
+to the user. To enable your app to perform these actions, and sync with the
+EPG after device restart, add the following elements to your app manifest:</p>
+
+<pre>
+<uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
+<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED "/>
+</pre>
+
+<p>Add the following element to ensure that your app shows up in the
+Google Play Store as an app that provides content channels in Android TV:</p>
+
+<pre>
+<uses-feature
+ android:name="android.software.live_tv"
+ android:required="true" />
+</pre>
+
+<p>Next, create a class which extends the <code>EpgSyncJobService</code>
+class. This abstract class makes it easy to create a job service that
+creates and updates channels in the system database.</p>
+
+<p>In your subclass, create and return your full list of channels in
+<code>getChannels()</code>. If your channels come from an XMLTV file,
+use the <code>XmlTvParser</code> class. Otherwise generate
+channels programmatically using the <code>Channel.Builder</code> class.
+</p>
+
+<p>For each channel, the system calls <code>getProgramsForChannel()</code>
+when it needs a list of programs that can be viewed within a given time window
+on the channel. Return a list of <code>Program</code> objects for the
+channel. Use the <code>XmlTvParser</code> class to obtain programs from an
+XMLTV file, or generate them programmatically using the
+<code>Program.Builder</code> class.</p>
+
+<p>For each <code>Program</code> object, use an
+<code>InternalProviderData</code> object to set program information such as the
+program's video type. If you only have a limited number of programs that you
+want the channel to repeat in a loop, use the
+<code>InternalProviderData.setRepeatable()</code> method with a value of
+<code>true</code> when setting information about your program.</p>
+
+<p>After you've implemented the job service, add it to your app manifest:</p>
+
+<pre>
+<service
+ android:name=".sync.SampleJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:exported="true" />
+</pre>
+
+<p>Finally, create a setup activity. Your setup activity should provide a way
+to sync channel and program data. One way to do this is for the user to do it
+via the UI in the activity. You might also have the app do it automatically
+when the activity starts. When the setup activity needs to sync channel and
+program info, the app should start the job service:</p>
+
+<pre>
+String inputId = getActivity().getIntent().getStringExtra(TvInputInfo.EXTRA_INPUT_ID);
+EpgSyncJobService.cancelAllSyncRequests(getActivity());
+EpgSyncJobService.requestImmediateSync(getActivity(), inputId,
+ new ComponentName(getActivity(), SampleJobService.class));
+</pre>
+
+<p>Use the <code>requestImmediateSync()</code> method to sync
+the job service. The user must wait for the sync to finish, so you should
+keep your request period relatively short.</p>
+
+<p>Use the <code>setUpPeriodicSync()</code> method to have the job service
+periodically sync channel and program data in the background:</p>
+
+<pre>
+EpgSyncJobService.setUpPeriodicSync(context, inputId,
+ new ComponentName(context, SampleJobService.class));
+</pre>
+
+<p>The TIF Companion Library provides an additional overloaded method of
+<code>requestImmediateSync()</code> that lets you specify the duration of
+channel data to sync in milliseconds. The default method syncs one hour's
+worth of channel data.
+</p>
+
+<p>The TIF Companion Library also provides an additional overloaded method of
+<code>setUpPeriodicSync()</code> that lets you specify the duration of
+channel data to sync, and how often the periodic sync should occur. The
+default method syncs 48 hours of channel data every 12 hours.
+</p>
+
+<p>For more details about channel data and the EPG, see
+<a href="{@docRoot}training/tv/tif/channel.html"> Working with Channel Data</a>.
+</p>
+
+<h3 id="playback">Handle tuning requests and media playback</h3>
+
+<p>When a user selects a specific channel, the system TV app uses a
+<code>Session</code>, created by your app, to tune to the requested channel
+and play content. The TIF Companion Library provides several
+classes you can extend to handle channel and session calls from the system.</p>
+
+<p>Your <code>BaseTvInputService</code> subclass creates sessions which handle
+tuning requests. Override the
+<code>onCreateSession()</code> method, create a session extended from
+the <code>BaseTvInputService.Session</code> class, and call
+<code>super.sessionCreated()</code> with your new session. In the following
+example, <code>onCreateSession()</code> returns a
+<code>RichTvInputSessionImpl</code> object that extends
+<code>BaseTvInputService.Session</code>:</p>
+
+<pre>
+@Override
+public final Session onCreateSession(String inputId) {
+ RichTvInputSessionImpl session = new RichTvInputSessionImpl(this, inputId);
+ session.setOverlayViewEnabled(true);
+ return super.sessionCreated(session);
+}
+</pre>
+
+<p>When the user uses the system TV app to start viewing one of your channels,
+the system calls your session's <code>onPlayChannel()</code> method. Override
+this method if you need to do any special channel initialization before the
+program starts playing.</p>
+
+<p>The system then obtains the currently scheduled program and calls your
+session's <code>onPlayProgram()</code> method, specifying the program
+information and start time in milliseconds. Use the
+<code>TvPlayer</code> interface to start playing the program.</p>
+
+<p>Your media player code should implement <code>TvPlayer</code> to handle
+specific playback events. The <code>TvPlayer</code> class handles features
+like time-shifting controls without adding complexity to your
+<code>BaseTvInputService</code> implementation.</p>
+
+<p>In your session's <code>getTvPlayer()</code> method, return
+your media player that implements <code>TvPlayer</code>. The
+<a class="external-link"
+href="https://github.com/googlesamples/androidtv-sample-inputs">
+TV Input Service sample app</a> implements a media player that uses
+<a href="{@docRoot}guide/topics/media/exoplayer.html">ExoPlayer</a>.</p>
+
+<h2 id="NoTIFCompanion">Create a TV Input Service Using the TIF Framework</h2>
+
+<p>If your TV input service can't use the TIF Companion Library, you need
+to implement the following components:</p>
<ul>
<li>{@link android.media.tv.TvInputService} provides long-running and background availability for
@@ -56,43 +294,18 @@
the interaction with TV inputs and apps</li>
</ul>
-<h2 id="manifest">Declare Your TV Input Service in the Manifest</h2>
+<p>You also need to do the following:</p>
-<p>Your app manifest must declare your {@link android.media.tv.TvInputService}. Within that
-declaration, specify the {@link android.Manifest.permission#BIND_TV_INPUT} permission to allow the
-service to connect the TV input to the system. A system service (<code>TvInputManagerService</code>)
-performs the binding and has that permission. The system TV app sends requests to TV input services
-via the {@link android.media.tv.TvInputManager} interface. The service declaration must also
-include an intent filter that specifies the {@link android.media.tv.TvInputService}
-as the action to perform with the intent. Also within the service declaration, declare the service
-meta data in a separate XML resource. The service declaration, the intent filter and the service
-meta data are described in the following example.</p>
+<ol>
+<li>Declare your TV input service in the manifest, as
+described in <a href="#manifest">Declare your TV input service in the
+manifest</a>.</li>
+<li>Create the service metadata file.</li>
+<li>Create and register your channel and program information.</li>
+<li>Create your setup activity.</li>
+</ol>
-<pre>
-<service android:name="com.example.sampletvinput.SampleTvInput"
- android:label="@string/sample_tv_input_label"
- android:permission="android.permission.BIND_TV_INPUT">
- <intent-filter>
- <action android:name="android.media.tv.TvInputService" />
- </intent-filter>
- <meta-data android:name="android.media.tv.input"
- android:resource="@xml/sample_tv_input" />
-</service>
-</pre>
-
-<p>Define the service meta data in separate XML file, as shown in the following example. The service
-meta data must include a setup interface that describes the TV input's initial configuration and
-channel scan. The service meta data file is located in the XML resources directory
-for your application and must match the name of the resource in the manifest. Using the example
-manifest entries above, you would create an XML file in the location
-<code>res/xml/sample_tv_input.xml</code>, with the following contents:</p>
-
-<pre>
-<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
- android:setupActivity="com.example.sampletvinput.SampleTvInputSetupActivity" />
-</pre>
-
-<h2 id="tvinput">Define Your TV Input Service</h2>
+<h3 id="tvinput">Define your TV input service</h3>
<div class="figure">
<img id="tvinputlife" src="{@docRoot}images/tv/tvinput-life.png" alt=""/>
@@ -102,7 +315,7 @@
<p>For your service, you extend the {@link android.media.tv.TvInputService} class. A
{@link android.media.tv.TvInputService} implementation is a
<a href="{@docRoot}guide/components/bound-services.html">bound service</a> where the system service
-(<code>TvInputManagerService</code>) is the client that binds to it. The service life cycle methods
+is the client that binds to it. The service life cycle methods
you need to implement are illustrated in figure 1.</p>
<p>The {@link android.app.Service#onCreate()} method initializes and starts the
@@ -145,7 +358,8 @@
<p>The {@link android.media.tv.TvInputService} creates a
{@link android.media.tv.TvInputService.Session} that implements {@link android.os.Handler.Callback}
-to handle player state changes. With {@link android.media.tv.TvInputService.Session#onSetSurface(android.view.Surface) onSetSurface()},
+to handle player state changes. With
+{@link android.media.tv.TvInputService.Session#onSetSurface(android.view.Surface) onSetSurface()},
the {@link android.media.tv.TvInputService.Session} sets the {@link android.view.Surface} with the
video content. See <a href="{@docRoot}training/tv/tif/ui.html#surface">Integrate Player with Surface</a>
for more information about working with {@link android.view.Surface} to render video.</p>
@@ -153,16 +367,16 @@
<p>The {@link android.media.tv.TvInputService.Session} handles the
{@link android.media.tv.TvInputService.Session#onTune(android.net.Uri) onTune()}
event when the user selects a channel, and notifies the system TV app for changes in the content and
-content meta data. These <code>notify()</code> methods are described in
+content metadata. These <code>notify()</code> methods are described in
<a href="{@docRoot}training/tv/tif/ui.html#control">
Control Content</a> and <a href="{@docRoot}training/tv/tif/ui.html#track">Handle Track Selection</a>
further in this training.</p>
-<h2 id="setup">Define Your Setup Activity</h2>
+<h3 id="setup">Define your setup activity</h3>
<p>The system TV app works with the setup activity you define for your TV input. The
setup activity is required and must provide at least one channel record for the system database. The
-system TV app will invoke the setup activity when it cannot find a channel for the TV input.
+system TV app invokes the setup activity when it cannot find a channel for the TV input.
<p>The setup activity describes to the system TV app the channels made available through the TV
input, as demonstrated in the next lesson, <a href="{@docRoot}training/tv/tif/channel.html">Creating
-and Updating Channel Data</a>.</p>
+and Updating Channel Data</a>.</p>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 3f0bed8..b093458 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -979,12 +979,11 @@
/**
* Draw the picture, stretched to fit into the dst rectangle.
*/
- public void drawPicture(@NonNull Picture picture, @NonNull Rect dst) {
+ public void drawPicture(@NonNull Picture picture, @NonNull RectF dst) {
save();
translate(dst.left, dst.top);
if (picture.getWidth() > 0 && picture.getHeight() > 0) {
- scale((float) dst.width() / picture.getWidth(),
- (float) dst.height() / picture.getHeight());
+ scale(dst.width() / picture.getWidth(), dst.height() / picture.getHeight());
}
drawPicture(picture);
restore();
@@ -993,11 +992,12 @@
/**
* Draw the picture, stretched to fit into the dst rectangle.
*/
- public void drawPicture(@NonNull Picture picture, @NonNull RectF dst) {
+ public void drawPicture(@NonNull Picture picture, @NonNull Rect dst) {
save();
translate(dst.left, dst.top);
if (picture.getWidth() > 0 && picture.getHeight() > 0) {
- scale(dst.width() / picture.getWidth(), dst.height() / picture.getHeight());
+ scale((float) dst.width() / picture.getWidth(),
+ (float) dst.height() / picture.getHeight());
}
drawPicture(picture);
restore();
@@ -1136,53 +1136,29 @@
float right, float bottom);
/**
- * <p>Draw the specified arc, which will be scaled to fit inside the
- * specified oval.</p>
+ * <p>
+ * Draw the specified arc, which will be scaled to fit inside the specified oval.
+ * </p>
+ * <p>
+ * If the start angle is negative or >= 360, the start angle is treated as start angle modulo
+ * 360.
+ * </p>
+ * <p>
+ * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
+ * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
+ * negative, the sweep angle is treated as sweep angle modulo 360
+ * </p>
+ * <p>
+ * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
+ * degrees (3 o'clock on a watch.)
+ * </p>
*
- * <p>If the start angle is negative or >= 360, the start angle is treated
- * as start angle modulo 360.</p>
- *
- * <p>If the sweep angle is >= 360, then the oval is drawn
- * completely. Note that this differs slightly from SkPath::arcTo, which
- * treats the sweep angle modulo 360. If the sweep angle is negative,
- * the sweep angle is treated as sweep angle modulo 360</p>
- *
- * <p>The arc is drawn clockwise. An angle of 0 degrees correspond to the
- * geometric angle of 0 degrees (3 o'clock on a watch.)</p>
- *
+ * @param oval The bounds of oval used to define the shape and size of the arc
* @param startAngle Starting angle (in degrees) where the arc begins
* @param sweepAngle Sweep angle (in degrees) measured clockwise
- * @param useCenter If true, include the center of the oval in the arc, and
- close it if it is being stroked. This will draw a wedge
- * @param paint The paint used to draw the arc
- */
- public void drawArc(float left, float top, float right, float bottom, float startAngle,
- float sweepAngle, boolean useCenter, @NonNull Paint paint) {
- super.drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint);
- }
-
- /**
- * <p>Draw the specified arc, which will be scaled to fit inside the
- * specified oval.</p>
- *
- * <p>If the start angle is negative or >= 360, the start angle is treated
- * as start angle modulo 360.</p>
- *
- * <p>If the sweep angle is >= 360, then the oval is drawn
- * completely. Note that this differs slightly from SkPath::arcTo, which
- * treats the sweep angle modulo 360. If the sweep angle is negative,
- * the sweep angle is treated as sweep angle modulo 360</p>
- *
- * <p>The arc is drawn clockwise. An angle of 0 degrees correspond to the
- * geometric angle of 0 degrees (3 o'clock on a watch.)</p>
- *
- * @param oval The bounds of oval used to define the shape and size
- * of the arc
- * @param startAngle Starting angle (in degrees) where the arc begins
- * @param sweepAngle Sweep angle (in degrees) measured clockwise
- * @param useCenter If true, include the center of the oval in the arc, and
- close it if it is being stroked. This will draw a wedge
- * @param paint The paint used to draw the arc
+ * @param useCenter If true, include the center of the oval in the arc, and close it if it is
+ * being stroked. This will draw a wedge
+ * @param paint The paint used to draw the arc
*/
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
@NonNull Paint paint) {
@@ -1190,8 +1166,37 @@
}
/**
- * Fill the entire canvas' bitmap (restricted to the current clip) with the
- * specified ARGB color, using srcover porterduff mode.
+ * <p>
+ * Draw the specified arc, which will be scaled to fit inside the specified oval.
+ * </p>
+ * <p>
+ * If the start angle is negative or >= 360, the start angle is treated as start angle modulo
+ * 360.
+ * </p>
+ * <p>
+ * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
+ * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
+ * negative, the sweep angle is treated as sweep angle modulo 360
+ * </p>
+ * <p>
+ * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
+ * degrees (3 o'clock on a watch.)
+ * </p>
+ *
+ * @param startAngle Starting angle (in degrees) where the arc begins
+ * @param sweepAngle Sweep angle (in degrees) measured clockwise
+ * @param useCenter If true, include the center of the oval in the arc, and close it if it is
+ * being stroked. This will draw a wedge
+ * @param paint The paint used to draw the arc
+ */
+ public void drawArc(float left, float top, float right, float bottom, float startAngle,
+ float sweepAngle, boolean useCenter, @NonNull Paint paint) {
+ super.drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint);
+ }
+
+ /**
+ * Fill the entire canvas' bitmap (restricted to the current clip) with the specified ARGB
+ * color, using srcover porterduff mode.
*
* @param a alpha component (0..255) of the color to draw onto the canvas
* @param r red component (0..255) of the color to draw onto the canvas
@@ -1203,60 +1208,68 @@
}
/**
- * Draw the specified bitmap, with its top/left corner at (x,y), using
- * the specified paint, transformed by the current matrix.
- *
- * <p>Note: if the paint contains a maskfilter that generates a mask which
- * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
- * then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
- * Thus the color outside of the original width/height will be the edge
- * color replicated.
- *
- * <p>If the bitmap and canvas have different densities, this function
- * will take care of automatically scaling the bitmap to draw at the
- * same density as the canvas.
+ * Draw the specified bitmap, with its top/left corner at (x,y), using the specified paint,
+ * transformed by the current matrix.
+ * <p>
+ * Note: if the paint contains a maskfilter that generates a mask which extends beyond the
+ * bitmap's original width/height (e.g. BlurMaskFilter), then the bitmap will be drawn as if it
+ * were in a Shader with CLAMP mode. Thus the color outside of the original width/height will be
+ * the edge color replicated.
+ * <p>
+ * If the bitmap and canvas have different densities, this function will take care of
+ * automatically scaling the bitmap to draw at the same density as the canvas.
*
* @param bitmap The bitmap to be drawn
- * @param left The position of the left side of the bitmap being drawn
- * @param top The position of the top side of the bitmap being drawn
- * @param paint The paint used to draw the bitmap (may be null)
+ * @param left The position of the left side of the bitmap being drawn
+ * @param top The position of the top side of the bitmap being drawn
+ * @param paint The paint used to draw the bitmap (may be null)
*/
public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
super.drawBitmap(bitmap, left, top, paint);
}
/**
- * Draw the bitmap using the specified matrix.
+ * Draw the specified bitmap, scaling/translating automatically to fill the destination
+ * rectangle. If the source rectangle is not null, it specifies the subset of the bitmap to
+ * draw.
+ * <p>
+ * Note: if the paint contains a maskfilter that generates a mask which extends beyond the
+ * bitmap's original width/height (e.g. BlurMaskFilter), then the bitmap will be drawn as if it
+ * were in a Shader with CLAMP mode. Thus the color outside of the original width/height will be
+ * the edge color replicated.
+ * <p>
+ * This function <em>ignores the density associated with the bitmap</em>. This is because the
+ * source and destination rectangle coordinate spaces are in their respective densities, so must
+ * already have the appropriate scaling factor applied.
*
- * @param bitmap The bitmap to draw
- * @param matrix The matrix used to transform the bitmap when it is drawn
- * @param paint May be null. The paint used to draw the bitmap
+ * @param bitmap The bitmap to be drawn
+ * @param src May be null. The subset of the bitmap to be drawn
+ * @param dst The rectangle that the bitmap will be scaled/translated to fit into
+ * @param paint May be null. The paint used to draw the bitmap
*/
- public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
- super.drawBitmap(bitmap, matrix, paint);
+ public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
+ @Nullable Paint paint) {
+ super.drawBitmap(bitmap, src, dst, paint);
}
/**
- * Draw the specified bitmap, scaling/translating automatically to fill
- * the destination rectangle. If the source rectangle is not null, it
- * specifies the subset of the bitmap to draw.
- *
- * <p>Note: if the paint contains a maskfilter that generates a mask which
- * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
- * then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
- * Thus the color outside of the original width/height will be the edge
- * color replicated.
- *
- * <p>This function <em>ignores the density associated with the bitmap</em>.
- * This is because the source and destination rectangle coordinate
- * spaces are in their respective densities, so must already have the
- * appropriate scaling factor applied.
+ * Draw the specified bitmap, scaling/translating automatically to fill the destination
+ * rectangle. If the source rectangle is not null, it specifies the subset of the bitmap to
+ * draw.
+ * <p>
+ * Note: if the paint contains a maskfilter that generates a mask which extends beyond the
+ * bitmap's original width/height (e.g. BlurMaskFilter), then the bitmap will be drawn as if it
+ * were in a Shader with CLAMP mode. Thus the color outside of the original width/height will be
+ * the edge color replicated.
+ * <p>
+ * This function <em>ignores the density associated with the bitmap</em>. This is because the
+ * source and destination rectangle coordinate spaces are in their respective densities, so must
+ * already have the appropriate scaling factor applied.
*
* @param bitmap The bitmap to be drawn
- * @param src May be null. The subset of the bitmap to be drawn
- * @param dst The rectangle that the bitmap will be scaled/translated
- * to fit into
- * @param paint May be null. The paint used to draw the bitmap
+ * @param src May be null. The subset of the bitmap to be drawn
+ * @param dst The rectangle that the bitmap will be scaled/translated to fit into
+ * @param paint May be null. The paint used to draw the bitmap
*/
public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,
@Nullable Paint paint) {
@@ -1264,55 +1277,25 @@
}
/**
- * Draw the specified bitmap, scaling/translating automatically to fill
- * the destination rectangle. If the source rectangle is not null, it
- * specifies the subset of the bitmap to draw.
- *
- * <p>Note: if the paint contains a maskfilter that generates a mask which
- * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
- * then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
- * Thus the color outside of the original width/height will be the edge
- * color replicated.
- *
- * <p>This function <em>ignores the density associated with the bitmap</em>.
- * This is because the source and destination rectangle coordinate
- * spaces are in their respective densities, so must already have the
- * appropriate scaling factor applied.
- *
- * @param bitmap The bitmap to be drawn
- * @param src May be null. The subset of the bitmap to be drawn
- * @param dst The rectangle that the bitmap will be scaled/translated
- * to fit into
- * @param paint May be null. The paint used to draw the bitmap
- */
- public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
- @Nullable Paint paint) {
- super.drawBitmap(bitmap, src, dst, paint);
- }
-
- /**
- * Treat the specified array of colors as a bitmap, and draw it. This gives
- * the same result as first creating a bitmap from the array, and then
- * drawing it, but this method avoids explicitly creating a bitmap object
- * which can be more efficient if the colors are changing often.
+ * Treat the specified array of colors as a bitmap, and draw it. This gives the same result as
+ * first creating a bitmap from the array, and then drawing it, but this method avoids
+ * explicitly creating a bitmap object which can be more efficient if the colors are changing
+ * often.
*
* @param colors Array of colors representing the pixels of the bitmap
* @param offset Offset into the array of colors for the first pixel
- * @param stride The number of colors in the array between rows (must be
- * >= width or <= -width).
+ * @param stride The number of colors in the array between rows (must be >= width or <= -width).
* @param x The X coordinate for where to draw the bitmap
* @param y The Y coordinate for where to draw the bitmap
* @param width The width of the bitmap
* @param height The height of the bitmap
- * @param hasAlpha True if the alpha channel of the colors contains valid
- * values. If false, the alpha byte is ignored (assumed to
- * be 0xFF for every pixel).
- * @param paint May be null. The paint used to draw the bitmap
- *
+ * @param hasAlpha True if the alpha channel of the colors contains valid values. If false, the
+ * alpha byte is ignored (assumed to be 0xFF for every pixel).
+ * @param paint May be null. The paint used to draw the bitmap
* @deprecated Usage with a {@link #isHardwareAccelerated() hardware accelerated} canvas
- * requires an internal copy of color buffer contents every time this method is called. Using a
- * Bitmap avoids this copy, and allows the application to more explicitly control the lifetime
- * and copies of pixel data.
+ * requires an internal copy of color buffer contents every time this method is
+ * called. Using a Bitmap avoids this copy, and allows the application to more
+ * explicitly control the lifetime and copies of pixel data.
*/
@Deprecated
public void drawBitmap(@NonNull int[] colors, int offset, int stride, float x, float y,
@@ -1324,9 +1307,9 @@
* Legacy version of drawBitmap(int[] colors, ...) that took ints for x,y
*
* @deprecated Usage with a {@link #isHardwareAccelerated() hardware accelerated} canvas
- * requires an internal copy of color buffer contents every time this method is called. Using a
- * Bitmap avoids this copy, and allows the application to more explicitly control the lifetime
- * and copies of pixel data.
+ * requires an internal copy of color buffer contents every time this method is
+ * called. Using a Bitmap avoids this copy, and allows the application to more
+ * explicitly control the lifetime and copies of pixel data.
*/
@Deprecated
public void drawBitmap(@NonNull int[] colors, int offset, int stride, int x, int y,
@@ -1335,30 +1318,35 @@
}
/**
- * Draw the bitmap through the mesh, where mesh vertices are evenly
- * distributed across the bitmap. There are meshWidth+1 vertices across, and
- * meshHeight+1 vertices down. The verts array is accessed in row-major
- * order, so that the first meshWidth+1 vertices are distributed across the
- * top of the bitmap from left to right. A more general version of this
- * method is drawVertices().
+ * Draw the bitmap using the specified matrix.
+ *
+ * @param bitmap The bitmap to draw
+ * @param matrix The matrix used to transform the bitmap when it is drawn
+ * @param paint May be null. The paint used to draw the bitmap
+ */
+ public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
+ super.drawBitmap(bitmap, matrix, paint);
+ }
+
+ /**
+ * Draw the bitmap through the mesh, where mesh vertices are evenly distributed across the
+ * bitmap. There are meshWidth+1 vertices across, and meshHeight+1 vertices down. The verts
+ * array is accessed in row-major order, so that the first meshWidth+1 vertices are distributed
+ * across the top of the bitmap from left to right. A more general version of this method is
+ * drawVertices().
*
* @param bitmap The bitmap to draw using the mesh
- * @param meshWidth The number of columns in the mesh. Nothing is drawn if
- * this is 0
- * @param meshHeight The number of rows in the mesh. Nothing is drawn if
- * this is 0
- * @param verts Array of x,y pairs, specifying where the mesh should be
- * drawn. There must be at least
- * (meshWidth+1) * (meshHeight+1) * 2 + vertOffset values
- * in the array
+ * @param meshWidth The number of columns in the mesh. Nothing is drawn if this is 0
+ * @param meshHeight The number of rows in the mesh. Nothing is drawn if this is 0
+ * @param verts Array of x,y pairs, specifying where the mesh should be drawn. There must be at
+ * least (meshWidth+1) * (meshHeight+1) * 2 + vertOffset values in the array
* @param vertOffset Number of verts elements to skip before drawing
- * @param colors May be null. Specifies a color at each vertex, which is
- * interpolated across the cell, and whose values are
- * multiplied by the corresponding bitmap colors. If not null,
- * there must be at least (meshWidth+1) * (meshHeight+1) +
- * colorOffset values in the array.
+ * @param colors May be null. Specifies a color at each vertex, which is interpolated across the
+ * cell, and whose values are multiplied by the corresponding bitmap colors. If not
+ * null, there must be at least (meshWidth+1) * (meshHeight+1) + colorOffset values
+ * in the array.
* @param colorOffset Number of color elements to skip before drawing
- * @param paint May be null. The paint used to draw the bitmap
+ * @param paint May be null. The paint used to draw the bitmap
*/
public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight,
@NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset,
@@ -1368,22 +1356,21 @@
}
/**
- * Draw the specified circle using the specified paint. If radius is <= 0,
- * then nothing will be drawn. The circle will be filled or framed based
- * on the Style in the paint.
+ * Draw the specified circle using the specified paint. If radius is <= 0, then nothing will be
+ * drawn. The circle will be filled or framed based on the Style in the paint.
*
- * @param cx The x-coordinate of the center of the cirle to be drawn
- * @param cy The y-coordinate of the center of the cirle to be drawn
+ * @param cx The x-coordinate of the center of the cirle to be drawn
+ * @param cy The y-coordinate of the center of the cirle to be drawn
* @param radius The radius of the cirle to be drawn
- * @param paint The paint used to draw the circle
+ * @param paint The paint used to draw the circle
*/
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
super.drawCircle(cx, cy, radius, paint);
}
/**
- * Fill the entire canvas' bitmap (restricted to the current clip) with the
- * specified color, using srcover porterduff mode.
+ * Fill the entire canvas' bitmap (restricted to the current clip) with the specified color,
+ * using srcover porterduff mode.
*
* @param color the color to draw onto the canvas
*/
@@ -1392,27 +1379,29 @@
}
/**
- * Fill the entire canvas' bitmap (restricted to the current clip) with the
- * specified color and porter-duff xfermode.
+ * Fill the entire canvas' bitmap (restricted to the current clip) with the specified color and
+ * porter-duff xfermode.
*
* @param color the color to draw with
- * @param mode the porter-duff mode to apply to the color
+ * @param mode the porter-duff mode to apply to the color
*/
public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
super.drawColor(color, mode);
}
/**
- * Draw a line segment with the specified start and stop x,y coordinates,
- * using the specified paint.
- *
- * <p>Note that since a line is always "framed", the Style is ignored in the paint.</p>
- *
- * <p>Degenerate lines (length is 0) will not be drawn.</p>
+ * Draw a line segment with the specified start and stop x,y coordinates, using the specified
+ * paint.
+ * <p>
+ * Note that since a line is always "framed", the Style is ignored in the paint.
+ * </p>
+ * <p>
+ * Degenerate lines (length is 0) will not be drawn.
+ * </p>
*
* @param startX The x-coordinate of the start point of the line
* @param startY The y-coordinate of the start point of the line
- * @param paint The paint used to draw the line
+ * @param paint The paint used to draw the line
*/
public void drawLine(float startX, float startY, float stopX, float stopY,
@NonNull Paint paint) {
@@ -1420,40 +1409,30 @@
}
/**
- * Draw a series of lines. Each line is taken from 4 consecutive values
- * in the pts array. Thus to draw 1 line, the array must contain at least 4
- * values. This is logically the same as drawing the array as follows:
- * drawLine(pts[0], pts[1], pts[2], pts[3]) followed by
+ * Draw a series of lines. Each line is taken from 4 consecutive values in the pts array. Thus
+ * to draw 1 line, the array must contain at least 4 values. This is logically the same as
+ * drawing the array as follows: drawLine(pts[0], pts[1], pts[2], pts[3]) followed by
* drawLine(pts[4], pts[5], pts[6], pts[7]) and so on.
*
- * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...]
- * @param offset Number of values in the array to skip before drawing.
- * @param count The number of values in the array to process, after
- * skipping "offset" of them. Since each line uses 4 values,
- * the number of "lines" that are drawn is really
- * (count >> 2).
- * @param paint The paint used to draw the points
+ * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...]
+ * @param offset Number of values in the array to skip before drawing.
+ * @param count The number of values in the array to process, after skipping "offset" of them.
+ * Since each line uses 4 values, the number of "lines" that are drawn is really
+ * (count >> 2).
+ * @param paint The paint used to draw the points
*/
- public void drawLines(@Size(multiple=4) @NonNull float[] pts, int offset, int count,
+ public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,
@NonNull Paint paint) {
super.drawLines(pts, offset, count, paint);
}
- public void drawLines(@Size(multiple=4) @NonNull float[] pts, @NonNull Paint paint) {
+ public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) {
super.drawLines(pts, paint);
}
/**
- * Draw the specified oval using the specified paint. The oval will be
- * filled or framed based on the Style in the paint.
- */
- public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
- super.drawOval(left, top, right, bottom, paint);
- }
-
- /**
- * Draw the specified oval using the specified paint. The oval will be
- * filled or framed based on the Style in the paint.
+ * Draw the specified oval using the specified paint. The oval will be filled or framed based on
+ * the Style in the paint.
*
* @param oval The rectangle bounds of the oval to be drawn
*/
@@ -1462,9 +1441,17 @@
}
/**
- * Fill the entire canvas' bitmap (restricted to the current clip) with
- * the specified paint. This is equivalent (but faster) to drawing an
- * infinitely large rectangle with the specified paint.
+ * Draw the specified oval using the specified paint. The oval will be filled or framed based on
+ * the Style in the paint.
+ */
+ public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
+ super.drawOval(left, top, right, bottom, paint);
+ }
+
+ /**
+ * Fill the entire canvas' bitmap (restricted to the current clip) with the specified paint.
+ * This is equivalent (but faster) to drawing an infinitely large rectangle with the specified
+ * paint.
*
* @param paint The paint used to draw onto the canvas
*/
@@ -1478,7 +1465,6 @@
* @param patch The ninepatch object to render
* @param dst The destination rectangle.
* @param paint The paint to draw the bitmap with. may be null
- *
* @hide
*/
public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
@@ -1491,7 +1477,6 @@
* @param patch The ninepatch object to render
* @param dst The destination rectangle.
* @param paint The paint to draw the bitmap with. may be null
- *
* @hide
*/
public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) {
@@ -1499,10 +1484,10 @@
}
/**
- * Draw the specified path using the specified paint. The path will be
- * filled or framed based on the Style in the paint.
+ * Draw the specified path using the specified paint. The path will be filled or framed based on
+ * the Style in the paint.
*
- * @param path The path to be drawn
+ * @param path The path to be drawn
* @param paint The paint used to draw the path
*/
public void drawPath(@NonNull Path path, @NonNull Paint paint) {
@@ -1517,22 +1502,19 @@
}
/**
- * Draw a series of points. Each point is centered at the coordinate
- * specified by pts[], and its diameter is specified by the paint's stroke
- * width (as transformed by the canvas' CTM), with special treatment for
- * a stroke width of 0, which always draws exactly 1 pixel (or at most 4
- * if antialiasing is enabled). The shape of the point is controlled by
- * the paint's Cap type. The shape is a square, unless the cap type is
- * Round, in which case the shape is a circle.
+ * Draw a series of points. Each point is centered at the coordinate specified by pts[], and its
+ * diameter is specified by the paint's stroke width (as transformed by the canvas' CTM), with
+ * special treatment for a stroke width of 0, which always draws exactly 1 pixel (or at most 4
+ * if antialiasing is enabled). The shape of the point is controlled by the paint's Cap type.
+ * The shape is a square, unless the cap type is Round, in which case the shape is a circle.
*
- * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...]
- * @param offset Number of values to skip before starting to draw.
- * @param count The number of values to process, after skipping offset
- * of them. Since one point uses two values, the number of
- * "points" that are drawn is really (count >> 1).
- * @param paint The paint used to draw the points
+ * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...]
+ * @param offset Number of values to skip before starting to draw.
+ * @param count The number of values to process, after skipping offset of them. Since one point
+ * uses two values, the number of "points" that are drawn is really (count >> 1).
+ * @param paint The paint used to draw the points
*/
- public void drawPoints(@Size(multiple=2) float[] pts, int offset, int count,
+ public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
@NonNull Paint paint) {
super.drawPoints(pts, offset, count, paint);
}
@@ -1540,80 +1522,50 @@
/**
* Helper for drawPoints() that assumes you want to draw the entire array
*/
- public void drawPoints(@Size(multiple=2) @NonNull float[] pts, @NonNull Paint paint) {
+ public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) {
super.drawPoints(pts, paint);
}
/**
- * Draw the text in the array, with each character's origin specified by
- * the pos array.
+ * Draw the text in the array, with each character's origin specified by the pos array.
*
- * @param text The text to be drawn
- * @param index The index of the first character to draw
- * @param count The number of characters to draw, starting from index.
- * @param pos Array of [x,y] positions, used to position each
- * character
- * @param paint The paint used for the text (e.g. color, size, style)
- *
- * @deprecated This method does not support glyph composition and decomposition and
- * should therefore not be used to render complex scripts. It also doesn't
- * handle supplementary characters (eg emoji).
+ * @param text The text to be drawn
+ * @param index The index of the first character to draw
+ * @param count The number of characters to draw, starting from index.
+ * @param pos Array of [x,y] positions, used to position each character
+ * @param paint The paint used for the text (e.g. color, size, style)
+ * @deprecated This method does not support glyph composition and decomposition and should
+ * therefore not be used to render complex scripts. It also doesn't handle
+ * supplementary characters (eg emoji).
*/
@Deprecated
public void drawPosText(@NonNull char[] text, int index, int count,
- @NonNull @Size(multiple=2) float[] pos,
+ @NonNull @Size(multiple = 2) float[] pos,
@NonNull Paint paint) {
super.drawPosText(text, index, count, pos, paint);
}
/**
- * Draw the text in the array, with each character's origin specified by
- * the pos array.
+ * Draw the text in the array, with each character's origin specified by the pos array.
*
- * @param text The text to be drawn
- * @param pos Array of [x,y] positions, used to position each character
+ * @param text The text to be drawn
+ * @param pos Array of [x,y] positions, used to position each character
* @param paint The paint used for the text (e.g. color, size, style)
- *
- * @deprecated This method does not support glyph composition and decomposition and
- * should therefore not be used to render complex scripts. It also doesn't
- * handle supplementary characters (eg emoji).
+ * @deprecated This method does not support glyph composition and decomposition and should
+ * therefore not be used to render complex scripts. It also doesn't handle
+ * supplementary characters (eg emoji).
*/
@Deprecated
- public void drawPosText(@NonNull String text, @NonNull @Size(multiple=2) float[] pos,
+ public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos,
@NonNull Paint paint) {
super.drawPosText(text, pos, paint);
}
/**
- * Draw the specified Rect using the specified paint. The rectangle will
- * be filled or framed based on the Style in the paint.
+ * Draw the specified Rect using the specified paint. The rectangle will be filled or framed
+ * based on the Style in the paint.
*
- * @param left The left side of the rectangle to be drawn
- * @param top The top side of the rectangle to be drawn
- * @param right The right side of the rectangle to be drawn
- * @param bottom The bottom side of the rectangle to be drawn
- * @param paint The paint used to draw the rect
- */
- public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
- super.drawRect(left, top, right, bottom, paint);
- }
-
- /**
- * Draw the specified Rect using the specified Paint. The rectangle
- * will be filled or framed based on the Style in the paint.
- *
- * @param r The rectangle to be drawn.
- * @param paint The paint used to draw the rectangle
- */
- public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
- super.drawRect(r, paint);
- }
-
- /**
- * Draw the specified Rect using the specified paint. The rectangle will
- * be filled or framed based on the Style in the paint.
- *
- * @param rect The rect to be drawn
+ * @param rect The rect to be drawn
* @param paint The paint used to draw the rect
*/
public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
@@ -1621,8 +1573,33 @@
}
/**
- * Fill the entire canvas' bitmap (restricted to the current clip) with the
- * specified RGB color, using srcover porterduff mode.
+ * Draw the specified Rect using the specified Paint. The rectangle will be filled or framed
+ * based on the Style in the paint.
+ *
+ * @param r The rectangle to be drawn.
+ * @param paint The paint used to draw the rectangle
+ */
+ public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
+ super.drawRect(r, paint);
+ }
+
+ /**
+ * Draw the specified Rect using the specified paint. The rectangle will be filled or framed
+ * based on the Style in the paint.
+ *
+ * @param left The left side of the rectangle to be drawn
+ * @param top The top side of the rectangle to be drawn
+ * @param right The right side of the rectangle to be drawn
+ * @param bottom The bottom side of the rectangle to be drawn
+ * @param paint The paint used to draw the rect
+ */
+ public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
+ super.drawRect(left, top, right, bottom, paint);
+ }
+
+ /**
+ * Fill the entire canvas' bitmap (restricted to the current clip) with the specified RGB color,
+ * using srcover porterduff mode.
*
* @param r red component (0..255) of the color to draw onto the canvas
* @param g green component (0..255) of the color to draw onto the canvas
@@ -1633,11 +1610,24 @@
}
/**
- * Draw the specified round-rect using the specified paint. The roundrect
- * will be filled or framed based on the Style in the paint.
+ * Draw the specified round-rect using the specified paint. The roundrect will be filled or
+ * framed based on the Style in the paint.
*
- * @param rx The x-radius of the oval used to round the corners
- * @param ry The y-radius of the oval used to round the corners
+ * @param rect The rectangular bounds of the roundRect to be drawn
+ * @param rx The x-radius of the oval used to round the corners
+ * @param ry The y-radius of the oval used to round the corners
+ * @param paint The paint used to draw the roundRect
+ */
+ public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
+ super.drawRoundRect(rect, rx, ry, paint);
+ }
+
+ /**
+ * Draw the specified round-rect using the specified paint. The roundrect will be filled or
+ * framed based on the Style in the paint.
+ *
+ * @param rx The x-radius of the oval used to round the corners
+ * @param ry The y-radius of the oval used to round the corners
* @param paint The paint used to draw the roundRect
*/
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
@@ -1646,25 +1636,12 @@
}
/**
- * Draw the specified round-rect using the specified paint. The roundrect
- * will be filled or framed based on the Style in the paint.
+ * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
+ * based on the Align setting in the paint.
*
- * @param rect The rectangular bounds of the roundRect to be drawn
- * @param rx The x-radius of the oval used to round the corners
- * @param ry The y-radius of the oval used to round the corners
- * @param paint The paint used to draw the roundRect
- */
- public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
- super.drawRoundRect(rect, rx, ry, paint);
- }
-
- /**
- * Draw the text, with origin at (x,y), using the specified paint. The
- * origin is interpreted based on the Align setting in the paint.
- *
- * @param text The text to be drawn
- * @param x The x-coordinate of the origin of the text being drawn
- * @param y The y-coordinate of the baseline of the text being drawn
+ * @param text The text to be drawn
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawText(@NonNull char[] text, int index, int count, float x, float y,
@@ -1673,30 +1650,12 @@
}
/**
- * Draw the specified range of text, specified by start/end, with its
- * origin at (x,y), in the specified Paint. The origin is interpreted
- * based on the Align setting in the Paint.
+ * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
+ * based on the Align setting in the paint.
*
- * @param text The text to be drawn
- * @param start The index of the first character in text to draw
- * @param end (end - 1) is the index of the last character in text
- * to draw
- * @param x The x-coordinate of origin for where to draw the text
- * @param y The y-coordinate of origin for where to draw the text
- * @param paint The paint used for the text (e.g. color, size, style)
- */
- public void drawText(@NonNull CharSequence text, int start, int end, float x, float y,
- @NonNull Paint paint) {
- super.drawText(text, start, end, x, y, paint);
- }
-
- /**
- * Draw the text, with origin at (x,y), using the specified paint. The
- * origin is interpreted based on the Align setting in the paint.
- *
- * @param text The text to be drawn
- * @param x The x-coordinate of the origin of the text being drawn
- * @param y The y-coordinate of the baseline of the text being drawn
+ * @param text The text to be drawn
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
@@ -1704,14 +1663,14 @@
}
/**
- * Draw the text, with origin at (x,y), using the specified paint.
- * The origin is interpreted based on the Align setting in the paint.
+ * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
+ * based on the Align setting in the paint.
*
- * @param text The text to be drawn
+ * @param text The text to be drawn
* @param start The index of the first character in text to draw
- * @param end (end - 1) is the index of the last character in text to draw
- * @param x The x-coordinate of the origin of the text being drawn
- * @param y The y-coordinate of the baseline of the text being drawn
+ * @param end (end - 1) is the index of the last character in text to draw
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawText(@NonNull String text, int start, int end, float x, float y,
@@ -1720,17 +1679,30 @@
}
/**
- * Draw the text, with origin at (x,y), using the specified paint, along
- * the specified path. The paint's Align setting determins where along the
- * path to start the text.
+ * Draw the specified range of text, specified by start/end, with its origin at (x,y), in the
+ * specified Paint. The origin is interpreted based on the Align setting in the Paint.
*
- * @param text The text to be drawn
- * @param path The path the text should follow for its baseline
- * @param hOffset The distance along the path to add to the text's
- * starting position
- * @param vOffset The distance above(-) or below(+) the path to position
- * the text
- * @param paint The paint used for the text (e.g. color, size, style)
+ * @param text The text to be drawn
+ * @param start The index of the first character in text to draw
+ * @param end (end - 1) is the index of the last character in text to draw
+ * @param x The x-coordinate of origin for where to draw the text
+ * @param y The y-coordinate of origin for where to draw the text
+ * @param paint The paint used for the text (e.g. color, size, style)
+ */
+ public void drawText(@NonNull CharSequence text, int start, int end, float x, float y,
+ @NonNull Paint paint) {
+ super.drawText(text, start, end, x, y, paint);
+ }
+
+ /**
+ * Draw the text, with origin at (x,y), using the specified paint, along the specified path. The
+ * paint's Align setting determins where along the path to start the text.
+ *
+ * @param text The text to be drawn
+ * @param path The path the text should follow for its baseline
+ * @param hOffset The distance along the path to add to the text's starting position
+ * @param vOffset The distance above(-) or below(+) the path to position the text
+ * @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path,
float hOffset, float vOffset, @NonNull Paint paint) {
@@ -1738,17 +1710,14 @@
}
/**
- * Draw the text, with origin at (x,y), using the specified paint, along
- * the specified path. The paint's Align setting determins where along the
- * path to start the text.
+ * Draw the text, with origin at (x,y), using the specified paint, along the specified path. The
+ * paint's Align setting determins where along the path to start the text.
*
- * @param text The text to be drawn
- * @param path The path the text should follow for its baseline
- * @param hOffset The distance along the path to add to the text's
- * starting position
- * @param vOffset The distance above(-) or below(+) the path to position
- * the text
- * @param paint The paint used for the text (e.g. color, size, style)
+ * @param text The text to be drawn
+ * @param path The path the text should follow for its baseline
+ * @param hOffset The distance along the path to add to the text's starting position
+ * @param vOffset The distance above(-) or below(+) the path to position the text
+ * @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
float vOffset, @NonNull Paint paint) {
@@ -1758,21 +1727,20 @@
/**
* Draw a run of text, all in a single direction, with optional context for complex text
* shaping.
- *
- * <p>See {@link #drawTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)}
- * for more details. This method uses a character array rather than CharSequence to
- * represent the string. Also, to be consistent with the pattern established in
- * {@link #drawText}, in this method {@code count} and {@code contextCount} are used rather
- * than offsets of the end position; {@code count = end - start, contextCount = contextEnd -
+ * <p>
+ * See {@link #drawTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)} for
+ * more details. This method uses a character array rather than CharSequence to represent the
+ * string. Also, to be consistent with the pattern established in {@link #drawText}, in this
+ * method {@code count} and {@code contextCount} are used rather than offsets of the end
+ * position; {@code count = end - start, contextCount = contextEnd -
* contextStart}.
*
* @param text the text to render
* @param index the start of the text to render
* @param count the count of chars to render
- * @param contextIndex the start of the context for shaping. Must be
- * no greater than index.
- * @param contextCount the number of characters in the context for shaping.
- * contexIndex + contextCount must be no less than index + count.
+ * @param contextIndex the start of the context for shaping. Must be no greater than index.
+ * @param contextCount the number of characters in the context for shaping. contexIndex +
+ * contextCount must be no less than index + count.
* @param x the x position at which to draw the text
* @param y the y position at which to draw the text
* @param isRtl whether the run is in RTL direction
@@ -1786,36 +1754,34 @@
/**
* Draw a run of text, all in a single direction, with optional context for complex text
* shaping.
- *
- * <p>The run of text includes the characters from {@code start} to {@code end} in the text. In
+ * <p>
+ * The run of text includes the characters from {@code start} to {@code end} in the text. In
* addition, the range {@code contextStart} to {@code contextEnd} is used as context for the
* purpose of complex text shaping, such as Arabic text potentially shaped differently based on
* the text next to it.
- *
- * <p>All text outside the range {@code contextStart..contextEnd} is ignored. The text between
+ * <p>
+ * All text outside the range {@code contextStart..contextEnd} is ignored. The text between
* {@code start} and {@code end} will be laid out and drawn.
- *
- * <p>The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is
+ * <p>
+ * The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is
* suitable only for runs of a single direction. Alignment of the text is as determined by the
* Paint's TextAlign value. Further, {@code 0 <= contextStart <= start <= end <= contextEnd
* <= text.length} must hold on entry.
- *
- * <p>Also see {@link android.graphics.Paint#getRunAdvance} for a corresponding method to
- * measure the text; the advance width of the text drawn matches the value obtained from that
- * method.
+ * <p>
+ * Also see {@link android.graphics.Paint#getRunAdvance} for a corresponding method to measure
+ * the text; the advance width of the text drawn matches the value obtained from that method.
*
* @param text the text to render
- * @param start the start of the text to render. Data before this position
- * can be used for shaping context.
- * @param end the end of the text to render. Data at or after this
- * position can be used for shaping context.
+ * @param start the start of the text to render. Data before this position can be used for
+ * shaping context.
+ * @param end the end of the text to render. Data at or after this position can be used for
+ * shaping context.
* @param contextStart the index of the start of the shaping context
* @param contextEnd the index of the end of the shaping context
* @param x the x position at which to draw the text
* @param y the y position at which to draw the text
* @param isRtl whether the run is in RTL direction
* @param paint the paint
- *
* @see #drawTextRun(char[], int, int, int, int, float, float, boolean, Paint)
*/
public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart,
@@ -1824,32 +1790,30 @@
}
/**
- * Draw the array of vertices, interpreted as triangles (based on mode). The
- * verts array is required, and specifies the x,y pairs for each vertex. If
- * texs is non-null, then it is used to specify the coordinate in shader
- * coordinates to use at each vertex (the paint must have a shader in this
- * case). If there is no texs array, but there is a color array, then each
- * color is interpolated across its corresponding triangle in a gradient. If
- * both texs and colors arrays are present, then they behave as before, but
- * the resulting color at each pixels is the result of multiplying the
- * colors from the shader and the color-gradient together. The indices array
- * is optional, but if it is present, then it is used to specify the index
- * of each triangle, rather than just walking through the arrays in order.
+ * Draw the array of vertices, interpreted as triangles (based on mode). The verts array is
+ * required, and specifies the x,y pairs for each vertex. If texs is non-null, then it is used
+ * to specify the coordinate in shader coordinates to use at each vertex (the paint must have a
+ * shader in this case). If there is no texs array, but there is a color array, then each color
+ * is interpolated across its corresponding triangle in a gradient. If both texs and colors
+ * arrays are present, then they behave as before, but the resulting color at each pixels is the
+ * result of multiplying the colors from the shader and the color-gradient together. The indices
+ * array is optional, but if it is present, then it is used to specify the index of each
+ * triangle, rather than just walking through the arrays in order.
*
* @param mode How to interpret the array of vertices
- * @param vertexCount The number of values in the vertices array (and
- * corresponding texs and colors arrays if non-null). Each logical
- * vertex is two values (x, y), vertexCount must be a multiple of 2.
+ * @param vertexCount The number of values in the vertices array (and corresponding texs and
+ * colors arrays if non-null). Each logical vertex is two values (x, y), vertexCount
+ * must be a multiple of 2.
* @param verts Array of vertices for the mesh
* @param vertOffset Number of values in the verts to skip before drawing.
- * @param texs May be null. If not null, specifies the coordinates to sample
- * into the current shader (e.g. bitmap tile or gradient)
+ * @param texs May be null. If not null, specifies the coordinates to sample into the current
+ * shader (e.g. bitmap tile or gradient)
* @param texOffset Number of values in texs to skip before drawing.
- * @param colors May be null. If not null, specifies a color for each
- * vertex, to be interpolated across the triangle.
+ * @param colors May be null. If not null, specifies a color for each vertex, to be interpolated
+ * across the triangle.
* @param colorOffset Number of values in colors to skip before drawing.
- * @param indices If not null, array of indices to reference into the
- * vertex (texs, colors) array.
+ * @param indices If not null, array of indices to reference into the vertex (texs, colors)
+ * array.
* @param indexCount number of entries in the indices array (if not null).
* @param paint Specifies the shader to use if the texs array is non-null.
*/
diff --git a/include/androidfw/Asset.h b/include/androidfw/Asset.h
index 52c8637..11709ce 100644
--- a/include/androidfw/Asset.h
+++ b/include/androidfw/Asset.h
@@ -26,11 +26,12 @@
#include <utils/Compat.h>
#include <utils/Errors.h>
-#include <utils/FileMap.h>
#include <utils/String8.h>
namespace android {
+class FileMap;
+
/*
* Instances of this class provide read-only operations on a byte stream.
*
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index 3b5cdce..4377213 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -70,11 +70,11 @@
static const char* IDMAP_BIN;
static const char* OVERLAY_DIR;
/*
- * If OVERLAY_SKU_DIR_PROPERTY is set, search for runtime resource overlay
- * APKs in OVERLAY_DIR/<value of OVERLAY_SKU_DIR_PROPERTY> rather than in
+ * If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay
+ * APKs in OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to
* OVERLAY_DIR.
*/
- static const char* OVERLAY_SKU_DIR_PROPERTY;
+ static const char* OVERLAY_THEME_DIR_PROPERTY;
static const char* TARGET_PACKAGE_NAME;
static const char* TARGET_APK_PATH;
static const char* IDMAP_DIR;
diff --git a/include/androidfw/AttributeFinder.h b/include/androidfw/AttributeFinder.h
deleted file mode 100644
index acf7056..0000000
--- a/include/androidfw/AttributeFinder.h
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef H_ATTRIBUTE_FINDER
-#define H_ATTRIBUTE_FINDER
-
-#include <stdint.h>
-#include <utils/KeyedVector.h>
-
-namespace android {
-
-static inline uint32_t getPackage(uint32_t attr) {
- return attr >> 24;
-}
-
-/**
- * A helper class to search linearly for the requested
- * attribute, maintaining it's position and optimizing for
- * the case that subsequent searches will involve an attribute with
- * a higher attribute ID.
- *
- * In the case that a subsequent attribute has a different package ID,
- * its resource ID may not be larger than the preceding search, so
- * back tracking is supported for this case. This
- * back tracking requirement is mainly for shared library
- * resources, whose package IDs get assigned at runtime
- * and thus attributes from a shared library may
- * be out of order.
- *
- * We make two assumptions about the order of attributes:
- * 1) The input has the same sorting rules applied to it as
- * the attribute data contained by this class.
- * 2) Attributes are grouped by package ID.
- * 3) Among attributes with the same package ID, the attributes are
- * sorted by increasing resource ID.
- *
- * Ex: 02010000, 02010001, 010100f4, 010100f5, 0x7f010001, 07f010003
- *
- * The total order of attributes (including package ID) can not be linear
- * as shared libraries get assigned dynamic package IDs at runtime, which
- * may break the sort order established at build time.
- */
-template <typename Derived, typename Iterator>
-class BackTrackingAttributeFinder {
-public:
- BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
-
- Iterator find(uint32_t attr);
-
-private:
- void jumpToClosestAttribute(uint32_t packageId);
- void markCurrentPackageId(uint32_t packageId);
-
- bool mFirstTime;
- Iterator mBegin;
- Iterator mEnd;
- Iterator mCurrent;
- Iterator mLargest;
- uint32_t mLastPackageId;
- uint32_t mCurrentAttr;
-
- // Package Offsets (best-case, fast look-up).
- Iterator mFrameworkStart;
- Iterator mAppStart;
-
- // Worst case, we have shared-library resources.
- KeyedVector<uint32_t, Iterator> mPackageOffsets;
-};
-
-template <typename Derived, typename Iterator> inline
-BackTrackingAttributeFinder<Derived, Iterator>::BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end)
- : mFirstTime(true)
- , mBegin(begin)
- , mEnd(end)
- , mCurrent(begin)
- , mLargest(begin)
- , mLastPackageId(0)
- , mCurrentAttr(0)
- , mFrameworkStart(end)
- , mAppStart(end) {
-}
-
-template <typename Derived, typename Iterator>
-void BackTrackingAttributeFinder<Derived, Iterator>::jumpToClosestAttribute(const uint32_t packageId) {
- switch (packageId) {
- case 0x01:
- mCurrent = mFrameworkStart;
- break;
- case 0x7f:
- mCurrent = mAppStart;
- break;
- default: {
- ssize_t idx = mPackageOffsets.indexOfKey(packageId);
- if (idx >= 0) {
- // We have seen this package ID before, so jump to the first
- // attribute with this package ID.
- mCurrent = mPackageOffsets[idx];
- } else {
- mCurrent = mEnd;
- }
- break;
- }
- }
-
- // We have never seen this package ID yet, so jump to the
- // latest/largest index we have processed so far.
- if (mCurrent == mEnd) {
- mCurrent = mLargest;
- }
-
- if (mCurrent != mEnd) {
- mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent);
- }
-}
-
-template <typename Derived, typename Iterator>
-void BackTrackingAttributeFinder<Derived, Iterator>::markCurrentPackageId(const uint32_t packageId) {
- switch (packageId) {
- case 0x01:
- mFrameworkStart = mCurrent;
- break;
- case 0x7f:
- mAppStart = mCurrent;
- break;
- default:
- mPackageOffsets.add(packageId, mCurrent);
- break;
- }
-}
-
-template <typename Derived, typename Iterator>
-Iterator BackTrackingAttributeFinder<Derived, Iterator>::find(uint32_t attr) {
- if (!(mBegin < mEnd)) {
- return mEnd;
- }
-
- if (mFirstTime) {
- // One-time initialization. We do this here instead of the constructor
- // because the derived class we access in getAttribute() may not be
- // fully constructed.
- mFirstTime = false;
- mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mBegin);
- mLastPackageId = getPackage(mCurrentAttr);
- markCurrentPackageId(mLastPackageId);
- }
-
- // Looking for the needle (attribute we're looking for)
- // in the haystack (the attributes we're searching through)
- const uint32_t needlePackageId = getPackage(attr);
- if (mLastPackageId != needlePackageId) {
- jumpToClosestAttribute(needlePackageId);
- mLastPackageId = needlePackageId;
- }
-
- // Walk through the xml attributes looking for the requested attribute.
- while (mCurrent != mEnd) {
- const uint32_t haystackPackageId = getPackage(mCurrentAttr);
- if (needlePackageId == haystackPackageId && attr < mCurrentAttr) {
- // The attribute we are looking was not found.
- break;
- }
- const uint32_t prevAttr = mCurrentAttr;
-
- // Move to the next attribute in the XML.
- ++mCurrent;
- if (mCurrent != mEnd) {
- mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent);
- const uint32_t newHaystackPackageId = getPackage(mCurrentAttr);
- if (haystackPackageId != newHaystackPackageId) {
- // We've moved to the next group of attributes
- // with a new package ID, so we should record
- // the offset of this new package ID.
- markCurrentPackageId(newHaystackPackageId);
- }
- }
-
- if (mCurrent > mLargest) {
- // We've moved past the latest attribute we've
- // seen.
- mLargest = mCurrent;
- }
-
- if (attr == prevAttr) {
- // We found the attribute we were looking for.
- return mCurrent - 1;
- }
- }
- return mEnd;
-}
-
-} // namespace android
-
-#endif // H_ATTRIBUTE_FINDER
diff --git a/include/androidfw/AttributeResolution.h b/include/androidfw/AttributeResolution.h
index 2f60a1d..3ed8bce 100644
--- a/include/androidfw/AttributeResolution.h
+++ b/include/androidfw/AttributeResolution.h
@@ -21,6 +21,18 @@
namespace android {
+// Offsets into the outValues array populated by the methods below. outValues is a uint32_t
+// array, but each logical element takes up 6 uint32_t-sized physical elements.
+enum {
+ STYLE_NUM_ENTRIES = 6,
+ STYLE_TYPE = 0,
+ STYLE_DATA = 1,
+ STYLE_ASSET_COOKIE = 2,
+ STYLE_RESOURCE_ID = 3,
+ STYLE_CHANGING_CONFIGURATIONS = 4,
+ STYLE_DENSITY = 5
+};
+
// These are all variations of the same method. They each perform the exact same operation,
// but on various data sources. I *think* they are re-written to avoid an extra branch
// in the inner loop, but after one branch miss (some pointer != null), the branch predictor should
@@ -28,26 +40,17 @@
// TODO(adamlesinski): Run performance tests against these methods and a new, single method
// that uses all the sources and branches to the right ones within the inner loop.
-bool resolveAttrs(ResTable::Theme* theme,
- uint32_t defStyleAttr,
- uint32_t defStyleRes,
- uint32_t* srcValues, size_t srcValuesLength,
- uint32_t* attrs, size_t attrsLength,
- uint32_t* outValues,
- uint32_t* outIndices);
+bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
+ uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
-bool applyStyle(ResTable::Theme* theme, ResXMLParser* xmlParser,
- uint32_t defStyleAttr,
- uint32_t defStyleRes,
- uint32_t* attrs, size_t attrsLength,
- uint32_t* outValues,
- uint32_t* outIndices);
+bool ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_res, uint32_t* attrs, size_t attrs_length, uint32_t* out_values,
+ uint32_t* out_indices);
-bool retrieveAttributes(const ResTable* res, ResXMLParser* xmlParser,
- uint32_t* attrs, size_t attrsLength,
- uint32_t* outValues,
- uint32_t* outIndices);
+bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
-} // namespace android
+} // namespace android
#endif /* ANDROIDFW_ATTRIBUTERESOLUTION_H */
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 6349e86..08d6591 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -22,7 +22,6 @@
#include <androidfw/Asset.h>
#include <androidfw/LocaleData.h>
-#include <utils/ByteOrder.h>
#include <utils/Errors.h>
#include <utils/String16.h>
#include <utils/Vector.h>
diff --git a/include/androidfw/TypeWrappers.h b/include/androidfw/TypeWrappers.h
index 7bdf8af..4233b6b 100644
--- a/include/androidfw/TypeWrappers.h
+++ b/include/androidfw/TypeWrappers.h
@@ -18,6 +18,7 @@
#define __TYPE_WRAPPERS_H
#include <androidfw/ResourceTypes.h>
+#include <utils/ByteOrder.h>
namespace android {
diff --git a/libs/androidfw/.clang-format b/libs/androidfw/.clang-format
new file mode 100644
index 0000000..ee1bee2
--- /dev/null
+++ b/libs/androidfw/.clang-format
@@ -0,0 +1,2 @@
+BasedOnStyle: Google
+ColumnLimit: 100
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 371bc9a..46fc9d4 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -73,7 +73,7 @@
const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
-const char* AssetManager::OVERLAY_SKU_DIR_PROPERTY = "ro.boot.vendor.overlay.sku";
+const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme";
const char* AssetManager::TARGET_PACKAGE_NAME = "android";
const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
const char* AssetManager::IDMAP_DIR = "/data/resource-cache";
diff --git a/libs/androidfw/AttributeFinder.h b/libs/androidfw/AttributeFinder.h
new file mode 100644
index 0000000..f281921
--- /dev/null
+++ b/libs/androidfw/AttributeFinder.h
@@ -0,0 +1,206 @@
+/*
+ * 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 ANDROIDFW_ATTRIBUTE_FINDER_H
+#define ANDROIDFW_ATTRIBUTE_FINDER_H
+
+#include <utils/KeyedVector.h>
+
+#include <stdint.h>
+
+namespace android {
+
+static inline uint32_t get_package(uint32_t attr) { return attr >> 24; }
+
+/**
+ * A helper class to search linearly for the requested
+ * attribute, maintaining it's position and optimizing for
+ * the case that subsequent searches will involve an attribute with
+ * a higher attribute ID.
+ *
+ * In the case that a subsequent attribute has a different package ID,
+ * its resource ID may not be larger than the preceding search, so
+ * back tracking is supported for this case. This
+ * back tracking requirement is mainly for shared library
+ * resources, whose package IDs get assigned at runtime
+ * and thus attributes from a shared library may
+ * be out of order.
+ *
+ * We make two assumptions about the order of attributes:
+ * 1) The input has the same sorting rules applied to it as
+ * the attribute data contained by this class.
+ * 2) Attributes are grouped by package ID.
+ * 3) Among attributes with the same package ID, the attributes are
+ * sorted by increasing resource ID.
+ *
+ * Ex: 02010000, 02010001, 010100f4, 010100f5, 0x7f010001, 07f010003
+ *
+ * The total order of attributes (including package ID) can not be linear
+ * as shared libraries get assigned dynamic package IDs at runtime, which
+ * may break the sort order established at build time.
+ */
+template <typename Derived, typename Iterator>
+class BackTrackingAttributeFinder {
+ public:
+ BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
+
+ Iterator Find(uint32_t attr);
+
+ private:
+ void JumpToClosestAttribute(uint32_t package_id);
+ void MarkCurrentPackageId(uint32_t package_id);
+
+ bool first_time_;
+ Iterator begin_;
+ Iterator end_;
+ Iterator current_;
+ Iterator largest_;
+ uint32_t last_package_id_;
+ uint32_t current_attr_;
+
+ // Package offsets (best-case, fast look-up).
+ Iterator framework_start_;
+ Iterator app_start_;
+
+ // Worst case, we have shared-library resources.
+ KeyedVector<uint32_t, Iterator> package_offsets_;
+};
+
+template <typename Derived, typename Iterator>
+inline BackTrackingAttributeFinder<Derived, Iterator>::BackTrackingAttributeFinder(
+ const Iterator& begin, const Iterator& end)
+ : first_time_(true),
+ begin_(begin),
+ end_(end),
+ current_(begin),
+ largest_(begin),
+ last_package_id_(0),
+ current_attr_(0),
+ framework_start_(end),
+ app_start_(end) {}
+
+template <typename Derived, typename Iterator>
+void BackTrackingAttributeFinder<Derived, Iterator>::JumpToClosestAttribute(
+ const uint32_t package_id) {
+ switch (package_id) {
+ case 0x01u:
+ current_ = framework_start_;
+ break;
+ case 0x7fu:
+ current_ = app_start_;
+ break;
+ default: {
+ ssize_t idx = package_offsets_.indexOfKey(package_id);
+ if (idx >= 0) {
+ // We have seen this package ID before, so jump to the first
+ // attribute with this package ID.
+ current_ = package_offsets_[idx];
+ } else {
+ current_ = end_;
+ }
+ break;
+ }
+ }
+
+ // We have never seen this package ID yet, so jump to the
+ // latest/largest index we have processed so far.
+ if (current_ == end_) {
+ current_ = largest_;
+ }
+
+ if (current_ != end_) {
+ current_attr_ = static_cast<const Derived*>(this)->GetAttribute(current_);
+ }
+}
+
+template <typename Derived, typename Iterator>
+void BackTrackingAttributeFinder<Derived, Iterator>::MarkCurrentPackageId(
+ const uint32_t package_id) {
+ switch (package_id) {
+ case 0x01u:
+ framework_start_ = current_;
+ break;
+ case 0x7fu:
+ app_start_ = current_;
+ break;
+ default:
+ package_offsets_.add(package_id, current_);
+ break;
+ }
+}
+
+template <typename Derived, typename Iterator>
+Iterator BackTrackingAttributeFinder<Derived, Iterator>::Find(uint32_t attr) {
+ if (!(begin_ < end_)) {
+ return end_;
+ }
+
+ if (first_time_) {
+ // One-time initialization. We do this here instead of the constructor
+ // because the derived class we access in getAttribute() may not be
+ // fully constructed.
+ first_time_ = false;
+ current_attr_ = static_cast<const Derived*>(this)->GetAttribute(begin_);
+ last_package_id_ = get_package(current_attr_);
+ MarkCurrentPackageId(last_package_id_);
+ }
+
+ // Looking for the needle (attribute we're looking for)
+ // in the haystack (the attributes we're searching through)
+ const uint32_t needle_package_id = get_package(attr);
+ if (last_package_id_ != needle_package_id) {
+ JumpToClosestAttribute(needle_package_id);
+ last_package_id_ = needle_package_id;
+ }
+
+ // Walk through the xml attributes looking for the requested attribute.
+ while (current_ != end_) {
+ const uint32_t haystack_package_id = get_package(current_attr_);
+ if (needle_package_id == haystack_package_id && attr < current_attr_) {
+ // The attribute we are looking was not found.
+ break;
+ }
+ const uint32_t prev_attr = current_attr_;
+
+ // Move to the next attribute in the XML.
+ ++current_;
+ if (current_ != end_) {
+ current_attr_ = static_cast<const Derived*>(this)->GetAttribute(current_);
+ const uint32_t new_haystack_package_id = get_package(current_attr_);
+ if (haystack_package_id != new_haystack_package_id) {
+ // We've moved to the next group of attributes
+ // with a new package ID, so we should record
+ // the offset of this new package ID.
+ MarkCurrentPackageId(new_haystack_package_id);
+ }
+ }
+
+ if (current_ > largest_) {
+ // We've moved past the latest attribute we've seen.
+ largest_ = current_;
+ }
+
+ if (attr == prev_attr) {
+ // We found the attribute we were looking for.
+ return current_ - 1;
+ }
+ }
+ return end_;
+}
+
+} // namespace android
+
+#endif // ANDROIDFW_ATTRIBUTE_FINDER_H
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index ad428a4..00f7a42 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#include "androidfw/AttributeFinder.h"
+#include "AttributeFinder.h"
+
#include "androidfw/AttributeResolution.h"
#include "androidfw/ResourceTypes.h"
@@ -25,476 +26,452 @@
namespace android {
-enum {
- STYLE_NUM_ENTRIES = 6,
- STYLE_TYPE = 0,
- STYLE_DATA = 1,
- STYLE_ASSET_COOKIE = 2,
- STYLE_RESOURCE_ID = 3,
- STYLE_CHANGING_CONFIGURATIONS = 4,
- STYLE_DENSITY = 5
-};
-
class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
-public:
- explicit XmlAttributeFinder(const ResXMLParser* parser) :
- BackTrackingAttributeFinder(0, parser != NULL ? parser->getAttributeCount() : 0),
- mParser(parser) {
- }
+ public:
+ explicit XmlAttributeFinder(const ResXMLParser* parser)
+ : BackTrackingAttributeFinder(0, parser != NULL ? parser->getAttributeCount() : 0),
+ parser_(parser) {}
- inline uint32_t getAttribute(size_t index) const {
- return mParser->getAttributeNameResID(index);
- }
+ inline uint32_t GetAttribute(size_t index) const { return parser_->getAttributeNameResID(index); }
-private:
- const ResXMLParser* mParser;
+ private:
+ const ResXMLParser* parser_;
};
-class BagAttributeFinder :
- public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
-public:
- BagAttributeFinder(const ResTable::bag_entry* start, const ResTable::bag_entry* end) :
- BackTrackingAttributeFinder(start, end) {}
+class BagAttributeFinder
+ : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
+ public:
+ BagAttributeFinder(const ResTable::bag_entry* start, const ResTable::bag_entry* end)
+ : BackTrackingAttributeFinder(start, end) {}
- inline uint32_t getAttribute(const ResTable::bag_entry* entry) const {
- return entry->map.name.ident;
- }
+ inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const {
+ return entry->map.name.ident;
+ }
};
-bool resolveAttrs(ResTable::Theme* theme,
- uint32_t defStyleAttr,
- uint32_t defStyleRes,
- uint32_t* srcValues, size_t srcValuesLength,
- uint32_t* attrs, size_t attrsLength,
- uint32_t* outValues,
- uint32_t* outIndices) {
+bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
+ uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
+ if (kDebugStyles) {
+ ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr,
+ def_style_res);
+ }
+
+ const ResTable& res = theme->getResTable();
+ ResTable_config config;
+ Res_value value;
+
+ int indices_idx = 0;
+
+ // Load default style from attribute, if specified...
+ uint32_t def_style_bag_type_set_flags = 0;
+ if (def_style_attr != 0) {
+ Res_value value;
+ if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) {
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ def_style_res = value.data;
+ }
+ }
+ }
+
+ // Now lock down the resource object and start pulling stuff from it.
+ res.lock();
+
+ // Retrieve the default style bag, if requested.
+ const ResTable::bag_entry* def_style_start = NULL;
+ uint32_t def_style_type_set_flags = 0;
+ ssize_t bag_off =
+ def_style_res != 0
+ ? res.getBagLocked(def_style_res, &def_style_start, &def_style_type_set_flags)
+ : -1;
+ def_style_type_set_flags |= def_style_bag_type_set_flags;
+ const ResTable::bag_entry* const def_style_end = def_style_start + (bag_off >= 0 ? bag_off : 0);
+ BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end);
+
+ // Now iterate through all of the attributes that the client has requested,
+ // filling in each with whatever data we can find.
+ for (size_t ii = 0; ii < attrs_length; ii++) {
+ const uint32_t cur_ident = attrs[ii];
+
if (kDebugStyles) {
- ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x "
- "defStyleRes=0x%x", theme, defStyleAttr, defStyleRes);
+ ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
}
- const ResTable& res = theme->getResTable();
- ResTable_config config;
- Res_value value;
+ ssize_t block = -1;
+ uint32_t type_set_flags = 0;
- int indicesIdx = 0;
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ config.density = 0;
- // Load default style from attribute, if specified...
- uint32_t defStyleBagTypeSetFlags = 0;
- if (defStyleAttr != 0) {
- Res_value value;
- if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- defStyleRes = value.data;
- }
- }
+ // Try to find a value for this attribute... we prioritize values
+ // coming from, first XML attributes, then XML style, then default
+ // style, and finally the theme.
+
+ // Retrieve the current input value if available.
+ if (src_values_length > 0 && src_values[ii] != 0) {
+ value.dataType = Res_value::TYPE_ATTRIBUTE;
+ value.data = src_values[ii];
+ if (kDebugStyles) {
+ ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
}
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
- // Retrieve the default style bag, if requested.
- const ResTable::bag_entry* defStyleStart = NULL;
- uint32_t defStyleTypeSetFlags = 0;
- ssize_t bagOff = defStyleRes != 0
- ? res.getBagLocked(defStyleRes, &defStyleStart, &defStyleTypeSetFlags) : -1;
- defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
- const ResTable::bag_entry* const defStyleEnd = defStyleStart + (bagOff >= 0 ? bagOff : 0);
- BagAttributeFinder defStyleAttrFinder(defStyleStart, defStyleEnd);
-
- // Now iterate through all of the attributes that the client has requested,
- // filling in each with whatever data we can find.
- ssize_t block = 0;
- uint32_t typeSetFlags;
- for (size_t ii=0; ii<attrsLength; ii++) {
- const uint32_t curIdent = attrs[ii];
-
+ if (value.dataType == Res_value::TYPE_NULL) {
+ const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident);
+ if (def_style_entry != def_style_end) {
+ block = def_style_entry->stringBlock;
+ type_set_flags = def_style_type_set_flags;
+ value = def_style_entry->map.value;
if (kDebugStyles) {
- ALOGI("RETRIEVING ATTR 0x%08x...", curIdent);
+ ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
+ }
+ }
- // Try to find a value for this attribute... we prioritize values
- // coming from, first XML attributes, then XML style, then default
- // style, and finally the theme.
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- typeSetFlags = 0;
- config.density = 0;
-
- // Retrieve the current input value if available.
- if (srcValuesLength > 0 && srcValues[ii] != 0) {
- block = -1;
- value.dataType = Res_value::TYPE_ATTRIBUTE;
- value.data = srcValues[ii];
- if (kDebugStyles) {
- ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
-
- if (value.dataType == Res_value::TYPE_NULL) {
- const ResTable::bag_entry* const defStyleEntry = defStyleAttrFinder.find(curIdent);
- if (defStyleEntry != defStyleEnd) {
- block = defStyleEntry->stringBlock;
- typeSetFlags = defStyleTypeSetFlags;
- value = defStyleEntry->map.value;
- if (kDebugStyles) {
- ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
- }
-
- uint32_t resid = 0;
- if (value.dataType != Res_value::TYPE_NULL) {
- // Take care of resolving the found resource to its final value.
- ssize_t newBlock = theme->resolveAttributeReference(&value, block,
- &resid, &typeSetFlags, &config);
- if (newBlock >= 0) block = newBlock;
- if (kDebugStyles) {
- ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- } else {
- // If we still don't have a value for this attribute, try to find
- // it in the theme!
- ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags);
- if (newBlock >= 0) {
- if (kDebugStyles) {
- ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- newBlock = res.resolveReference(&value, block, &resid,
- &typeSetFlags, &config);
- if (newBlock >= 0) block = newBlock;
- if (kDebugStyles) {
- ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
- }
-
- // Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- if (kDebugStyles) {
- ALOGI("-> Setting to @null!");
- }
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- block = -1;
- }
-
+ uint32_t resid = 0;
+ if (value.dataType != Res_value::TYPE_NULL) {
+ // Take care of resolving the found resource to its final value.
+ ssize_t new_block =
+ theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
+ if (new_block >= 0) block = new_block;
+ if (kDebugStyles) {
+ ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ } else {
+ // If we still don't have a value for this attribute, try to find
+ // it in the theme!
+ ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
+ if (new_block >= 0) {
if (kDebugStyles) {
- ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", curIdent, value.dataType,
- value.data);
+ ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
-
- // Write the final value back to Java.
- outValues[STYLE_TYPE] = value.dataType;
- outValues[STYLE_DATA] = value.data;
- outValues[STYLE_ASSET_COOKIE] = block != -1
- ? static_cast<uint32_t>(res.getTableCookie(block)) : static_cast<uint32_t>(-1);
- outValues[STYLE_RESOURCE_ID] = resid;
- outValues[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
- outValues[STYLE_DENSITY] = config.density;
-
- if (outIndices != NULL && value.dataType != Res_value::TYPE_NULL) {
- indicesIdx++;
- outIndices[indicesIdx] = ii;
+ new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
+ if (new_block >= 0) block = new_block;
+ if (kDebugStyles) {
+ ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
-
- outValues += STYLE_NUM_ENTRIES;
+ }
}
- res.unlock();
-
- if (outIndices != NULL) {
- outIndices[0] = indicesIdx;
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ if (kDebugStyles) {
+ ALOGI("-> Setting to @null!");
+ }
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ block = -1;
}
- return true;
-}
-bool applyStyle(ResTable::Theme* theme, ResXMLParser* xmlParser,
- uint32_t defStyleAttr,
- uint32_t defStyleRes,
- uint32_t* attrs, size_t attrsLength,
- uint32_t* outValues,
- uint32_t* outIndices) {
if (kDebugStyles) {
- ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p",
- theme, defStyleAttr, defStyleRes, xmlParser);
+ ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
}
- const ResTable& res = theme->getResTable();
- ResTable_config config;
+ // Write the final value back to Java.
+ out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_DATA] = value.data;
+ out_values[STYLE_ASSET_COOKIE] =
+ block != -1 ? static_cast<uint32_t>(res.getTableCookie(block)) : static_cast<uint32_t>(-1);
+ out_values[STYLE_RESOURCE_ID] = resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
+ out_values[STYLE_DENSITY] = config.density;
+
+ if (out_indices != NULL && value.dataType != Res_value::TYPE_NULL) {
+ indices_idx++;
+ out_indices[indices_idx] = ii;
+ }
+
+ out_values += STYLE_NUM_ENTRIES;
+ }
+
+ res.unlock();
+
+ if (out_indices != NULL) {
+ out_indices[0] = indices_idx;
+ }
+ return true;
+}
+
+bool ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_res, uint32_t* attrs, size_t attrs_length, uint32_t* out_values,
+ uint32_t* out_indices) {
+ if (kDebugStyles) {
+ ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
+ def_style_attr, def_style_res, xml_parser);
+ }
+
+ const ResTable& res = theme->getResTable();
+ ResTable_config config;
+ Res_value value;
+
+ int indices_idx = 0;
+
+ // Load default style from attribute, if specified...
+ uint32_t def_style_bag_type_set_flags = 0;
+ if (def_style_attr != 0) {
Res_value value;
+ if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) {
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ def_style_res = value.data;
+ }
+ }
+ }
- int indicesIdx = 0;
-
- // Load default style from attribute, if specified...
- uint32_t defStyleBagTypeSetFlags = 0;
- if (defStyleAttr != 0) {
- Res_value value;
- if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- defStyleRes = value.data;
- }
+ // Retrieve the style class associated with the current XML tag.
+ int style = 0;
+ uint32_t style_bag_type_set_flags = 0;
+ if (xml_parser != NULL) {
+ ssize_t idx = xml_parser->indexOfStyle();
+ if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
+ if (value.dataType == value.TYPE_ATTRIBUTE) {
+ if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) {
+ value.dataType = Res_value::TYPE_NULL;
}
+ }
+ if (value.dataType == value.TYPE_REFERENCE) {
+ style = value.data;
+ }
+ }
+ }
+
+ // Now lock down the resource object and start pulling stuff from it.
+ res.lock();
+
+ // Retrieve the default style bag, if requested.
+ const ResTable::bag_entry* def_style_attr_start = NULL;
+ uint32_t def_style_type_set_flags = 0;
+ ssize_t bag_off =
+ def_style_res != 0
+ ? res.getBagLocked(def_style_res, &def_style_attr_start, &def_style_type_set_flags)
+ : -1;
+ def_style_type_set_flags |= def_style_bag_type_set_flags;
+ const ResTable::bag_entry* const def_style_attr_end =
+ def_style_attr_start + (bag_off >= 0 ? bag_off : 0);
+ BagAttributeFinder def_style_attr_finder(def_style_attr_start, def_style_attr_end);
+
+ // Retrieve the style class bag, if requested.
+ const ResTable::bag_entry* style_attr_start = NULL;
+ uint32_t style_type_set_flags = 0;
+ bag_off = style != 0 ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) : -1;
+ style_type_set_flags |= style_bag_type_set_flags;
+ const ResTable::bag_entry* const style_attr_end = style_attr_start + (bag_off >= 0 ? bag_off : 0);
+ BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end);
+
+ // Retrieve the XML attributes, if requested.
+ static const ssize_t kXmlBlock = 0x10000000;
+ XmlAttributeFinder xml_attr_finder(xml_parser);
+ const size_t xml_attr_end = xml_parser != NULL ? xml_parser->getAttributeCount() : 0;
+
+ // Now iterate through all of the attributes that the client has requested,
+ // filling in each with whatever data we can find.
+ for (size_t ii = 0; ii < attrs_length; ii++) {
+ const uint32_t cur_ident = attrs[ii];
+
+ if (kDebugStyles) {
+ ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
}
- // Retrieve the style class associated with the current XML tag.
- int style = 0;
- uint32_t styleBagTypeSetFlags = 0;
- if (xmlParser != NULL) {
- ssize_t idx = xmlParser->indexOfStyle();
- if (idx >= 0 && xmlParser->getAttributeValue(idx, &value) >= 0) {
- if (value.dataType == value.TYPE_ATTRIBUTE) {
- if (theme->getAttribute(value.data, &value, &styleBagTypeSetFlags) < 0) {
- value.dataType = Res_value::TYPE_NULL;
- }
- }
- if (value.dataType == value.TYPE_REFERENCE) {
- style = value.data;
- }
- }
+ ssize_t block = kXmlBlock;
+ uint32_t type_set_flags = 0;
+
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ config.density = 0;
+
+ // Try to find a value for this attribute... we prioritize values
+ // coming from, first XML attributes, then XML style, then default
+ // style, and finally the theme.
+
+ // Walk through the xml attributes looking for the requested attribute.
+ const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
+ if (xml_attr_idx != xml_attr_end) {
+ // We found the attribute we were looking for.
+ xml_parser->getAttributeValue(xml_attr_idx, &value);
+ if (kDebugStyles) {
+ ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
}
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
- // Retrieve the default style bag, if requested.
- const ResTable::bag_entry* defStyleAttrStart = NULL;
- uint32_t defStyleTypeSetFlags = 0;
- ssize_t bagOff = defStyleRes != 0
- ? res.getBagLocked(defStyleRes, &defStyleAttrStart, &defStyleTypeSetFlags) : -1;
- defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
- const ResTable::bag_entry* const defStyleAttrEnd = defStyleAttrStart + (bagOff >= 0 ? bagOff : 0);
- BagAttributeFinder defStyleAttrFinder(defStyleAttrStart, defStyleAttrEnd);
-
- // Retrieve the style class bag, if requested.
- const ResTable::bag_entry* styleAttrStart = NULL;
- uint32_t styleTypeSetFlags = 0;
- bagOff = style != 0 ? res.getBagLocked(style, &styleAttrStart, &styleTypeSetFlags) : -1;
- styleTypeSetFlags |= styleBagTypeSetFlags;
- const ResTable::bag_entry* const styleAttrEnd = styleAttrStart + (bagOff >= 0 ? bagOff : 0);
- BagAttributeFinder styleAttrFinder(styleAttrStart, styleAttrEnd);
-
- // Retrieve the XML attributes, if requested.
- static const ssize_t kXmlBlock = 0x10000000;
- XmlAttributeFinder xmlAttrFinder(xmlParser);
- const size_t xmlAttrEnd = xmlParser != NULL ? xmlParser->getAttributeCount() : 0;
-
- // Now iterate through all of the attributes that the client has requested,
- // filling in each with whatever data we can find.
- ssize_t block = 0;
- uint32_t typeSetFlags;
- for (size_t ii = 0; ii < attrsLength; ii++) {
- const uint32_t curIdent = attrs[ii];
-
+ if (value.dataType == Res_value::TYPE_NULL) {
+ // Walk through the style class values looking for the requested attribute.
+ const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident);
+ if (style_attr_entry != style_attr_end) {
+ // We found the attribute we were looking for.
+ block = style_attr_entry->stringBlock;
+ type_set_flags = style_type_set_flags;
+ value = style_attr_entry->map.value;
if (kDebugStyles) {
- ALOGI("RETRIEVING ATTR 0x%08x...", curIdent);
+ ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
+ }
+ }
- // Try to find a value for this attribute... we prioritize values
- // coming from, first XML attributes, then XML style, then default
- // style, and finally the theme.
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- typeSetFlags = 0;
- config.density = 0;
-
- // Walk through the xml attributes looking for the requested attribute.
- const size_t xmlAttrIdx = xmlAttrFinder.find(curIdent);
- if (xmlAttrIdx != xmlAttrEnd) {
- // We found the attribute we were looking for.
- block = kXmlBlock;
- xmlParser->getAttributeValue(xmlAttrIdx, &value);
- if (kDebugStyles) {
- ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
+ if (value.dataType == Res_value::TYPE_NULL) {
+ // Walk through the default style values looking for the requested attribute.
+ const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident);
+ if (def_style_attr_entry != def_style_attr_end) {
+ // We found the attribute we were looking for.
+ block = def_style_attr_entry->stringBlock;
+ type_set_flags = style_type_set_flags;
+ value = def_style_attr_entry->map.value;
+ if (kDebugStyles) {
+ ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
+ }
+ }
- if (value.dataType == Res_value::TYPE_NULL) {
- // Walk through the style class values looking for the requested attribute.
- const ResTable::bag_entry* const styleAttrEntry = styleAttrFinder.find(curIdent);
- if (styleAttrEntry != styleAttrEnd) {
- // We found the attribute we were looking for.
- block = styleAttrEntry->stringBlock;
- typeSetFlags = styleTypeSetFlags;
- value = styleAttrEntry->map.value;
- if (kDebugStyles) {
- ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
+ uint32_t resid = 0;
+ if (value.dataType != Res_value::TYPE_NULL) {
+ // Take care of resolving the found resource to its final value.
+ ssize_t new_block =
+ theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
+ if (new_block >= 0) {
+ block = new_block;
+ }
+
+ if (kDebugStyles) {
+ ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ } else {
+ // If we still don't have a value for this attribute, try to find
+ // it in the theme!
+ ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
+ if (new_block >= 0) {
+ if (kDebugStyles) {
+ ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
-
- if (value.dataType == Res_value::TYPE_NULL) {
- // Walk through the default style values looking for the requested attribute.
- const ResTable::bag_entry* const defStyleAttrEntry = defStyleAttrFinder.find(curIdent);
- if (defStyleAttrEntry != defStyleAttrEnd) {
- // We found the attribute we were looking for.
- block = defStyleAttrEntry->stringBlock;
- typeSetFlags = styleTypeSetFlags;
- value = defStyleAttrEntry->map.value;
- if (kDebugStyles) {
- ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
- }
-
- uint32_t resid = 0;
- if (value.dataType != Res_value::TYPE_NULL) {
- // Take care of resolving the found resource to its final value.
- ssize_t newBlock = theme->resolveAttributeReference(&value, block,
- &resid, &typeSetFlags, &config);
- if (newBlock >= 0) {
- block = newBlock;
- }
-
- if (kDebugStyles) {
- ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- } else {
- // If we still don't have a value for this attribute, try to find
- // it in the theme!
- ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags);
- if (newBlock >= 0) {
- if (kDebugStyles) {
- ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- newBlock = res.resolveReference(&value, block, &resid,
- &typeSetFlags, &config);
-
- if (newBlock >= 0) {
- block = newBlock;
- }
-
- if (kDebugStyles) {
- ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
- }
-
- // Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- if (kDebugStyles) {
- ALOGI("-> Setting to @null!");
- }
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- block = kXmlBlock;
+ new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
+ if (new_block >= 0) {
+ block = new_block;
}
if (kDebugStyles) {
- ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", curIdent, value.dataType, value.data);
+ ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
-
- // Write the final value back to Java.
- outValues[STYLE_TYPE] = value.dataType;
- outValues[STYLE_DATA] = value.data;
- outValues[STYLE_ASSET_COOKIE] = block != kXmlBlock ?
- static_cast<uint32_t>(res.getTableCookie(block)) : static_cast<uint32_t>(-1);
- outValues[STYLE_RESOURCE_ID] = resid;
- outValues[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
- outValues[STYLE_DENSITY] = config.density;
-
- if (outIndices != NULL && value.dataType != Res_value::TYPE_NULL) {
- indicesIdx++;
- outIndices[indicesIdx] = ii;
- }
-
- outValues += STYLE_NUM_ENTRIES;
+ }
}
- res.unlock();
-
- if (outIndices != NULL) {
- outIndices[0] = indicesIdx;
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ if (kDebugStyles) {
+ ALOGI("-> Setting to @null!");
+ }
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ block = kXmlBlock;
}
- return true;
+
+ if (kDebugStyles) {
+ ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
+ }
+
+ // Write the final value back to Java.
+ out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_DATA] = value.data;
+ out_values[STYLE_ASSET_COOKIE] = block != kXmlBlock
+ ? static_cast<uint32_t>(res.getTableCookie(block))
+ : static_cast<uint32_t>(-1);
+ out_values[STYLE_RESOURCE_ID] = resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
+ out_values[STYLE_DENSITY] = config.density;
+
+ if (out_indices != NULL && value.dataType != Res_value::TYPE_NULL) {
+ indices_idx++;
+ out_indices[indices_idx] = ii;
+ }
+
+ out_values += STYLE_NUM_ENTRIES;
+ }
+
+ res.unlock();
+
+ if (out_indices != NULL) {
+ out_indices[0] = indices_idx;
+ }
+ return true;
}
-bool retrieveAttributes(const ResTable* res, ResXMLParser* xmlParser,
- uint32_t* attrs, size_t attrsLength,
- uint32_t* outValues,
- uint32_t* outIndices) {
- ResTable_config config;
- Res_value value;
+bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
+ ResTable_config config;
+ Res_value value;
- int indicesIdx = 0;
+ int indices_idx = 0;
- // Now lock down the resource object and start pulling stuff from it.
- res->lock();
+ // Now lock down the resource object and start pulling stuff from it.
+ res->lock();
- // Retrieve the XML attributes, if requested.
- const size_t NX = xmlParser->getAttributeCount();
- size_t ix=0;
- uint32_t curXmlAttr = xmlParser->getAttributeNameResID(ix);
+ // Retrieve the XML attributes, if requested.
+ const size_t xml_attr_count = xml_parser->getAttributeCount();
+ size_t ix = 0;
+ uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
- static const ssize_t kXmlBlock = 0x10000000;
+ static const ssize_t kXmlBlock = 0x10000000;
- // Now iterate through all of the attributes that the client has requested,
- // filling in each with whatever data we can find.
- ssize_t block = 0;
- uint32_t typeSetFlags;
- for (size_t ii=0; ii<attrsLength; ii++) {
- const uint32_t curIdent = attrs[ii];
+ // Now iterate through all of the attributes that the client has requested,
+ // filling in each with whatever data we can find.
+ for (size_t ii = 0; ii < attrs_length; ii++) {
+ const uint32_t cur_ident = attrs[ii];
+ ssize_t block = kXmlBlock;
+ uint32_t type_set_flags = 0;
- // Try to find a value for this attribute...
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- typeSetFlags = 0;
- config.density = 0;
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ config.density = 0;
- // Skip through XML attributes until the end or the next possible match.
- while (ix < NX && curIdent > curXmlAttr) {
- ix++;
- curXmlAttr = xmlParser->getAttributeNameResID(ix);
- }
- // Retrieve the current XML attribute if it matches, and step to next.
- if (ix < NX && curIdent == curXmlAttr) {
- block = kXmlBlock;
- xmlParser->getAttributeValue(ix, &value);
- ix++;
- curXmlAttr = xmlParser->getAttributeNameResID(ix);
- }
-
- //printf("Attribute 0x%08x: type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
- uint32_t resid = 0;
- if (value.dataType != Res_value::TYPE_NULL) {
- // Take care of resolving the found resource to its final value.
- //printf("Resolving attribute reference\n");
- ssize_t newBlock = res->resolveReference(&value, block, &resid,
- &typeSetFlags, &config);
- if (newBlock >= 0) block = newBlock;
- }
-
- // Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- }
-
- //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
-
- // Write the final value back to Java.
- outValues[STYLE_TYPE] = value.dataType;
- outValues[STYLE_DATA] = value.data;
- outValues[STYLE_ASSET_COOKIE] = block != kXmlBlock
- ? static_cast<uint32_t>(res->getTableCookie(block)) : static_cast<uint32_t>(-1);
- outValues[STYLE_RESOURCE_ID] = resid;
- outValues[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
- outValues[STYLE_DENSITY] = config.density;
-
- if (outIndices != NULL && value.dataType != Res_value::TYPE_NULL) {
- indicesIdx++;
- outIndices[indicesIdx] = ii;
- }
-
- outValues += STYLE_NUM_ENTRIES;
+ // Try to find a value for this attribute...
+ // Skip through XML attributes until the end or the next possible match.
+ while (ix < xml_attr_count && cur_ident > cur_xml_attr) {
+ ix++;
+ cur_xml_attr = xml_parser->getAttributeNameResID(ix);
+ }
+ // Retrieve the current XML attribute if it matches, and step to next.
+ if (ix < xml_attr_count && cur_ident == cur_xml_attr) {
+ xml_parser->getAttributeValue(ix, &value);
+ ix++;
+ cur_xml_attr = xml_parser->getAttributeNameResID(ix);
}
- res->unlock();
-
- if (outIndices != NULL) {
- outIndices[0] = indicesIdx;
+ uint32_t resid = 0;
+ if (value.dataType != Res_value::TYPE_NULL) {
+ // Take care of resolving the found resource to its final value.
+ // printf("Resolving attribute reference\n");
+ ssize_t new_block = res->resolveReference(&value, block, &resid, &type_set_flags, &config);
+ if (new_block >= 0) block = new_block;
}
- return true;
+
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ block = kXmlBlock;
+ }
+
+ // Write the final value back to Java.
+ out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_DATA] = value.data;
+ out_values[STYLE_ASSET_COOKIE] = block != kXmlBlock
+ ? static_cast<uint32_t>(res->getTableCookie(block))
+ : static_cast<uint32_t>(-1);
+ out_values[STYLE_RESOURCE_ID] = resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
+ out_values[STYLE_DENSITY] = config.density;
+
+ if (out_indices != NULL && value.dataType != Res_value::TYPE_NULL) {
+ indices_idx++;
+ out_indices[indices_idx] = ii;
+ }
+
+ out_values += STYLE_NUM_ENTRIES;
+ }
+
+ res->unlock();
+
+ if (out_indices != NULL) {
+ out_indices[0] = indices_idx;
+ }
+ return true;
}
-} // namespace android
+} // namespace android
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index b423f6c..b16279c 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -739,7 +739,7 @@
ALOGW("CREATING STRING CACHE OF %zu bytes",
static_cast<size_t>(mHeader->stringCount*sizeof(char16_t**)));
#endif
- mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**));
+ mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t*));
if (mCache == NULL) {
ALOGW("No memory trying to allocate decode cache table of %d bytes\n",
(int)(mHeader->stringCount*sizeof(char16_t**)));
@@ -4390,10 +4390,12 @@
if (set->numAttrs >= set->availAttrs) {
// Need to alloc more memory...
const size_t newAvail = set->availAttrs+N;
+ void *oldSet = set;
set = (bag_set*)realloc(set,
sizeof(bag_set)
+ sizeof(bag_entry)*newAvail);
if (set == NULL) {
+ free(oldSet);
return NO_MEMORY;
}
set->availAttrs = newAvail;
@@ -4440,7 +4442,7 @@
pos++;
const size_t size = dtohs(map->value.size);
curOff += size + sizeof(*map)-sizeof(map->value);
- };
+ }
if (curEntry > set->numAttrs) {
set->numAttrs = curEntry;
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 1fe1773..6837f25 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -40,7 +40,7 @@
-Werror \
-Wunused \
-Wunreachable-code \
- -Wno-missing-field-initializers \
+ -Wno-missing-field-initializers
# gtest is broken.
androidfw_test_cflags += -Wno-unnamed-type-template-args
@@ -52,9 +52,10 @@
LOCAL_MODULE := libandroidfw_tests
LOCAL_CFLAGS := $(androidfw_test_cflags)
-LOCAL_SRC_FILES := $(testFiles)
+LOCAL_SRC_FILES := $(testFiles) AttributeResolution_test.cpp
LOCAL_STATIC_LIBRARIES := \
libandroidfw \
+ libbase \
libutils \
libcutils \
liblog \
@@ -76,6 +77,7 @@
LOCAL_SHARED_LIBRARIES := \
libandroidfw \
+ libbase \
libcutils \
libutils \
libui \
diff --git a/libs/androidfw/tests/AttributeFinder_test.cpp b/libs/androidfw/tests/AttributeFinder_test.cpp
index 5054624..d9ed48e 100644
--- a/libs/androidfw/tests/AttributeFinder_test.cpp
+++ b/libs/androidfw/tests/AttributeFinder_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -14,115 +14,105 @@
* limitations under the License.
*/
-#include <androidfw/AttributeFinder.h>
+#include "../AttributeFinder.h"
+#include <android-base/macros.h>
#include <gtest/gtest.h>
using android::BackTrackingAttributeFinder;
class MockAttributeFinder : public BackTrackingAttributeFinder<MockAttributeFinder, int> {
-public:
- MockAttributeFinder(const uint32_t* attrs, int len)
- : BackTrackingAttributeFinder(0, len) {
- mAttrs = new uint32_t[len];
- memcpy(mAttrs, attrs, sizeof(*attrs) * len);
- }
+ public:
+ MockAttributeFinder(const uint32_t* attrs, int len) : BackTrackingAttributeFinder(0, len) {
+ attrs_ = new uint32_t[len];
+ memcpy(attrs_, attrs, sizeof(*attrs) * len);
+ }
- ~MockAttributeFinder() {
- delete mAttrs;
- }
+ ~MockAttributeFinder() { delete attrs_; }
- inline uint32_t getAttribute(const int index) const {
- return mAttrs[index];
- }
+ inline uint32_t GetAttribute(const int index) const { return attrs_[index]; }
-private:
- uint32_t* mAttrs;
+ private:
+ uint32_t* attrs_;
};
-static const uint32_t sortedAttributes[] = {
- 0x01010000, 0x01010001, 0x01010002, 0x01010004,
- 0x02010001, 0x02010010, 0x7f010001
-};
+static const uint32_t kSortedAttributes[] = {0x01010000, 0x01010001, 0x01010002, 0x01010004,
+ 0x02010001, 0x02010010, 0x7f010001};
-static const uint32_t packageUnsortedAttributes[] = {
- 0x02010001, 0x02010010, 0x01010000, 0x01010001,
- 0x01010002, 0x01010004, 0x7f010001
-};
+static const uint32_t kPackageUnsortedAttributes[] = {
+ 0x02010001, 0x02010010, 0x01010000, 0x01010001, 0x01010002, 0x01010004, 0x7f010001};
-static const uint32_t singlePackageAttributes[] = {
- 0x7f010007, 0x7f01000a, 0x7f01000d, 0x00000000
-};
+static const uint32_t kSinglePackageAttributes[] = {0x7f010007, 0x7f01000a, 0x7f01000d, 0x00000000};
TEST(AttributeFinderTest, IteratesSequentially) {
- const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
- MockAttributeFinder finder(sortedAttributes, end);
+ const int end = arraysize(kSortedAttributes);
+ MockAttributeFinder finder(kSortedAttributes, end);
- EXPECT_EQ(0, finder.find(0x01010000));
- EXPECT_EQ(1, finder.find(0x01010001));
- EXPECT_EQ(2, finder.find(0x01010002));
- EXPECT_EQ(3, finder.find(0x01010004));
- EXPECT_EQ(4, finder.find(0x02010001));
- EXPECT_EQ(5, finder.find(0x02010010));
- EXPECT_EQ(6, finder.find(0x7f010001));
- EXPECT_EQ(end, finder.find(0x7f010002));
+ EXPECT_EQ(0, finder.Find(0x01010000));
+ EXPECT_EQ(1, finder.Find(0x01010001));
+ EXPECT_EQ(2, finder.Find(0x01010002));
+ EXPECT_EQ(3, finder.Find(0x01010004));
+ EXPECT_EQ(4, finder.Find(0x02010001));
+ EXPECT_EQ(5, finder.Find(0x02010010));
+ EXPECT_EQ(6, finder.Find(0x7f010001));
+ EXPECT_EQ(end, finder.Find(0x7f010002));
}
TEST(AttributeFinderTest, PackagesAreOutOfOrder) {
- const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
- MockAttributeFinder finder(sortedAttributes, end);
+ const int end = arraysize(kSortedAttributes);
+ MockAttributeFinder finder(kSortedAttributes, end);
- EXPECT_EQ(6, finder.find(0x7f010001));
- EXPECT_EQ(end, finder.find(0x7f010002));
- EXPECT_EQ(4, finder.find(0x02010001));
- EXPECT_EQ(5, finder.find(0x02010010));
- EXPECT_EQ(0, finder.find(0x01010000));
- EXPECT_EQ(1, finder.find(0x01010001));
- EXPECT_EQ(2, finder.find(0x01010002));
- EXPECT_EQ(3, finder.find(0x01010004));
+ EXPECT_EQ(6, finder.Find(0x7f010001));
+ EXPECT_EQ(end, finder.Find(0x7f010002));
+ EXPECT_EQ(4, finder.Find(0x02010001));
+ EXPECT_EQ(5, finder.Find(0x02010010));
+ EXPECT_EQ(0, finder.Find(0x01010000));
+ EXPECT_EQ(1, finder.Find(0x01010001));
+ EXPECT_EQ(2, finder.Find(0x01010002));
+ EXPECT_EQ(3, finder.Find(0x01010004));
}
TEST(AttributeFinderTest, SomeAttributesAreNotFound) {
- const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
- MockAttributeFinder finder(sortedAttributes, end);
+ const int end = arraysize(kSortedAttributes);
+ MockAttributeFinder finder(kSortedAttributes, end);
- EXPECT_EQ(0, finder.find(0x01010000));
- EXPECT_EQ(1, finder.find(0x01010001));
- EXPECT_EQ(2, finder.find(0x01010002));
- EXPECT_EQ(end, finder.find(0x01010003));
- EXPECT_EQ(3, finder.find(0x01010004));
- EXPECT_EQ(end, finder.find(0x01010005));
- EXPECT_EQ(end, finder.find(0x01010006));
- EXPECT_EQ(4, finder.find(0x02010001));
- EXPECT_EQ(end, finder.find(0x02010002));
+ EXPECT_EQ(0, finder.Find(0x01010000));
+ EXPECT_EQ(1, finder.Find(0x01010001));
+ EXPECT_EQ(2, finder.Find(0x01010002));
+ EXPECT_EQ(end, finder.Find(0x01010003));
+ EXPECT_EQ(3, finder.Find(0x01010004));
+ EXPECT_EQ(end, finder.Find(0x01010005));
+ EXPECT_EQ(end, finder.Find(0x01010006));
+ EXPECT_EQ(4, finder.Find(0x02010001));
+ EXPECT_EQ(end, finder.Find(0x02010002));
}
TEST(AttributeFinderTest, FindAttributesInPackageUnsortedAttributeList) {
- const int end = sizeof(packageUnsortedAttributes) / sizeof(*packageUnsortedAttributes);
- MockAttributeFinder finder(packageUnsortedAttributes, end);
+ const int end = arraysize(kPackageUnsortedAttributes);
+ MockAttributeFinder finder(kPackageUnsortedAttributes, end);
- EXPECT_EQ(2, finder.find(0x01010000));
- EXPECT_EQ(3, finder.find(0x01010001));
- EXPECT_EQ(4, finder.find(0x01010002));
- EXPECT_EQ(end, finder.find(0x01010003));
- EXPECT_EQ(5, finder.find(0x01010004));
- EXPECT_EQ(end, finder.find(0x01010005));
- EXPECT_EQ(end, finder.find(0x01010006));
- EXPECT_EQ(0, finder.find(0x02010001));
- EXPECT_EQ(end, finder.find(0x02010002));
- EXPECT_EQ(1, finder.find(0x02010010));
- EXPECT_EQ(6, finder.find(0x7f010001));
+ EXPECT_EQ(2, finder.Find(0x01010000));
+ EXPECT_EQ(3, finder.Find(0x01010001));
+ EXPECT_EQ(4, finder.Find(0x01010002));
+ EXPECT_EQ(end, finder.Find(0x01010003));
+ EXPECT_EQ(5, finder.Find(0x01010004));
+ EXPECT_EQ(end, finder.Find(0x01010005));
+ EXPECT_EQ(end, finder.Find(0x01010006));
+ EXPECT_EQ(0, finder.Find(0x02010001));
+ EXPECT_EQ(end, finder.Find(0x02010002));
+ EXPECT_EQ(1, finder.Find(0x02010010));
+ EXPECT_EQ(6, finder.Find(0x7f010001));
}
TEST(AttributeFinderTest, FindAttributesInSinglePackageAttributeList) {
- const int end = sizeof(singlePackageAttributes) / sizeof(*singlePackageAttributes);
- MockAttributeFinder finder(singlePackageAttributes, end);
+ const int end = arraysize(kSinglePackageAttributes);
+ MockAttributeFinder finder(kSinglePackageAttributes, end);
- EXPECT_EQ(end, finder.find(0x010100f4));
- EXPECT_EQ(end, finder.find(0x010100f5));
- EXPECT_EQ(end, finder.find(0x010100f6));
- EXPECT_EQ(end, finder.find(0x010100f7));
- EXPECT_EQ(end, finder.find(0x010100f8));
- EXPECT_EQ(end, finder.find(0x010100fa));
- EXPECT_EQ(0, finder.find(0x7f010007));
+ EXPECT_EQ(end, finder.Find(0x010100f4));
+ EXPECT_EQ(end, finder.Find(0x010100f5));
+ EXPECT_EQ(end, finder.Find(0x010100f6));
+ EXPECT_EQ(end, finder.Find(0x010100f7));
+ EXPECT_EQ(end, finder.Find(0x010100f8));
+ EXPECT_EQ(end, finder.Find(0x010100fa));
+ EXPECT_EQ(0, finder.Find(0x7f010007));
}
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
new file mode 100644
index 0000000..7fbe6d3
--- /dev/null
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/AttributeResolution.h"
+#include "TestHelpers.h"
+#include "data/styles/R.h"
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+
+using namespace android;
+using android::base::ReadFileToString;
+using com::android::app::R;
+
+class AttributeResolutionTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ std::string test_source_dir = TestSourceDir();
+ std::string contents;
+ LOG_ALWAYS_FATAL_IF(!ReadFileToString(test_source_dir + "/styles/resources.arsc", &contents));
+ LOG_ALWAYS_FATAL_IF(
+ table_.add(contents.data(), contents.size(), 1 /*cookie*/, true /*copyData*/) != NO_ERROR);
+ }
+
+ protected:
+ ResTable table_;
+};
+
+class AttributeResolutionXmlTest : public AttributeResolutionTest {
+ public:
+ virtual void SetUp() override {
+ AttributeResolutionTest::SetUp();
+ std::string test_source_dir = TestSourceDir();
+ std::string contents;
+ LOG_ALWAYS_FATAL_IF(!ReadFileToString(test_source_dir + "/styles/layout.xml", &contents));
+ LOG_ALWAYS_FATAL_IF(xml_parser_.setTo(contents.data(), contents.size(), true /*copyData*/) !=
+ NO_ERROR);
+
+ // Skip to the first tag.
+ while (xml_parser_.next() != ResXMLParser::START_TAG) {
+ }
+ }
+
+ protected:
+ ResXMLTree xml_parser_;
+};
+
+TEST_F(AttributeResolutionTest, Theme) {
+ ResTable::Theme theme(table_);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
+
+ uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+ R::attr::attr_four};
+ std::vector<uint32_t> values;
+ values.resize(arraysize(attrs) * 6);
+
+ ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/,
+ nullptr /*src_values*/, 0 /*src_values_length*/, attrs, arraysize(attrs),
+ values.data(), nullptr /*out_indices*/));
+
+ const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
+
+ const uint32_t* values_cursor = values.data();
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(1u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(3u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(Res_value::DATA_NULL_UNDEFINED, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+}
+
+TEST_F(AttributeResolutionXmlTest, XmlParser) {
+ uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+ R::attr::attr_four};
+ std::vector<uint32_t> values;
+ values.resize(arraysize(attrs) * 6);
+
+ ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs, arraysize(attrs), values.data(),
+ nullptr /*out_indices*/));
+
+ uint32_t* values_cursor = values.data();
+ EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(10u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(R::attr::attr_indirect, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+}
+
+TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
+ ResTable::Theme theme(table_);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
+
+ uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four,
+ R::attr::attr_five};
+ std::vector<uint32_t> values;
+ values.resize(arraysize(attrs) * 6);
+
+ ASSERT_TRUE(ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs,
+ arraysize(attrs), values.data(), nullptr /*out_indices*/));
+
+ const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
+
+ uint32_t* values_cursor = values.data();
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(1u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(10u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(3u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(R::string::string_one, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+}
diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp
index 41a19a7..3d1d5f5 100644
--- a/libs/androidfw/tests/TestHelpers.cpp
+++ b/libs/androidfw/tests/TestHelpers.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -17,32 +17,46 @@
#include "TestHelpers.h"
#include <androidfw/ResourceTypes.h>
-#include <utils/String8.h>
#include <gtest/gtest.h>
+#include <unistd.h>
+#include <utils/String8.h>
+
+std::string TestSourceDir() {
+ const char* dir = getenv("ANDROID_BUILD_TOP");
+ LOG_ALWAYS_FATAL_IF(dir == nullptr, "Environment variable ANDROID_BUILD_TOP must be set");
+ std::string testdir = std::string(dir) + "/frameworks/base/libs/androidfw/tests/data";
+
+ // Check that the directory exists.
+ struct stat filestat;
+ LOG_ALWAYS_FATAL_IF(stat(testdir.c_str(), &filestat) != 0, "test data path '%s' does not exist",
+ testdir.c_str());
+ return testdir;
+}
namespace android {
-::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resourceId, const char* expectedStr) {
- Res_value val;
- ssize_t block = table.getResource(resourceId, &val, MAY_NOT_BE_BAG);
- if (block < 0) {
- return ::testing::AssertionFailure() << "could not find resource";
- }
+::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
+ const char* expected_str) {
+ Res_value val;
+ ssize_t block = table.getResource(resource_id, &val, MAY_NOT_BE_BAG);
+ if (block < 0) {
+ return ::testing::AssertionFailure() << "could not find resource";
+ }
- if (val.dataType != Res_value::TYPE_STRING) {
- return ::testing::AssertionFailure() << "resource is not a string";
- }
+ if (val.dataType != Res_value::TYPE_STRING) {
+ return ::testing::AssertionFailure() << "resource is not a string";
+ }
- const ResStringPool* pool = table.getTableStringBlock(block);
- if (pool == NULL) {
- return ::testing::AssertionFailure() << "table has no string pool for block " << block;
- }
+ const ResStringPool* pool = table.getTableStringBlock(block);
+ if (pool == NULL) {
+ return ::testing::AssertionFailure() << "table has no string pool for block " << block;
+ }
- const String8 actual = pool->string8ObjectAt(val.data);
- if (String8(expectedStr) != actual) {
- return ::testing::AssertionFailure() << actual.string();
- }
- return ::testing::AssertionSuccess() << actual.string();
+ const String8 actual_str = pool->string8ObjectAt(val.data);
+ if (String8(expected_str) != actual_str) {
+ return ::testing::AssertionFailure() << actual_str.string();
+ }
+ return ::testing::AssertionSuccess() << actual_str.string();
}
-} // namespace android
+} // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index ff9be16..5f0c4552 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -1,35 +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.
+ */
+
#ifndef __TEST_HELPERS_H
#define __TEST_HELPERS_H
-#include <ostream>
-
#include <androidfw/ResourceTypes.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
#include <gtest/gtest.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+#include <ostream>
+#include <string>
+
+std::string TestSourceDir();
static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) {
- return out << str.string();
+ return out << str.string();
}
static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) {
- return out << android::String8(str).string();
+ return out << android::String8(str).string();
}
namespace android {
enum { MAY_NOT_BE_BAG = false };
-static inline bool operator==(const android::ResTable_config& a, const android::ResTable_config& b) {
- return a.compare(b) == 0;
+static inline bool operator==(const android::ResTable_config& a,
+ const android::ResTable_config& b) {
+ return a.compare(b) == 0;
}
static inline ::std::ostream& operator<<(::std::ostream& out, const android::ResTable_config& c) {
- return out << c.toString().string();
+ return out << c.toString().string();
}
-::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resourceId, const char* expectedStr);
+::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
+ const char* expected_str);
-} // namespace android
+} // namespace android
-#endif // __TEST_HELPERS_H
+#endif // __TEST_HELPERS_H
diff --git a/libs/androidfw/tests/data/.gitignore b/libs/androidfw/tests/data/.gitignore
deleted file mode 100644
index c05cfb0..0000000
--- a/libs/androidfw/tests/data/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.apk
-*.arsc
diff --git a/libs/androidfw/tests/data/styles/AndroidManifest.xml b/libs/androidfw/tests/data/styles/AndroidManifest.xml
new file mode 100644
index 0000000..5211316
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.app">
+</manifest>
diff --git a/libs/androidfw/tests/data/styles/R.h b/libs/androidfw/tests/data/styles/R.h
new file mode 100644
index 0000000..6dc6ede
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/R.h
@@ -0,0 +1,35 @@
+#include <cstdint>
+
+namespace com {
+namespace android {
+namespace app {
+
+struct R {
+ struct attr {
+ enum : uint32_t {
+ attr_one = 0x7f010000u,
+ attr_two = 0x7f010001u,
+ attr_three = 0x7f010002u,
+ attr_four = 0x7f010003u,
+ attr_five = 0x7f010004u,
+ attr_indirect = 0x7f010005u,
+ };
+ };
+
+ struct string {
+ enum : uint32_t {
+ string_one = 0x7f030000u,
+ };
+ };
+
+ struct style {
+ enum : uint32_t {
+ StyleOne = 0x7f020000u,
+ StyleTwo = 0x7f020001u,
+ };
+ };
+};
+
+} // namespace app
+} // namespace android
+} // namespace com
diff --git a/libs/androidfw/tests/data/styles/build.sh b/libs/androidfw/tests/data/styles/build.sh
new file mode 100755
index 0000000..e763421
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/build.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+aapt package -F package.apk -M AndroidManifest.xml -S res
+unzip -j package.apk resources.arsc res/layout/layout.xml
+rm package.apk
diff --git a/libs/androidfw/tests/data/styles/layout.xml b/libs/androidfw/tests/data/styles/layout.xml
new file mode 100644
index 0000000..4997e71
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/layout.xml
Binary files differ
diff --git a/libs/androidfw/tests/data/styles/res/layout/layout.xml b/libs/androidfw/tests/data/styles/res/layout/layout.xml
new file mode 100644
index 0000000..f3aa0f8
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/res/layout/layout.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<View xmlns:app="http://schemas.android.com/apk/res-auto"
+ app:attr_four="?attr/attr_indirect"
+ app:attr_three="10" />
+
diff --git a/libs/androidfw/tests/data/styles/res/values/styles.xml b/libs/androidfw/tests/data/styles/res/values/styles.xml
new file mode 100644
index 0000000..70c54f6
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/res/values/styles.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <public type="attr" name="attr_one" id="0x7f010000" />
+ <attr name="attr_one" />
+
+ <public type="attr" name="attr_two" id="0x7f010001" />
+ <attr name="attr_two" />
+
+ <public type="attr" name="attr_three" id="0x7f010002" />
+ <attr name="attr_three" />
+
+ <public type="attr" name="attr_four" id="0x7f010003" />
+ <attr name="attr_four" />
+
+ <public type="attr" name="attr_five" id="0x7f010004" />
+ <attr name="attr_five" />
+
+ <public type="attr" name="attr_indirect" id="0x7f010005" />
+ <attr name="attr_indirect" />
+
+ <public type="string" name="string_one" id="0x7f030000" />
+ <string name="string_one">Hi</string>
+
+ <public type="style" name="StyleOne" id="0x7f020000" />
+ <style name="StyleOne">
+ <item name="attr_one">1</item>
+ </style>
+
+ <public type="style" name="StyleTwo" id="0x7f020001" />
+ <style name="StyleTwo" parent="@style/StyleOne">
+ <item name="attr_indirect">3</item>
+ <item name="attr_two">"string"</item>
+ <item name="attr_three">?attr/attr_indirect</item>
+ <item name="attr_five">@string/string_one</item>
+ </style>
+
+</resources>
diff --git a/libs/androidfw/tests/data/styles/resources.arsc b/libs/androidfw/tests/data/styles/resources.arsc
new file mode 100644
index 0000000..8f65c9a
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/resources.arsc
Binary files differ
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1282846..4fe866f 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -10,13 +10,13 @@
HWUI_ENABLE_OPENGL_VALIDATION := false
hwui_src_files := \
+ hwui/Bitmap.cpp \
font/CacheTexture.cpp \
font/Font.cpp \
hwui/Canvas.cpp \
hwui/MinikinSkia.cpp \
hwui/MinikinUtils.cpp \
hwui/PaintImpl.cpp \
- hwui/PixelRef.cpp \
hwui/Typeface.cpp \
renderstate/Blend.cpp \
renderstate/MeshState.cpp \
@@ -38,7 +38,6 @@
utils/Blur.cpp \
utils/GLUtils.cpp \
utils/LinearAllocator.cpp \
- utils/NinePatchImpl.cpp \
utils/StringUtils.cpp \
utils/TestWindowContext.cpp \
utils/VectorDrawableUtils.cpp \
@@ -80,6 +79,7 @@
PathParser.cpp \
PathTessellator.cpp \
PixelBuffer.cpp \
+ ProfileRenderer.cpp \
Program.cpp \
ProgramCache.cpp \
Properties.cpp \
@@ -265,6 +265,7 @@
tests/unit/BakedOpDispatcherTests.cpp \
tests/unit/BakedOpRendererTests.cpp \
tests/unit/BakedOpStateTests.cpp \
+ tests/unit/CanvasContextTests.cpp \
tests/unit/CanvasStateTests.cpp \
tests/unit/ClipAreaTests.cpp \
tests/unit/DamageAccumulatorTests.cpp \
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 6995039..6079d5d 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -46,7 +46,7 @@
const MergedBakedOpList& opList) {
const BakedOpState& firstState = *(opList.states[0]);
- const SkBitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap;
+ Bitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap;
Texture* texture = renderer.caches().textureCache.get(bitmap);
if (!texture) return;
@@ -292,7 +292,7 @@
Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
- SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
+ SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint);
TextDrawFunctor functor(&renderer, &state, renderClip,
x, y, pureTranslate, alpha, mode, op.paint);
@@ -528,7 +528,7 @@
void BakedOpDispatcher::onColorOp(BakedOpRenderer& renderer, const ColorOp& op, const BakedOpState& state) {
SkPaint paint;
paint.setColor(op.color);
- paint.setXfermodeMode(op.mode);
+ paint.setBlendMode(op.mode);
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
@@ -744,7 +744,7 @@
Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
- SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
+ SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint);
TextDrawFunctor functor(&renderer, &state, renderTargetClip,
0.0f, 0.0f, false, alpha, mode, op.paint);
@@ -776,11 +776,11 @@
}
void renderRectForLayer(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state,
- int color, SkXfermode::Mode mode, SkColorFilter* colorFilter) {
+ int color, SkBlendMode mode, SkColorFilter* colorFilter) {
SkPaint paint;
paint.setColor(color);
- paint.setXfermodeMode(mode);
- paint.setColorFilter(colorFilter);
+ paint.setBlendMode(mode);
+ paint.setColorFilter(sk_ref_sp(colorFilter));
RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint);
BakedOpDispatcher::onRectOp(renderer, rectOp, state);
}
@@ -808,11 +808,11 @@
if (CC_UNLIKELY(Properties::debugLayersUpdates)) {
// render debug layer highlight
renderRectForLayer(renderer, op, state,
- 0x7f00ff00, SkXfermode::Mode::kSrcOver_Mode, nullptr);
+ 0x7f00ff00, SkBlendMode::kSrcOver, nullptr);
} else if (CC_UNLIKELY(Properties::debugOverdraw)) {
// render transparent to increment overdraw for repaint area
renderRectForLayer(renderer, op, state,
- SK_ColorTRANSPARENT, SkXfermode::Mode::kSrcOver_Mode, nullptr);
+ SK_ColorTRANSPARENT, SkBlendMode::kSrcOver, nullptr);
}
}
}
@@ -829,14 +829,14 @@
if (op.paint && op.paint->getAlpha() < 255) {
SkPaint layerPaint;
layerPaint.setAlpha(op.paint->getAlpha());
- layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
- layerPaint.setColorFilter(op.paint->getColorFilter());
+ layerPaint.setBlendMode(SkBlendMode::kDstIn);
+ layerPaint.setColorFilter(sk_ref_sp(op.paint->getColorFilter()));
RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint);
BakedOpDispatcher::onRectOp(renderer, rectOp, state);
}
OffscreenBuffer& layer = **(op.layerHandle);
- auto mode = PaintUtils::getXfermodeDirect(op.paint);
+ auto mode = PaintUtils::getBlendModeDirect(op.paint);
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
.setRoundRectClipState(state.roundRectClipState)
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index ac7a600..e8972aa 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -181,7 +181,7 @@
if (!mRenderTarget.frameBufferId) mHasDrawn = true;
}
-Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) {
+Texture* BakedOpRenderer::getTexture(Bitmap* bitmap) {
return mCaches.textureCache.get(bitmap);
}
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 62bc564..4d76a3d 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -74,7 +74,7 @@
void endLayer();
WARN_UNUSED_RESULT OffscreenBuffer* copyToLayer(const Rect& area);
- Texture* getTexture(const SkBitmap* bitmap);
+ Texture* getTexture(Bitmap* bitmap);
const LightInfo& getLightInfo() const { return mLightInfo; }
void renderGlop(const BakedOpState& state, const Glop& glop) {
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index c42ff1a..a7d5f60 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -45,7 +45,7 @@
void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
mAlpha = PaintUtils::getAlphaDirect(paint);
- mMode = PaintUtils::getXfermodeDirect(paint);
+ mMode = PaintUtils::getBlendModeDirect(paint);
SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
SkRefCnt_SafeAssign(mColorFilter, colorFilter);
}
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 7420112..7335008 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -101,7 +101,7 @@
bool mBlend;
SkColorFilter* mColorFilter;
int mAlpha;
- SkXfermode::Mode mMode;
+ SkBlendMode mMode;
sp<GLConsumer> mSurfaceTexture;
SkMatrix* mTransform;
bool mNeedsGLContextAttach;
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 6e7d11f..5213d48 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -106,9 +106,9 @@
bool DisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
std::function<void(RenderNode*, TreeInfo&, bool)> childFn) {
TextureCache& cache = Caches::getInstance().textureCache;
- for (auto&& bitmapResource : bitmapResources) {
+ for (auto& bitmapResource : bitmapResources) {
void* ownerToken = &info.canvasContext;
- info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource);
+ info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource.get());
}
for (auto&& op : children) {
RenderNode* childNode = op->renderNode;
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 06b0891..cab092f 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -38,6 +38,7 @@
#include "Matrix.h"
#include "RenderProperties.h"
#include "TreeInfo.h"
+#include "hwui/Bitmap.h"
#include <vector>
@@ -101,7 +102,7 @@
const LsaVector<NodeOpType*>& getChildren() const { return children; }
- const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
+ const LsaVector<sk_sp<Bitmap>>& getBitmapResources() const { return bitmapResources; }
size_t addChild(NodeOpType* childOp);
@@ -140,7 +141,7 @@
LsaVector<NodeOpType*> children;
// Resources - Skia objects + 9 patches referred to by this DisplayList
- LsaVector<const SkBitmap*> bitmapResources;
+ LsaVector<sk_sp<Bitmap>> bitmapResources;
LsaVector<const SkPath*> pathResources;
LsaVector<const Res_png_9patch*> patchResources;
LsaVector<std::unique_ptr<const SkPaint>> paints;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index e836c20..dd9c40f 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -58,7 +58,7 @@
const BakedOpState* bakedState,
const ClipBase* clip,
float x, float y, bool pureTranslate,
- int alpha, SkXfermode::Mode mode, const SkPaint* paint)
+ int alpha, SkBlendMode mode, const SkPaint* paint)
: renderer(renderer)
, bakedState(bakedState)
, clip(clip)
@@ -79,7 +79,7 @@
float y;
bool pureTranslate;
int alpha;
- SkXfermode::Mode mode;
+ SkBlendMode mode;
const SkPaint* paint;
};
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index dbb66d9..245db1d 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -608,7 +608,7 @@
// MergingDrawBatch::canMergeWith()
if (bakedState->computedState.transform.isSimple()
&& bakedState->computedState.transform.positiveScale()
- && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
+ && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver
&& op.bitmap->colorType() != kAlpha_8_SkColorType
&& hasMergeableClip(*bakedState)) {
mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
@@ -631,7 +631,7 @@
}
void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) {
- const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
+ Bitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
SkPaint* paint = op.vectorDrawable->getPaint();
const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>(op.unmappedBounds,
op.localMatrix,
@@ -683,7 +683,7 @@
if (!bakedState) return; // quick rejected
if (bakedState->computedState.transform.isPureTranslate()
- && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
+ && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver
&& hasMergeableClip(*bakedState)) {
mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
@@ -750,7 +750,7 @@
batchid_t batchId = textBatchId(*(op.paint));
if (bakedState->computedState.transform.isPureTranslate()
- && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
+ && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver
&& hasMergeableClip(*bakedState)) {
mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor());
currentLayer().deferMergeableOp(mAllocator, bakedState, batchId, mergeId);
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 570322d..d3adc32 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -16,6 +16,7 @@
#include "FrameInfoVisualizer.h"
#include "BakedOpRenderer.h"
+#include "IProfileRenderer.h"
#include "utils/Color.h"
#include <cutils/compiler.h>
@@ -88,7 +89,7 @@
}
}
-void FrameInfoVisualizer::draw(ContentRenderer* renderer) {
+void FrameInfoVisualizer::draw(IProfileRenderer& renderer) {
RETURN_IF_DISABLED();
if (mShowDirtyRegions) {
@@ -96,8 +97,8 @@
if (mFlashToggle) {
SkPaint paint;
paint.setColor(0x7fff0000);
- renderer->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
- mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint);
+ renderer.drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
+ mDirtyRegion.fRight, mDirtyRegion.fBottom, paint);
}
}
@@ -111,7 +112,7 @@
info.markSwapBuffers();
info.markFrameCompleted();
- initializeRects(renderer->getViewportHeight(), renderer->getViewportWidth());
+ initializeRects(renderer.getViewportHeight(), renderer.getViewportWidth());
drawGraph(renderer);
drawThreshold(renderer);
}
@@ -194,26 +195,26 @@
}
}
-void FrameInfoVisualizer::drawGraph(ContentRenderer* renderer) {
+void FrameInfoVisualizer::drawGraph(IProfileRenderer& renderer) {
SkPaint paint;
for (size_t i = 0; i < Bar.size(); i++) {
nextBarSegment(Bar[i].start, Bar[i].end);
paint.setColor(Bar[i].color & BAR_FAST_MASK);
- renderer->drawRects(mFastRects.get(), mNumFastRects * 4, &paint);
+ renderer.drawRects(mFastRects.get(), mNumFastRects * 4, paint);
paint.setColor(Bar[i].color & BAR_JANKY_MASK);
- renderer->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint);
+ renderer.drawRects(mJankyRects.get(), mNumJankyRects * 4, paint);
}
}
-void FrameInfoVisualizer::drawThreshold(ContentRenderer* renderer) {
+void FrameInfoVisualizer::drawThreshold(IProfileRenderer& renderer) {
SkPaint paint;
paint.setColor(THRESHOLD_COLOR);
- float yLocation = renderer->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
- renderer->drawRect(0.0f,
+ float yLocation = renderer.getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
+ renderer.drawRect(0.0f,
yLocation - mThresholdStroke/2,
- renderer->getViewportWidth(),
+ renderer.getViewportWidth(),
yLocation + mThresholdStroke/2,
- &paint);
+ paint);
}
bool FrameInfoVisualizer::consumeProperties() {
diff --git a/libs/hwui/FrameInfoVisualizer.h b/libs/hwui/FrameInfoVisualizer.h
index d60c002..b98f501 100644
--- a/libs/hwui/FrameInfoVisualizer.h
+++ b/libs/hwui/FrameInfoVisualizer.h
@@ -28,8 +28,7 @@
namespace android {
namespace uirenderer {
-class BakedOpRenderer;
-typedef BakedOpRenderer ContentRenderer;
+class IProfileRenderer;
// TODO: This is a bit awkward as it needs to match the thing in CanvasContext
// A better abstraction here would be nice but iterators are painful
@@ -47,7 +46,7 @@
void setDensity(float density);
void unionDirty(SkRect* dirty);
- void draw(ContentRenderer* renderer);
+ void draw(IProfileRenderer& renderer);
void dumpData(int fd);
@@ -57,8 +56,8 @@
void initializeRects(const int baseline, const int width);
void nextBarSegment(FrameInfoIndex start, FrameInfoIndex end);
- void drawGraph(ContentRenderer* renderer);
- void drawThreshold(ContentRenderer* renderer);
+ void drawGraph(IProfileRenderer& renderer);
+ void drawThreshold(IProfileRenderer& renderer);
inline float durationMS(size_t index, FrameInfoIndex start, FrameInfoIndex end) {
float duration = mFrameSource[index].duration(start, end) * 0.000001f;
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 65922f6..f14b50a 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -220,9 +220,9 @@
////////////////////////////////////////////////////////////////////////////////
void GlopBuilder::setFill(int color, float alphaScale,
- SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
+ SkBlendMode mode, Blend::ModeOrderSwap modeUsage,
const SkShader* shader, const SkColorFilter* colorFilter) {
- if (mode != SkXfermode::kClear_Mode) {
+ if (mode != SkBlendMode::kClear) {
if (!shader) {
FloatColor c;
c.set(color);
@@ -246,8 +246,8 @@
|| mOutGlop->roundRectClipState
|| PaintUtils::isBlendedShader(shader)
|| PaintUtils::isBlendedColorFilter(colorFilter)
- || mode != SkXfermode::kSrcOver_Mode) {
- if (CC_LIKELY(mode <= SkXfermode::kScreen_Mode)) {
+ || mode != SkBlendMode::kSrcOver) {
+ if (CC_LIKELY(mode <= SkBlendMode::kScreen)) {
Blend::getFactors(mode, modeUsage,
&mOutGlop->blend.src, &mOutGlop->blend.dst);
} else {
@@ -257,12 +257,12 @@
// If the blend mode cannot be implemented using shaders, fall
// back to the default SrcOver blend mode instead
if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) {
- mDescription.framebufferMode = mode;
+ mDescription.framebufferMode = (SkXfermode::Mode)mode;
mDescription.swapSrcDst = (modeUsage == Blend::ModeOrderSwap::Swap);
// blending in shader, don't enable
} else {
// unsupported
- Blend::getFactors(SkXfermode::kSrcOver_Mode, modeUsage,
+ Blend::getFactors(SkBlendMode::kSrcOver, modeUsage,
&mOutGlop->blend.src, &mOutGlop->blend.dst);
}
}
@@ -271,11 +271,11 @@
if (colorFilter) {
SkColor color;
- SkXfermode::Mode mode;
+ SkXfermode::Mode xmode;
SkScalar srcColorMatrix[20];
- if (colorFilter->asColorMode(&color, &mode)) {
+ if (colorFilter->asColorMode(&color, &xmode)) {
mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Blend;
- mDescription.colorMode = mode;
+ mDescription.colorMode = xmode;
mOutGlop->fill.filter.color.set(color);
} else if (colorFilter->asColorMatrix(srcColorMatrix)) {
mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Matrix;
@@ -321,7 +321,7 @@
shader = nullptr;
}
setFill(color, alphaScale,
- PaintUtils::getXfermode(paint->getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ paint->getBlendMode(), Blend::ModeOrderSwap::NoSwap,
shader, paint->getColorFilter());
} else {
mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale };
@@ -330,7 +330,7 @@
|| (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha)
|| texture.blend
|| mOutGlop->roundRectClipState) {
- Blend::getFactors(SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
+ Blend::getFactors(SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap,
&mOutGlop->blend.src, &mOutGlop->blend.dst);
} else {
mOutGlop->blend = { GL_ZERO, GL_ZERO };
@@ -360,7 +360,7 @@
}
setFill(paint.getColor(), alphaScale,
- PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
paint.getShader(), paint.getColorFilter());
mDescription.useShadowAlphaInterp = shadowInterp;
mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
@@ -376,7 +376,7 @@
mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
setFill(paint.getColor(), alphaScale,
- PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
paint.getShader(), paint.getColorFilter());
mDescription.hasAlpha8Texture = true;
@@ -400,7 +400,7 @@
}
setFill(shadowColor, alphaScale,
- PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
paint.getShader(), paint.getColorFilter());
mDescription.hasAlpha8Texture = true;
@@ -413,7 +413,7 @@
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
- setFill(SK_ColorBLACK, 1.0f, SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
+ setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap,
nullptr, nullptr);
return *this;
}
@@ -423,13 +423,13 @@
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
- setFill(SK_ColorBLACK, 1.0f, SkXfermode::kClear_Mode, Blend::ModeOrderSwap::NoSwap,
+ setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kClear, Blend::ModeOrderSwap::NoSwap,
nullptr, nullptr);
return *this;
}
GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
- float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage) {
+ float alpha, SkBlendMode mode, Blend::ModeOrderSwap modeUsage) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
@@ -465,7 +465,7 @@
GL_TEXTURE_EXTERNAL_OES, GL_LINEAR, GL_CLAMP_TO_EDGE,
&textureTransform };
- setFill(SK_ColorWHITE, 1.0f, SkXfermode::kSrc_Mode, Blend::ModeOrderSwap::NoSwap,
+ setFill(SK_ColorWHITE, 1.0f, SkBlendMode::kSrc, Blend::ModeOrderSwap::NoSwap,
nullptr, nullptr);
mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 1f3b53a..d511ccb 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -70,7 +70,7 @@
GlopBuilder& setFillBlack();
GlopBuilder& setFillClear();
GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
- float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage);
+ float alpha, SkBlendMode mode, Blend::ModeOrderSwap modeUsage);
GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
// TODO: Texture should probably know and own its target.
// setFillLayer() forces it to GL_TEXTURE which isn't always correct.
@@ -112,7 +112,7 @@
static void dump(const Glop& glop);
private:
void setFill(int color, float alphaScale,
- SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
+ SkBlendMode mode, Blend::ModeOrderSwap modeUsage,
const SkShader* shader, const SkColorFilter* colorFilter);
enum StageFlags {
diff --git a/libs/hwui/IProfileRenderer.h b/libs/hwui/IProfileRenderer.h
new file mode 100644
index 0000000..947ed34
--- /dev/null
+++ b/libs/hwui/IProfileRenderer.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPaint.h"
+
+namespace android {
+namespace uirenderer {
+
+class IProfileRenderer {
+public:
+ virtual void drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) = 0;
+ virtual void drawRects(const float* rects, int count, const SkPaint& paint) = 0;
+ virtual uint32_t getViewportWidth() = 0;
+ virtual uint32_t getViewportHeight() = 0;
+
+ virtual ~IProfileRenderer() {}
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 01650ef..9874ce2 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -28,7 +28,7 @@
#include <ui/Region.h>
#include <SkPaint.h>
-#include <SkXfermode.h>
+#include <SkBlendMode.h>
#include "Matrix.h"
#include "Rect.h"
@@ -98,7 +98,7 @@
this->alpha = alpha;
}
- inline void setAlpha(int alpha, SkXfermode::Mode mode) {
+ inline void setAlpha(int alpha, SkBlendMode mode) {
this->alpha = alpha;
this->mode = mode;
}
@@ -107,7 +107,7 @@
return alpha;
}
- inline SkXfermode::Mode getMode() const {
+ inline SkBlendMode getMode() const {
return mode;
}
@@ -208,7 +208,7 @@
/**
* Blending mode of the layer.
*/
- SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
+ SkBlendMode mode = SkBlendMode::kSrcOver;
/**
* Optional texture coordinates transform.
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index 66413dc..c5d5492 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -274,7 +274,7 @@
// One or more unclipped saveLayers have been enqueued, with deferred clears.
// Flush all of these clears with a single draw
SkPaint* paint = allocator.create<SkPaint>();
- paint->setXfermodeMode(SkXfermode::kClear_Mode);
+ paint->setBlendMode(SkBlendMode::kClear);
SimpleRectsOp* op = allocator.create_trivial<SimpleRectsOp>(bounds,
Matrix4::identity(), nullptr, paint,
verts, vertCount);
diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h
new file mode 100644
index 0000000..7a271b7
--- /dev/null
+++ b/libs/hwui/NinePatchUtils.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+namespace android {
+namespace NinePatchUtils {
+
+static inline void SetLatticeDivs(SkCanvas::Lattice* lattice, const Res_png_9patch& chunk,
+ int width, int height) {
+ lattice->fXCount = chunk.numXDivs;
+ lattice->fYCount = chunk.numYDivs;
+ lattice->fXDivs = chunk.getXDivs();
+ lattice->fYDivs = chunk.getYDivs();
+
+ // We'll often see ninepatches where the last div is equal to the width or height.
+ // This doesn't provide any additional information and is not supported by Skia.
+ if (lattice->fXCount > 0 && width == lattice->fXDivs[lattice->fXCount - 1]) {
+ lattice->fXCount--;
+ }
+ if (lattice->fYCount > 0 && height == lattice->fYDivs[lattice->fYCount - 1]) {
+ lattice->fYCount--;
+ }
+}
+
+}; // namespace NinePatchUtils
+}; // namespace android
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index e69ea79..d46c46f93 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -17,6 +17,8 @@
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkColor.h>
+#include <SkColorFilter.h>
+#include <SkMaskFilter.h>
#include <SkPaint.h>
#include <SkPath.h>
#include <SkPathEffect.h>
@@ -149,8 +151,7 @@
paint.setColorFilter(nullptr);
paint.setMaskFilter(nullptr);
paint.setShader(nullptr);
- SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
- SkSafeUnref(paint.setXfermode(mode));
+ paint.setBlendMode(SkBlendMode::kSrc);
}
static SkBitmap* drawPath(const SkPath* path, const SkPaint* paint, PathTexture* texture,
diff --git a/libs/hwui/ProfileRenderer.cpp b/libs/hwui/ProfileRenderer.cpp
new file mode 100644
index 0000000..0ad484c
--- /dev/null
+++ b/libs/hwui/ProfileRenderer.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ProfileRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+void ProfileRenderer::drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) {
+ mRenderer.drawRect(left, top, right, bottom, &paint);
+}
+
+void ProfileRenderer::drawRects(const float* rects, int count, const SkPaint& paint) {
+ mRenderer.drawRects(rects, count, &paint);
+}
+
+uint32_t ProfileRenderer::getViewportWidth() {
+ return mRenderer.getViewportWidth();
+}
+
+uint32_t ProfileRenderer::getViewportHeight() {
+ return mRenderer.getViewportHeight();
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/ProfileRenderer.h b/libs/hwui/ProfileRenderer.h
new file mode 100644
index 0000000..b9e586f
--- /dev/null
+++ b/libs/hwui/ProfileRenderer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "IProfileRenderer.h"
+
+#include "BakedOpRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+class ProfileRenderer : public IProfileRenderer {
+public:
+ ProfileRenderer(BakedOpRenderer& renderer)
+ : mRenderer(renderer) {
+ }
+
+ void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
+ void drawRects(const float* rects, int count, const SkPaint& paint) override;
+ uint32_t getViewportWidth() override;
+ uint32_t getViewportHeight() override;
+
+ virtual ~ProfileRenderer() {}
+
+private:
+ BakedOpRenderer& mRenderer;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index ebc41b1..f9a7c36f2 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -26,7 +26,6 @@
#include "Vector.h"
#include <androidfw/ResourceTypes.h>
-#include <SkXfermode.h>
class SkBitmap;
class SkPaint;
@@ -212,14 +211,14 @@
};
struct BitmapOp : RecordedOp {
- BitmapOp(BASE_PARAMS, const SkBitmap* bitmap)
+ BitmapOp(BASE_PARAMS, Bitmap* bitmap)
: SUPER(BitmapOp)
, bitmap(bitmap) {}
- const SkBitmap* bitmap;
+ Bitmap* bitmap;
};
struct BitmapMeshOp : RecordedOp {
- BitmapMeshOp(BASE_PARAMS, const SkBitmap* bitmap, int meshWidth, int meshHeight,
+ BitmapMeshOp(BASE_PARAMS, Bitmap* bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors)
: SUPER(BitmapMeshOp)
, bitmap(bitmap)
@@ -227,7 +226,7 @@
, meshHeight(meshHeight)
, vertices(vertices)
, colors(colors) {}
- const SkBitmap* bitmap;
+ Bitmap* bitmap;
const int meshWidth;
const int meshHeight;
const float* vertices;
@@ -235,11 +234,11 @@
};
struct BitmapRectOp : RecordedOp {
- BitmapRectOp(BASE_PARAMS, const SkBitmap* bitmap, const Rect& src)
+ BitmapRectOp(BASE_PARAMS, Bitmap* bitmap, const Rect& src)
: SUPER(BitmapRectOp)
, bitmap(bitmap)
, src(src) {}
- const SkBitmap* bitmap;
+ Bitmap* bitmap;
const Rect src;
};
@@ -257,12 +256,12 @@
struct ColorOp : RecordedOp {
// Note: unbounded op that will fillclip, so no bounds/matrix needed
- ColorOp(const ClipBase* localClip, int color, SkXfermode::Mode mode)
+ ColorOp(const ClipBase* localClip, int color, SkBlendMode mode)
: RecordedOp(RecordedOpId::ColorOp, Rect(), Matrix4::identity(), localClip, nullptr)
, color(color)
, mode(mode) {}
const int color;
- const SkXfermode::Mode mode;
+ const SkBlendMode mode;
};
struct FunctorOp : RecordedOp {
@@ -289,11 +288,11 @@
};
struct PatchOp : RecordedOp {
- PatchOp(BASE_PARAMS, const SkBitmap* bitmap, const Res_png_9patch* patch)
+ PatchOp(BASE_PARAMS, Bitmap* bitmap, const Res_png_9patch* patch)
: SUPER(PatchOp)
, bitmap(bitmap)
, patch(patch) {}
- const SkBitmap* bitmap;
+ Bitmap* bitmap;
const Res_png_9patch* patch;
};
@@ -504,7 +503,7 @@
: SUPER_PAINTLESS(LayerOp)
, layerHandle(layerHandle)
, alpha(paint ? paint->getAlpha() / 255.0f : 1.0f)
- , mode(PaintUtils::getXfermodeDirect(paint))
+ , mode(PaintUtils::getBlendModeDirect(paint))
, colorFilter(paint ? paint->getColorFilter() : nullptr) {}
explicit LayerOp(RenderNode& node)
@@ -518,7 +517,7 @@
// constructed until after this operation is constructed.
OffscreenBuffer** layerHandle;
const float alpha;
- const SkXfermode::Mode mode;
+ const SkBlendMode mode;
// pointer to object owned by either LayerProperties, or a recorded Paint object in a
// BeginLayerOp. Lives longer than LayerOp in either case, so no skia ref counting is used.
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 27e6a12..f5bcba2 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -36,7 +36,7 @@
"Destroyed a RecordingCanvas during a record!");
}
-void RecordingCanvas::resetRecording(int width, int height) {
+void RecordingCanvas::resetRecording(int width, int height, RenderNode* node) {
LOG_ALWAYS_FATAL_IF(mDisplayList,
"prepareDirty called a second time during a recording!");
mDisplayList = new DisplayList();
@@ -247,7 +247,7 @@
// ----------------------------------------------------------------------------
// android/graphics/Canvas draw operations
// ----------------------------------------------------------------------------
-void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) {
+void RecordingCanvas::drawColor(int color, SkBlendMode mode) {
addOp(alloc().create_trivial<ColorOp>(
getRecordedClip(),
color,
@@ -468,17 +468,17 @@
}
// Bitmap-based
-void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
+void RecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
save(SaveFlags::Matrix);
translate(left, top);
- drawBitmap(&bitmap, paint);
+ drawBitmap(bitmap, paint);
restore();
}
-void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
const SkPaint* paint) {
if (matrix.isIdentity()) {
- drawBitmap(&bitmap, paint);
+ drawBitmap(bitmap, paint);
} else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))
&& MathUtils::isPositive(matrix.getScaleX())
&& MathUtils::isPositive(matrix.getScaleY())) {
@@ -492,12 +492,12 @@
} else {
save(SaveFlags::Matrix);
concat(matrix);
- drawBitmap(&bitmap, paint);
+ drawBitmap(bitmap, paint);
restore();
}
}
-void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+void RecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint) {
if (srcLeft == 0 && srcTop == 0
@@ -508,7 +508,7 @@
// transform simple rect to rect drawing case into position bitmap ops, since they merge
save(SaveFlags::Matrix);
translate(dstLeft, dstTop);
- drawBitmap(&bitmap, paint);
+ drawBitmap(bitmap, paint);
restore();
} else {
addOp(alloc().create_trivial<BitmapRectOp>(
@@ -520,7 +520,7 @@
}
}
-void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+void RecordingCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) {
int vertexCount = (meshWidth + 1) * (meshHeight + 1);
addOp(alloc().create_trivial<BitmapMeshOp>(
@@ -532,7 +532,7 @@
refBuffer<int>(colors, vertexCount))); // 1 color per vertex
}
-void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& patch,
+void RecordingCanvas::drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& patch,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) {
addOp(alloc().create_trivial<PatchOp>(
@@ -575,12 +575,12 @@
}
}
-void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
+void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkPaint* paint) {
addOp(alloc().create_trivial<BitmapOp>(
- Rect(bitmap->width(), bitmap->height()),
+ Rect(bitmap.width(), bitmap.height()),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
- refPaint(paint), refBitmap(*bitmap)));
+ refPaint(paint), refBitmap(bitmap)));
}
void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
@@ -666,7 +666,9 @@
SkBitmap bitmap;
SkShader::TileMode xy[2];
if (shader->isABitmap(&bitmap, nullptr, xy)) {
- refBitmap(bitmap);
+ // TODO: create hwui-owned BitmapShader.
+ Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef());
+ refBitmap(*hwuiBitmap);
return;
}
SkShader::ComposeRec rec;
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index efa6b91..b6031c4 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -22,10 +22,10 @@
#include "ResourceCache.h"
#include "SkiaCanvasProxy.h"
#include "Snapshot.h"
+#include "hwui/Bitmap.h"
#include "hwui/Canvas.h"
#include "utils/LinearAllocator.h"
#include "utils/Macros.h"
-#include "utils/NinePatch.h"
#include <SkDrawFilter.h>
#include <SkPaint.h>
@@ -50,7 +50,7 @@
RecordingCanvas(size_t width, size_t height);
virtual ~RecordingCanvas();
- virtual void resetRecording(int width, int height) override;
+ virtual void resetRecording(int width, int height, RenderNode* node = nullptr) override;
virtual WARN_UNUSED_RESULT DisplayList* finishRecording() override;
// ----------------------------------------------------------------------------
// MISC HWUI OPERATIONS - TODO: CATEGORIZE
@@ -144,7 +144,7 @@
// ----------------------------------------------------------------------------
// android/graphics/Canvas draw operations
// ----------------------------------------------------------------------------
- virtual void drawColor(int color, SkXfermode::Mode mode) override;
+ virtual void drawColor(int color, SkBlendMode mode) override;
virtual void drawPaint(const SkPaint& paint) override;
// Geometry
@@ -176,15 +176,14 @@
virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
// Bitmap-based
- virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
- const SkPaint* paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint) override;
- virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) override;
- virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) override;
@@ -204,7 +203,7 @@
return mState.writableSnapshot()->mutateClipArea().serializeClip(alloc());
}
- void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
+ void drawBitmap(Bitmap& bitmap, const SkPaint* paint);
void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint);
@@ -286,14 +285,17 @@
return cachedRegion;
}
- inline const SkBitmap* refBitmap(const SkBitmap& bitmap) {
+ inline Bitmap* refBitmap(Bitmap& bitmap) {
// Note that this assumes the bitmap is immutable. There are cases this won't handle
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
// contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case.
- SkBitmap* localBitmap = alloc().create<SkBitmap>(bitmap);
- mDisplayList->bitmapResources.push_back(localBitmap);
- return localBitmap;
+
+ // this is required because sk_sp's ctor adopts the pointer,
+ // but does not increment the refcount,
+ bitmap.ref();
+ mDisplayList->bitmapResources.emplace_back(&bitmap);
+ return &bitmap;
}
inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index b0114bc..146fbe7 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -52,7 +52,7 @@
bool LayerProperties::setFromPaint(const SkPaint* paint) {
bool changed = false;
changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
- changed |= setXferMode(PaintUtils::getXfermodeDirect(paint));
+ changed |= setXferMode(PaintUtils::getBlendModeDirect(paint));
changed |= setColorFilter(paint ? paint->getColorFilter() : nullptr);
return changed;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 2f5223c..9ee2f9c 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -24,10 +24,10 @@
#include "utils/MathUtils.h"
#include "utils/PaintUtils.h"
+#include <SkBlendMode.h>
#include <SkCamera.h>
#include <SkMatrix.h>
#include <SkRegion.h>
-#include <SkXfermode.h>
#include <algorithm>
#include <stddef.h>
@@ -93,11 +93,11 @@
return mAlpha;
}
- bool setXferMode(SkXfermode::Mode mode) {
+ bool setXferMode(SkBlendMode mode) {
return RP_SET(mMode, mode);
}
- SkXfermode::Mode xferMode() const {
+ SkBlendMode xferMode() const {
return mMode;
}
@@ -133,7 +133,7 @@
// Whether or not that Layer's content is opaque, doesn't include alpha
bool mOpaque;
uint8_t mAlpha;
- SkXfermode::Mode mMode;
+ SkBlendMode mMode;
SkColorFilter* mColorFilter = nullptr;
};
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 9553ab4..c48b4dc 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -17,7 +17,9 @@
#include "SkiaCanvas.h"
#include "CanvasProperty.h"
+#include "NinePatchUtils.h"
#include "VectorDrawable.h"
+#include "hwui/Bitmap.h"
#include "hwui/MinikinUtils.h"
#include <SkDrawable.h>
@@ -30,11 +32,14 @@
#include <SkRSXform.h>
#include <SkShader.h>
#include <SkTemplates.h>
+#include <SkTextBlob.h>
#include <memory>
namespace android {
+using uirenderer::PaintUtils;
+
Canvas* Canvas::create_canvas(const SkBitmap& bitmap) {
return new SkiaCanvas(bitmap);
}
@@ -43,6 +48,11 @@
return new SkiaCanvas(skiaCanvas);
}
+SkiaCanvas::SkiaCanvas() {}
+
+SkiaCanvas::SkiaCanvas(SkCanvas* canvas)
+ : mCanvas(SkRef(canvas)) {}
+
SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
mCanvas.reset(new SkCanvas(bitmap));
}
@@ -76,21 +86,21 @@
};
void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
- sk_sp<SkCanvas> newCanvas(new SkCanvas(bitmap));
+ SkCanvas* newCanvas = new SkCanvas(bitmap);
if (!bitmap.isNull()) {
// Copy the canvas matrix & clip state.
newCanvas->setMatrix(mCanvas->getTotalMatrix());
- ClipCopier copier(newCanvas.get());
+ ClipCopier copier(newCanvas);
mCanvas->replayClips(&copier);
}
// unrefs the existing canvas
- mCanvas = std::move(newCanvas);
+ mCanvas.reset(newCanvas);
// clean up the old save stack
- mSaveStack.reset(NULL);
+ mSaveStack.reset(nullptr);
}
// ----------------------------------------------------------------------------
@@ -128,13 +138,8 @@
// operation. It does this by explicitly saving off the clip & matrix state
// when requested and playing it back after the SkCanvas::restore.
void SkiaCanvas::restore() {
- const SaveRec* rec = (NULL == mSaveStack.get())
- ? NULL
- : static_cast<SaveRec*>(mSaveStack->back());
- int currentSaveCount = mCanvas->getSaveCount();
- SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
-
- if (NULL == rec || rec->saveCount != currentSaveCount) {
+ const auto* rec = this->currentSaveRec();
+ if (!rec) {
// Fast path - no record for this frame.
mCanvas->restore();
return;
@@ -148,27 +153,18 @@
savedMatrix = mCanvas->getTotalMatrix();
}
- SkTArray<SkClipStack::Element> savedClips;
- int topClipStackFrame = mCanvas->getClipStack()->getSaveCount();
- if (preserveClip) {
- saveClipsForFrame(savedClips, topClipStackFrame);
- }
+ const size_t clipIndex = rec->clipIndex;
mCanvas->restore();
+ mSaveStack->pop_back();
if (preserveMatrix) {
mCanvas->setMatrix(savedMatrix);
}
- if (preserveClip && !savedClips.empty() &&
- topClipStackFrame != mCanvas->getClipStack()->getSaveCount()) {
- // Only reapply the saved clips if the top clip stack frame was actually
- // popped by restore(). If it wasn't, it means it doesn't belong to the
- // restored canvas frame (SkCanvas lazy save/restore kicked in).
- applyClips(savedClips);
+ if (preserveClip) {
+ this->applyPersistentClips(clipIndex);
}
-
- mSaveStack->pop_back();
}
void SkiaCanvas::restoreToCount(int restoreCount) {
@@ -212,6 +208,56 @@
return this->saveLayer(left, top, right, bottom, nullptr, flags);
}
+class SkiaCanvas::Clip {
+public:
+ Clip(const SkRect& rect, SkRegion::Op op, const SkMatrix& m)
+ : mType(Type::Rect), mOp(op), mMatrix(m), mRRect(SkRRect::MakeRect(rect)) {}
+ Clip(const SkRRect& rrect, SkRegion::Op op, const SkMatrix& m)
+ : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {}
+ Clip(const SkPath& path, SkRegion::Op op, const SkMatrix& m)
+ : mType(Type::Path), mOp(op), mMatrix(m), mPath(&path) {}
+
+ void apply(SkCanvas* canvas) const {
+ canvas->setMatrix(mMatrix);
+ switch (mType) {
+ case Type::Rect:
+ canvas->clipRect(mRRect.rect(), mOp);
+ break;
+ case Type::RRect:
+ canvas->clipRRect(mRRect, mOp);
+ break;
+ case Type::Path:
+ canvas->clipPath(*mPath.get(), mOp);
+ break;
+ }
+ }
+
+private:
+ enum class Type {
+ Rect,
+ RRect,
+ Path,
+ };
+
+ Type mType;
+ SkRegion::Op mOp;
+ SkMatrix mMatrix;
+
+ // These are logically a union (tracked separately due to non-POD path).
+ SkTLazy<SkPath> mPath;
+ SkRRect mRRect;
+};
+
+const SkiaCanvas::SaveRec* SkiaCanvas::currentSaveRec() const {
+ const SaveRec* rec = mSaveStack
+ ? static_cast<const SaveRec*>(mSaveStack->back())
+ : nullptr;
+ int currentSaveCount = mCanvas->getSaveCount();
+ SkASSERT(!rec || currentSaveCount >= rec->saveCount);
+
+ return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr;
+}
+
// ----------------------------------------------------------------------------
// functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags)
// ----------------------------------------------------------------------------
@@ -228,45 +274,48 @@
return;
}
- if (NULL == mSaveStack.get()) {
+ if (!mSaveStack) {
mSaveStack.reset(new SkDeque(sizeof(struct SaveRec), 8));
}
SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
rec->saveCount = mCanvas->getSaveCount();
rec->saveFlags = flags;
+ rec->clipIndex = mClipStack.size();
}
-void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips,
- int saveCountToBackup) {
- // Each SkClipStack::Element stores the index of the canvas save
- // with which it is associated. Backup only those Elements that
- // are associated with 'saveCountToBackup'
- SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
- SkClipStack::Iter::kTop_IterStart);
- while (const SkClipStack::Element* elem = clipIterator.prev()) {
- if (elem->getSaveCount() < saveCountToBackup) {
- // done with the target save count.
- break;
- }
- SkASSERT(elem->getSaveCount() == saveCountToBackup);
- clips.push_back(*elem);
+template <typename T>
+void SkiaCanvas::recordClip(const T& clip, SkRegion::Op op) {
+ // Only need tracking when in a partial save frame which
+ // doesn't restore the clip.
+ const SaveRec* rec = this->currentSaveRec();
+ if (rec && !(rec->saveFlags & SaveFlags::Clip)) {
+ mClipStack.emplace_back(clip, op, mCanvas->getTotalMatrix());
}
}
-void SkiaCanvas::applyClips(const SkTArray<SkClipStack::Element>& clips) {
- ClipCopier clipCopier(mCanvas.get());
+// Applies and optionally removes all clips >= index.
+void SkiaCanvas::applyPersistentClips(size_t clipStartIndex) {
+ SkASSERT(clipStartIndex <= mClipStack.size());
+ const auto begin = mClipStack.cbegin() + clipStartIndex;
+ const auto end = mClipStack.cend();
- // The clip stack stores clips in device space.
- SkMatrix origMatrix = mCanvas->getTotalMatrix();
- mCanvas->resetMatrix();
+ // Clip application mutates the CTM.
+ const SkMatrix saveMatrix = mCanvas->getTotalMatrix();
- // We pushed the clips in reverse order.
- for (int i = clips.count() - 1; i >= 0; --i) {
- clips[i].replay(&clipCopier);
+ for (auto clip = begin; clip != end; ++clip) {
+ clip->apply(mCanvas.get());
}
- mCanvas->setMatrix(origMatrix);
+ mCanvas->setMatrix(saveMatrix);
+
+ // If the current/post-restore save rec is also persisting clips, we
+ // leave them on the stack to be reapplied part of the next restore().
+ // Otherwise we're done and just pop them.
+ const auto* rec = this->currentSaveRec();
+ if (!rec || (rec->saveFlags & SaveFlags::Clip)) {
+ mClipStack.erase(begin, end);
+ }
}
// ----------------------------------------------------------------------------
@@ -342,6 +391,7 @@
bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ this->recordClip(rect, op);
mCanvas->clipRect(rect, op);
return !mCanvas->isClipEmpty();
}
@@ -349,8 +399,10 @@
bool SkiaCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
SkRRect roundRect;
if (path->isRRect(&roundRect)) {
+ this->recordClip(roundRect, op);
mCanvas->clipRRect(roundRect, op);
} else {
+ this->recordClip(*path, op);
mCanvas->clipPath(*path, op);
}
return !mCanvas->isClipEmpty();
@@ -362,10 +414,13 @@
// The region is specified in device space.
SkMatrix savedMatrix = mCanvas->getTotalMatrix();
mCanvas->resetMatrix();
+ this->recordClip(rgnPath, op);
mCanvas->clipPath(rgnPath, op);
mCanvas->setMatrix(savedMatrix);
} else {
- mCanvas->clipRect(SkRect::MakeEmpty(), op);
+ const auto emptyClip = SkRect::MakeEmpty();
+ this->recordClip(emptyClip, op);
+ mCanvas->clipRect(emptyClip, op);
}
return !mCanvas->isClipEmpty();
}
@@ -386,7 +441,7 @@
// Canvas draw operations
// ----------------------------------------------------------------------------
-void SkiaCanvas::drawColor(int color, SkXfermode::Mode mode) {
+void SkiaCanvas::drawColor(int color, SkBlendMode mode) {
mCanvas->drawColor(color, mode);
}
@@ -400,6 +455,7 @@
void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint,
SkCanvas::PointMode mode) {
+ if (CC_UNLIKELY(count < 2 || PaintUtils::paintWillNotDraw(paint))) return;
// convert the floats into SkPoints
count >>= 1; // now it is the number of points
std::unique_ptr<SkPoint[]> pts(new SkPoint[count]);
@@ -425,45 +481,49 @@
}
void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
+ if (CC_UNLIKELY(count < 4 || PaintUtils::paintWillNotDraw(paint))) return;
this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
}
void SkiaCanvas::drawRect(float left, float top, float right, float bottom,
const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
mCanvas->drawRectCoords(left, top, right, bottom, paint);
}
void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
- SkRegion::Iterator it(region);
- while (!it.done()) {
- mCanvas->drawRect(SkRect::Make(it.rect()), paint);
- it.next();
- }
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+ mCanvas->drawRegion(region, paint);
}
void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
mCanvas->drawRoundRect(rect, rx, ry, paint);
}
void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
+ if (CC_UNLIKELY(radius <= 0 || PaintUtils::paintWillNotDraw(paint))) return;
mCanvas->drawCircle(x, y, radius, paint);
}
void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
mCanvas->drawOval(oval, paint);
}
void SkiaCanvas::drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
}
void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
SkRect rect;
SkRRect roundRect;
if (path.isOval(&rect)) {
@@ -490,27 +550,34 @@
// Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
-void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
- mCanvas->drawBitmap(bitmap, left, top, paint);
+void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
+ SkBitmap skBitmap;
+ bitmap.getSkBitmap(&skBitmap);
+ mCanvas->drawBitmap(skBitmap, left, top, paint);
}
-void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
+void SkiaCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix, const SkPaint* paint) {
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
SkAutoCanvasRestore acr(mCanvas.get(), true);
mCanvas->concat(matrix);
mCanvas->drawBitmap(bitmap, 0, 0, paint);
}
-void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+void SkiaCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint) {
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
mCanvas->drawBitmapRect(bitmap, srcRect, dstRect, paint);
}
-void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) {
-
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
const int ptCount = (meshWidth + 1) * (meshHeight + 1);
const int indexCount = meshWidth * meshHeight * 6;
@@ -595,23 +662,96 @@
if (paint) {
tmpPaint = *paint;
}
- sk_sp<SkShader> shader = SkMakeBitmapShader(bitmap,
- SkShader::kClamp_TileMode,
- SkShader::kClamp_TileMode,
- nullptr,
- kNever_SkCopyPixelsMode,
- nullptr);
- tmpPaint.setShader(std::move(shader));
+
+ sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+ tmpPaint.setShader(image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode));
mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices,
texs, (const SkColor*)colors, NULL, indices,
indexCount, tmpPaint);
}
-void SkiaCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& chunk,
+static inline int num_distinct_rects(const SkCanvas::Lattice& lattice) {
+ int xRects;
+ if (lattice.fXCount > 0) {
+ xRects = (0 == lattice.fXDivs[0]) ? lattice.fXCount : lattice.fXCount + 1;
+ } else {
+ xRects = 1;
+ }
+
+ int yRects;
+ if (lattice.fYCount > 0) {
+ yRects = (0 == lattice.fYDivs[0]) ? lattice.fYCount : lattice.fYCount + 1;
+ } else {
+ yRects = 1;
+ }
+ return xRects * yRects;
+}
+
+static inline void set_lattice_flags(SkCanvas::Lattice* lattice, SkCanvas::Lattice::Flags* flags,
+ int numFlags, const Res_png_9patch& chunk) {
+ lattice->fFlags = flags;
+ sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::Flags));
+
+ bool needPadRow = lattice->fYCount > 0 && 0 == lattice->fYDivs[0];
+ bool needPadCol = lattice->fXCount > 0 && 0 == lattice->fXDivs[0];
+
+ int yCount = lattice->fYCount;
+ if (needPadRow) {
+ // Skip flags for the degenerate first row of rects.
+ flags += lattice->fXCount + 1;
+ yCount--;
+ }
+
+ int i = 0;
+ bool setFlags = false;
+ for (int y = 0; y < yCount + 1; y++) {
+ for (int x = 0; x < lattice->fXCount + 1; x++) {
+ if (0 == x && needPadCol) {
+ // First rect of each column is degenerate, skip the flag.
+ flags++;
+ continue;
+ }
+
+ if (0 == chunk.getColors()[i++]) {
+ *flags = SkCanvas::Lattice::kTransparent_Flags;
+ setFlags = true;
+ }
+
+ flags++;
+ }
+ }
+
+ if (!setFlags) {
+ lattice->fFlags = nullptr;
+ }
+}
+
+void SkiaCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk,
float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
- SkRect bounds = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- NinePatch::Draw(mCanvas.get(), bounds, bitmap, chunk, paint, nullptr);
+
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
+
+ SkCanvas::Lattice lattice;
+ NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
+
+ lattice.fFlags = nullptr;
+ int numFlags = 0;
+ if (chunk.numColors > 0 && chunk.numColors == num_distinct_rects(lattice)) {
+ // We can expect the framework to give us a color for every distinct rect.
+ // Skia requires a flag for every rect.
+ numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
+ }
+
+ SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags);
+ if (numFlags > 0) {
+ set_lattice_flags(&lattice, flags.get(), numFlags, chunk);
+ }
+
+ lattice.fBounds = nullptr;
+ SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+ mCanvas->drawBitmapLattice(bitmap, lattice, dst, paint);
}
void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
@@ -626,9 +766,26 @@
const SkPaint& paint, float x, float y,
float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
float totalAdvance) {
- static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
- mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paint);
- drawTextDecorations(x, y, totalAdvance, paint);
+ if (!text || !positions || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
+ // Set align to left for drawing, as we don't want individual
+ // glyphs centered or right-aligned; the offset above takes
+ // care of all alignment.
+ SkPaint paintCopy(paint);
+ paintCopy.setTextAlign(SkPaint::kLeft_Align);
+
+ SkRect bounds = SkRect::MakeLTRB(boundsLeft + x, boundsTop + y,
+ boundsRight + x, boundsBottom + y);
+
+ SkTextBlobBuilder builder;
+ const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(paintCopy, count, &bounds);
+ // TODO: we could reduce the number of memcpy's if the this were exposed further up
+ // in the architecture.
+ memcpy(buffer.glyphs, text, count * sizeof(uint16_t));
+ memcpy(buffer.pos, positions, (count << 1) * sizeof(float));
+
+ sk_sp<SkTextBlob> textBlob(builder.make());
+ mCanvas->drawTextBlob(textBlob, 0, 0, paintCopy);
+ drawTextDecorations(x, y, totalAdvance, paintCopy);
}
void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
@@ -733,7 +890,7 @@
// Canvas draw operations: View System
// ----------------------------------------------------------------------------
-void SkiaCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layer) {
+void SkiaCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw Layers");
}
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 0e506f4..d1edff9 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -22,8 +22,7 @@
#include "VectorDrawable.h"
#include <SkCanvas.h>
-#include <SkClipStack.h>
-#include <SkTArray.h>
+#include <SkTLazy.h>
namespace android {
@@ -39,16 +38,14 @@
* not be NULL. This constructor will ref() the SkCanvas, and unref()
* it in its destructor.
*/
- explicit SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {
- SkASSERT(canvas);
- canvas->ref();
- }
+ explicit SkiaCanvas(SkCanvas* canvas);
virtual SkCanvas* asSkCanvas() override {
return mCanvas.get();
}
- virtual void resetRecording(int width, int height) override {
+ virtual void resetRecording(int width, int height,
+ uirenderer::RenderNode* renderNode) override {
LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas");
}
@@ -100,7 +97,7 @@
virtual SkDrawFilter* getDrawFilter() override;
virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
- virtual void drawColor(int color, SkXfermode::Mode mode) override;
+ virtual void drawColor(int color, SkBlendMode mode) override;
virtual void drawPaint(const SkPaint& paint) override;
virtual void drawPoint(float x, float y, const SkPaint& paint) override;
@@ -123,16 +120,14 @@
const float* verts, const float* tex, const int* colors,
const uint16_t* indices, int indexCount, const SkPaint& paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
- const SkPaint* paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
- const SkPaint* paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint) override;
- virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) override;
- virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) override;
@@ -153,7 +148,7 @@
uirenderer::GlFunctorLifecycleListener* listener) override;
protected:
- explicit SkiaCanvas() {}
+ SkiaCanvas();
void reset(SkCanvas* skiaCanvas);
void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); }
@@ -168,19 +163,26 @@
struct SaveRec {
int saveCount;
SaveFlags::Flags saveFlags;
+ size_t clipIndex;
};
bool mHighContrastText = false;
+ const SaveRec* currentSaveRec() const;
void recordPartialSave(SaveFlags::Flags flags);
- void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount);
- void applyClips(const SkTArray<SkClipStack::Element>& clips);
+
+ template<typename T>
+ void recordClip(const T&, SkRegion::Op);
+ void applyPersistentClips(size_t clipStartIndex);
void drawPoints(const float* points, int count, const SkPaint& paint,
SkCanvas::PointMode mode);
- sk_sp<SkCanvas> mCanvas;
+ class Clip;
+
+ sk_sp<SkCanvas> mCanvas;
std::unique_ptr<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
+ std::vector<Clip> mClipStack; // tracks persistent clips.
};
} // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index fded604..863146e 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -16,6 +16,8 @@
#include "SkiaCanvasProxy.h"
+#include "hwui/Bitmap.h"
+
#include <cutils/log.h>
#include <SkPatchUtils.h>
#include <SkPaint.h>
@@ -105,16 +107,12 @@
void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
const SkPaint* paint) {
- SkPixelRef* pxRef = bitmap.pixelRef();
-
+ sk_sp<Bitmap> hwuiBitmap = Bitmap::createFrom(bitmap.info(), *bitmap.pixelRef());
// HWUI doesn't support extractSubset(), so convert any subsetted bitmap into
// a drawBitmapRect(); pass through an un-subsetted bitmap.
- if (pxRef && bitmap.dimensions() != pxRef->info().dimensions()) {
- SkBitmap fullBitmap;
- fullBitmap.setInfo(pxRef->info());
- fullBitmap.setPixelRef(pxRef, 0, 0);
+ if (hwuiBitmap && bitmap.dimensions() != hwuiBitmap->info().dimensions()) {
SkIPoint origin = bitmap.pixelRefOrigin();
- mCanvas->drawBitmap(fullBitmap, origin.fX, origin.fY,
+ mCanvas->drawBitmap(*hwuiBitmap, origin.fX, origin.fY,
origin.fX + bitmap.dimensions().width(),
origin.fY + bitmap.dimensions().height(),
left, top,
@@ -122,15 +120,16 @@
top + bitmap.dimensions().height(),
paint);
} else {
- mCanvas->drawBitmap(bitmap, left, top, paint);
+ mCanvas->drawBitmap(*hwuiBitmap, left, top, paint);
}
}
-void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr,
+void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& skBitmap, const SkRect* srcPtr,
const SkRect& dst, const SkPaint* paint, SrcRectConstraint) {
- SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height());
+ SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(skBitmap.width(), skBitmap.height());
// TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
- mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+ Bitmap* bitmap = reinterpret_cast<Bitmap*>(skBitmap.pixelRef());
+ mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
}
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 5d9e5c0..489a306 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -21,6 +21,7 @@
#include "Layer.h"
#include "Matrix.h"
#include "Texture.h"
+#include "hwui/Bitmap.h"
#include <SkMatrix.h>
#include <utils/Log.h>
@@ -206,7 +207,9 @@
return false;
}
- outData->bitmapTexture = caches.textureCache.get(&bitmap);
+ // TODO: create hwui-owned BitmapShader.
+ Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef());
+ outData->bitmapTexture = caches.textureCache.get(hwuiBitmap);
if (!outData->bitmapTexture) return false;
outData->bitmapSampler = (*textureUnit)++;
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 5ccdbda..08641b7 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -23,6 +23,7 @@
#include "TextureCache.h"
#include "Properties.h"
#include "utils/TraceUtils.h"
+#include "hwui/Bitmap.h"
namespace android {
namespace uirenderer {
@@ -91,7 +92,7 @@
}
}
-bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) {
+bool TextureCache::canMakeTextureFromBitmap(Bitmap* bitmap) {
if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)",
bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize);
@@ -102,8 +103,8 @@
// Returns a prepared Texture* that either is already in the cache or can fit
// in the cache (and is thus added to the cache)
-Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) {
- Texture* texture = mCache.get(bitmap->pixelRef()->getStableID());
+Texture* TextureCache::getCachedTexture(Bitmap* bitmap) {
+ Texture* texture = mCache.get(bitmap->getStableID());
if (!texture) {
if (!canMakeTextureFromBitmap(bitmap)) {
@@ -126,7 +127,9 @@
texture = new Texture(Caches::getInstance());
texture->bitmapSize = size;
texture->generation = bitmap->getGenerationID();
- texture->upload(*bitmap);
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ texture->upload(skBitmap);
mSize += size;
TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
@@ -134,19 +137,21 @@
if (mDebugEnabled) {
ALOGD("Texture created, size = %d", size);
}
- mCache.put(bitmap->pixelRef()->getStableID(), texture);
+ mCache.put(bitmap->getStableID(), texture);
}
} else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
// Texture was in the cache but is dirty, re-upload
// TODO: Re-adjust the cache size if the bitmap's dimensions have changed
- texture->upload(*bitmap);
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ texture->upload(skBitmap);
texture->generation = bitmap->getGenerationID();
}
return texture;
}
-bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap) {
+bool TextureCache::prefetchAndMarkInUse(void* ownerToken, Bitmap* bitmap) {
Texture* texture = getCachedTexture(bitmap);
if (texture) {
texture->isInUse = ownerToken;
@@ -154,11 +159,11 @@
return texture;
}
-bool TextureCache::prefetch(const SkBitmap* bitmap) {
+bool TextureCache::prefetch(Bitmap* bitmap) {
return getCachedTexture(bitmap);
}
-Texture* TextureCache::get(const SkBitmap* bitmap) {
+Texture* TextureCache::get(Bitmap* bitmap) {
Texture* texture = getCachedTexture(bitmap);
if (!texture) {
@@ -169,7 +174,9 @@
const uint32_t size = bitmap->rowBytes() * bitmap->height();
texture = new Texture(Caches::getInstance());
texture->bitmapSize = size;
- texture->upload(*bitmap);
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ texture->upload(skBitmap);
texture->generation = bitmap->getGenerationID();
texture->cleanup = true;
}
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 88ef771..68a548b 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -28,6 +28,9 @@
#include <unordered_map>
namespace android {
+
+class Bitmap;
+
namespace uirenderer {
class Texture;
@@ -73,20 +76,20 @@
* acquired for the bitmap, false otherwise. If a Texture was acquired it is
* marked as in use.
*/
- bool prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap);
+ bool prefetchAndMarkInUse(void* ownerToken, Bitmap* bitmap);
/**
* Attempts to precache the SkBitmap. Returns true if a Texture was successfully
* acquired for the bitmap, false otherwise. Does not mark the Texture
* as in use and won't update currently in-use Textures.
*/
- bool prefetch(const SkBitmap* bitmap);
+ bool prefetch(Bitmap* bitmap);
/**
* Returns the texture associated with the specified bitmap from within the cache.
* If the texture cannot be found in the cache, a new texture is generated.
*/
- Texture* get(const SkBitmap* bitmap);
+ Texture* get(Bitmap* bitmap);
/**
* Removes the texture associated with the specified pixelRef. This is meant
@@ -119,9 +122,9 @@
void flush();
private:
- bool canMakeTextureFromBitmap(const SkBitmap* bitmap);
+ bool canMakeTextureFromBitmap(Bitmap* bitmap);
- Texture* getCachedTexture(const SkBitmap* bitmap);
+ Texture* getCachedTexture(Bitmap* bitmap);
LruCache<uint32_t, Texture*> mCache;
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index a2f3cb6..b50647a 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -201,10 +201,7 @@
SkPaint paint;
if (properties.getFillGradient() != nullptr) {
paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha()));
- SkShader* newShader = properties.getFillGradient()->newWithLocalMatrix(matrix);
- // newWithLocalMatrix(...) creates a new SkShader and returns a bare pointer. We need to
- // remove the extra ref so that the ref count is correctly managed.
- paint.setShader(newShader)->unref();
+ paint.setShader(properties.getFillGradient()->makeWithLocalMatrix(matrix));
needsFill = true;
} else if (properties.getFillColor() != SK_ColorTRANSPARENT) {
paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha()));
@@ -223,10 +220,7 @@
bool needsStroke = false;
if (properties.getStrokeGradient() != nullptr) {
paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha()));
- SkShader* newShader = properties.getStrokeGradient()->newWithLocalMatrix(matrix);
- // newWithLocalMatrix(...) creates a new SkShader and returns a bare pointer. We need to
- // remove the extra ref so that the ref count is correctly managed.
- paint.setShader(newShader)->unref();
+ paint.setShader(properties.getStrokeGradient()->makeWithLocalMatrix(matrix));
needsStroke = true;
} else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) {
paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha()));
@@ -508,18 +502,18 @@
}
void Tree::drawStaging(Canvas* outCanvas) {
- bool redrawNeeded = allocateBitmapIfNeeded(&mStagingCache.bitmap,
+ bool redrawNeeded = allocateBitmapIfNeeded(mStagingCache,
mStagingProperties.getScaledWidth(), mStagingProperties.getScaledHeight());
// draw bitmap cache
if (redrawNeeded || mStagingCache.dirty) {
- updateBitmapCache(&mStagingCache.bitmap, true);
+ updateBitmapCache(*mStagingCache.bitmap, true);
mStagingCache.dirty = false;
}
SkPaint tmpPaint;
SkPaint* paint = updatePaint(&tmpPaint, &mStagingProperties);
- outCanvas->drawBitmap(mStagingCache.bitmap, 0, 0,
- mStagingCache.bitmap.width(), mStagingCache.bitmap.height(),
+ outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0,
+ mStagingCache.bitmap->width(), mStagingCache.bitmap->height(),
mStagingProperties.getBounds().left(), mStagingProperties.getBounds().top(),
mStagingProperties.getBounds().right(), mStagingProperties.getBounds().bottom(), paint);
}
@@ -534,53 +528,53 @@
if (prop->getRootAlpha() == 1.0f && prop->getColorFilter() == nullptr) {
return nullptr;
} else {
- outPaint->setColorFilter(prop->getColorFilter());
+ outPaint->setColorFilter(sk_ref_sp(prop->getColorFilter()));
outPaint->setFilterQuality(kLow_SkFilterQuality);
outPaint->setAlpha(prop->getRootAlpha() * 255);
return outPaint;
}
}
-const SkBitmap& Tree::getBitmapUpdateIfDirty() {
- bool redrawNeeded = allocateBitmapIfNeeded(&mCache.bitmap, mProperties.getScaledWidth(),
+Bitmap& Tree::getBitmapUpdateIfDirty() {
+ bool redrawNeeded = allocateBitmapIfNeeded(mCache, mProperties.getScaledWidth(),
mProperties.getScaledHeight());
if (redrawNeeded || mCache.dirty) {
- updateBitmapCache(&mCache.bitmap, false);
+ updateBitmapCache(*mCache.bitmap, false);
mCache.dirty = false;
}
- return mCache.bitmap;
+ return *mCache.bitmap;
}
-void Tree::updateBitmapCache(SkBitmap* outCache, bool useStagingData) {
- outCache->eraseColor(SK_ColorTRANSPARENT);
- SkCanvas outCanvas(*outCache);
+void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
+ SkBitmap outCache;
+ bitmap.getSkBitmap(&outCache);
+ outCache.eraseColor(SK_ColorTRANSPARENT);
+ SkCanvas outCanvas(outCache);
float viewportWidth = useStagingData ?
mStagingProperties.getViewportWidth() : mProperties.getViewportWidth();
float viewportHeight = useStagingData ?
mStagingProperties.getViewportHeight() : mProperties.getViewportHeight();
- float scaleX = outCache->width() / viewportWidth;
- float scaleY = outCache->height() / viewportHeight;
+ float scaleX = outCache.width() / viewportWidth;
+ float scaleY = outCache.height() / viewportHeight;
mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY, useStagingData);
}
-bool Tree::allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height) {
- if (!canReuseBitmap(*outCache, width, height)) {
+bool Tree::allocateBitmapIfNeeded(Cache& cache, int width, int height) {
+ if (!canReuseBitmap(cache.bitmap.get(), width, height)) {
#ifndef ANDROID_ENABLE_LINEAR_BLENDING
sk_sp<SkColorSpace> colorSpace = nullptr;
#else
sk_sp<SkColorSpace> colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
#endif
SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace);
- outCache->setInfo(info);
- // TODO: Count the bitmap cache against app's java heap
- outCache->allocPixels(info);
+ cache.bitmap = Bitmap::allocateHeapBitmap(info);
return true;
}
return false;
}
-bool Tree::canReuseBitmap(const SkBitmap& bitmap, int width, int height) {
- return width == bitmap.width() && height == bitmap.height();
+bool Tree::canReuseBitmap(Bitmap* bitmap, int width, int height) {
+ return bitmap && width == bitmap->width() && height == bitmap->height();
}
void Tree::onPropertyChanged(TreeProperties* prop) {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index e68fbf4..e9a9c71 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -18,6 +18,7 @@
#define ANDROID_HWUI_VPATH_H
#include "hwui/Canvas.h"
+#include "hwui/Bitmap.h"
#include "DisplayList.h"
#include <SkBitmap.h>
@@ -561,7 +562,7 @@
const SkRect& bounds, bool needsMirroring, bool canReuseCache);
void drawStaging(Canvas* canvas);
- const SkBitmap& getBitmapUpdateIfDirty();
+ Bitmap& getBitmapUpdateIfDirty();
void setAllowCaching(bool allowCaching) {
mAllowCaching = allowCaching;
}
@@ -691,11 +692,15 @@
void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; }
private:
+ struct Cache {
+ sk_sp<Bitmap> bitmap;
+ bool dirty = true;
+ };
SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
- bool allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height);
- bool canReuseBitmap(const SkBitmap&, int width, int height);
- void updateBitmapCache(SkBitmap* outCache, bool useStagingData);
+ bool allocateBitmapIfNeeded(Cache& cache, int width, int height);
+ bool canReuseBitmap(Bitmap*, int width, int height);
+ void updateBitmapCache(Bitmap& outCache, bool useStagingData);
// Cap the bitmap size, such that it won't hurt the performance too much
// and it won't crash due to a very large scale.
// The drawable will look blurry above this size.
@@ -708,10 +713,6 @@
TreeProperties mStagingProperties = TreeProperties(this);
SkPaint mPaint;
- struct Cache {
- SkBitmap bitmap;
- bool dirty = true;
- };
Cache mStagingCache;
Cache mCache;
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
new file mode 100644
index 0000000..31fbe68
--- /dev/null
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "Bitmap.h"
+
+#include "Caches.h"
+
+#include <cutils/log.h>
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+
+namespace android {
+
+static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) {
+ int32_t rowBytes32 = SkToS32(rowBytes);
+ int64_t bigSize = (int64_t) height * rowBytes32;
+ if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
+ return false; // allocation will be too large
+ }
+
+ *size = sk_64_asS32(bigSize);
+ return true;
+}
+
+typedef sk_sp<Bitmap> (*AllocPixeRef)(size_t allocSize, const SkImageInfo& info, size_t rowBytes,
+ SkColorTable* ctable);
+
+static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, SkColorTable* ctable, AllocPixeRef alloc) {
+ const SkImageInfo& info = bitmap->info();
+ if (info.colorType() == kUnknown_SkColorType) {
+ LOG_ALWAYS_FATAL("unknown bitmap configuration");
+ return nullptr;
+ }
+
+ size_t size;
+
+ // we must respect the rowBytes value already set on the bitmap instead of
+ // attempting to compute our own.
+ const size_t rowBytes = bitmap->rowBytes();
+ if (!computeAllocationSize(rowBytes, bitmap->height(), &size)) {
+ return nullptr;
+ }
+
+ auto wrapper = alloc(size, info, rowBytes, ctable);
+ if (wrapper) {
+ wrapper->getSkBitmap(bitmap);
+ // since we're already allocated, we lockPixels right away
+ // HeapAllocator behaves this way too
+ bitmap->lockPixels();
+ }
+ return wrapper;
+}
+
+sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
+ return allocateBitmap(bitmap, ctable, &Bitmap::allocateAshmemBitmap);
+}
+
+static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes,
+ SkColorTable* ctable) {
+ void* addr = calloc(size, 1);
+ if (!addr) {
+ return nullptr;
+ }
+ return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes, ctable));
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
+ return allocateBitmap(bitmap, ctable, &android::allocateHeapBitmap);
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
+ size_t size;
+ if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
+ LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
+ return nullptr;
+ }
+ return android::allocateHeapBitmap(size, info, info.minRowBytes(), nullptr);
+}
+
+sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info,
+ size_t rowBytes, SkColorTable* ctable) {
+ // Create new ashmem region with read/write priv
+ int fd = ashmem_create_region("bitmap", size);
+ if (fd < 0) {
+ return nullptr;
+ }
+
+ void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ close(fd);
+ return nullptr;
+ }
+
+ if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
+ munmap(addr, size);
+ close(fd);
+ return nullptr;
+ }
+ return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes, ctable));
+}
+
+void FreePixelRef(void* addr, void* context) {
+ auto pixelRef = (SkPixelRef*) context;
+ pixelRef->unlockPixels();
+ pixelRef->unref();
+}
+
+sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) {
+ pixelRef.ref();
+ pixelRef.lockPixels();
+ return sk_sp<Bitmap>(new Bitmap((void*) pixelRef.pixels(), (void*) &pixelRef, FreePixelRef,
+ info, pixelRef.rowBytes(), pixelRef.colorTable()));
+}
+
+void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes, SkColorTable* ctable) {
+ if (kIndex_8_SkColorType != newInfo.colorType()) {
+ ctable = nullptr;
+ }
+ mRowBytes = rowBytes;
+ if (mColorTable.get() != ctable) {
+ mColorTable.reset(ctable);
+ }
+
+ // Need to validate the alpha type to filter against the color type
+ // to prevent things like a non-opaque RGB565 bitmap
+ SkAlphaType alphaType;
+ LOG_ALWAYS_FATAL_IF(!SkColorTypeValidateAlphaType(
+ newInfo.colorType(), newInfo.alphaType(), &alphaType),
+ "Failed to validate alpha type!");
+
+ // Dirty hack is dirty
+ // TODO: Figure something out here, Skia's current design makes this
+ // really hard to work with. Skia really, really wants immutable objects,
+ // but with the nested-ref-count hackery going on that's just not
+ // feasible without going insane trying to figure it out
+ SkImageInfo* myInfo = const_cast<SkImageInfo*>(&this->info());
+ *myInfo = newInfo;
+ changeAlphaType(alphaType);
+
+ // Docs say to only call this in the ctor, but we're going to call
+ // it anyway even if this isn't always the ctor.
+ // TODO: Fix this too as part of the above TODO
+ setPreLocked(getStorage(), mRowBytes, mColorTable.get());
+}
+
+Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+ : SkPixelRef(info)
+ , mPixelStorageType(PixelStorageType::Heap) {
+ mPixelStorage.heap.address = address;
+ mPixelStorage.heap.size = size;
+ reconfigure(info, rowBytes, ctable);
+}
+
+Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc,
+ const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+ : SkPixelRef(info)
+ , mPixelStorageType(PixelStorageType::External) {
+ mPixelStorage.external.address = address;
+ mPixelStorage.external.context = context;
+ mPixelStorage.external.freeFunc = freeFunc;
+ reconfigure(info, rowBytes, ctable);
+}
+
+Bitmap::Bitmap(void* address, int fd, size_t mappedSize,
+ const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+ : SkPixelRef(info)
+ , mPixelStorageType(PixelStorageType::Ashmem) {
+ mPixelStorage.ashmem.address = address;
+ mPixelStorage.ashmem.fd = fd;
+ mPixelStorage.ashmem.size = mappedSize;
+ reconfigure(info, rowBytes, ctable);
+}
+
+Bitmap::~Bitmap() {
+ switch (mPixelStorageType) {
+ case PixelStorageType::External:
+ mPixelStorage.external.freeFunc(mPixelStorage.external.address,
+ mPixelStorage.external.context);
+ break;
+ case PixelStorageType::Ashmem:
+ munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
+ close(mPixelStorage.ashmem.fd);
+ break;
+ case PixelStorageType::Heap:
+ free(mPixelStorage.heap.address);
+ break;
+ }
+
+ if (android::uirenderer::Caches::hasInstance()) {
+ android::uirenderer::Caches::getInstance().textureCache.releaseTexture(getStableID());
+ }
+}
+
+bool Bitmap::hasHardwareMipMap() const {
+ return mHasHardwareMipMap;
+}
+
+void Bitmap::setHasHardwareMipMap(bool hasMipMap) {
+ mHasHardwareMipMap = hasMipMap;
+}
+
+void* Bitmap::getStorage() const {
+ switch (mPixelStorageType) {
+ case PixelStorageType::External:
+ return mPixelStorage.external.address;
+ case PixelStorageType::Ashmem:
+ return mPixelStorage.ashmem.address;
+ case PixelStorageType::Heap:
+ return mPixelStorage.heap.address;
+ }
+}
+
+bool Bitmap::onNewLockPixels(LockRec* rec) {
+ rec->fPixels = getStorage();
+ rec->fRowBytes = mRowBytes;
+ rec->fColorTable = mColorTable.get();
+ return true;
+}
+
+size_t Bitmap::getAllocatedSizeInBytes() const {
+ return info().getSafeSize(mRowBytes);
+}
+
+int Bitmap::getAshmemFd() const {
+ switch (mPixelStorageType) {
+ case PixelStorageType::Ashmem:
+ return mPixelStorage.ashmem.fd;
+ default:
+ return -1;
+ }
+}
+
+size_t Bitmap::getAllocationByteCount() const {
+ switch (mPixelStorageType) {
+ case PixelStorageType::Heap:
+ return mPixelStorage.heap.size;
+ default:
+ return rowBytes() * height();
+ }
+}
+
+void Bitmap::reconfigure(const SkImageInfo& info) {
+ reconfigure(info, info.minRowBytes(), nullptr);
+}
+
+void Bitmap::setAlphaType(SkAlphaType alphaType) {
+ if (!SkColorTypeValidateAlphaType(info().colorType(), alphaType, &alphaType)) {
+ return;
+ }
+
+ changeAlphaType(alphaType);
+}
+
+void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
+ outBitmap->setInfo(info(), rowBytes());
+ outBitmap->setPixelRef(this);
+ outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
+}
+
+void Bitmap::getBounds(SkRect* bounds) const {
+ SkASSERT(bounds);
+ bounds->set(0, 0, SkIntToScalar(info().width()), SkIntToScalar(info().height()));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/hwui/hwui/PixelRef.h b/libs/hwui/hwui/Bitmap.h
similarity index 72%
rename from libs/hwui/hwui/PixelRef.h
rename to libs/hwui/hwui/Bitmap.h
index 3f8aea6..e86ac11 100644
--- a/libs/hwui/hwui/PixelRef.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -31,13 +31,21 @@
typedef void (*FreeFunc)(void* addr, void* context);
-class ANDROID_API PixelRef : public SkPixelRef {
+class ANDROID_API Bitmap : public SkPixelRef {
public:
- PixelRef(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes,
+ static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable);
+ static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info);
+
+ static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable);
+ static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info,
+ size_t rowBytes, SkColorTable* ctable);
+
+ static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&);
+ Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes,
SkColorTable* ctable);
- PixelRef(void* address, void* context, FreeFunc freeFunc,
+ Bitmap(void* address, void* context, FreeFunc freeFunc,
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
- PixelRef(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
+ Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
size_t rowBytes, SkColorTable* ctable);
int width() const { return info().width(); }
@@ -46,7 +54,7 @@
// Can't mark as override since SkPixelRef::rowBytes isn't virtual
// but that's OK since we just want Bitmap to be able to rely
// on calling rowBytes() on an unlocked pixelref, which it will be
- // doing on a PixelRef type, not a SkPixelRef, so static
+ // doing on a Bitmap type, not a SkPixelRef, so static
// dispatching will do what we want.
size_t rowBytes() const { return mRowBytes; }
void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
@@ -58,17 +66,21 @@
int getAshmemFd() const;
size_t getAllocationByteCount() const;
+ void setHasHardwareMipMap(bool hasMipMap);
+ bool hasHardwareMipMap() const;
+
+ bool isOpaque() const {return info().isOpaque(); }
+ SkColorType colorType() const { return info().colorType(); }
+ void getBounds(SkRect* bounds) const;
+
protected:
virtual bool onNewLockPixels(LockRec* rec) override;
virtual void onUnlockPixels() override { };
virtual size_t getAllocatedSizeInBytes() const override;
private:
- friend class Bitmap;
- virtual ~PixelRef();
+ virtual ~Bitmap();
void doFreePixels();
void* getStorage() const;
- void setHasHardwareMipMap(bool hasMipMap);
- bool hasHardwareMipMap() const;
PixelStorageType mPixelStorageType;
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index b18e794..1ea8bd2 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -17,6 +17,7 @@
#include "Canvas.h"
#include "RecordingCanvas.h"
+#include "RenderNode.h"
#include "MinikinUtils.h"
#include "Paint.h"
#include "Typeface.h"
@@ -25,7 +26,7 @@
namespace android {
-Canvas* Canvas::create_recording_canvas(int width, int height) {
+Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
return new uirenderer::RecordingCanvas(width, height);
}
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index d76143b..d7839b4 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -21,7 +21,7 @@
#include "GlFunctorLifecycleListener.h"
#include "utils/Macros.h"
-#include "utils/NinePatch.h"
+#include <androidfw/ResourceTypes.h>
#include <SkBitmap.h>
#include <SkCanvas.h>
@@ -65,6 +65,7 @@
};
typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
+class Bitmap;
class Paint;
struct Typeface;
@@ -74,7 +75,23 @@
static Canvas* create_canvas(const SkBitmap& bitmap);
- static WARN_UNUSED_RESULT Canvas* create_recording_canvas(int width, int height);
+ /**
+ * Create a new Canvas object that records view system drawing operations for deferred
+ * rendering. A canvas returned by this call supports calls to the resetRecording(...) and
+ * finishRecording() calls. The latter call returns a DisplayList that is specific to the
+ * RenderPipeline defined by Properties::getRenderPipelineType().
+ *
+ * @param width of the requested Canvas.
+ * @param height of the requested Canvas.
+ * @param renderNode is an optional parameter that specifies the node that will consume the
+ * DisplayList produced by the returned Canvas. This enables the reuse of select C++
+ * objects as a speed optimization.
+ * @return new non-null Canvas Object. The type of DisplayList produced by this canvas is
+ determined based on Properties::getRenderPipelineType().
+ *
+ */
+ static WARN_UNUSED_RESULT Canvas* create_recording_canvas(int width, int height,
+ uirenderer::RenderNode* renderNode = nullptr);
/**
* Create a new Canvas object which delegates to an SkCanvas.
@@ -83,7 +100,8 @@
* delegated to this object. This function will call ref() on the
* SkCanvas, and the returned Canvas will unref() it upon
* destruction.
- * @return new Canvas object. Will not return NULL.
+ * @return new non-null Canvas Object. The type of DisplayList produced by this canvas is
+ * determined based on Properties::getRenderPipelineType().
*/
static Canvas* create_canvas(SkCanvas* skiaCanvas);
@@ -112,7 +130,8 @@
// View System operations (not exposed in public Canvas API)
// ----------------------------------------------------------------------------
- virtual void resetRecording(int width, int height) = 0;
+ virtual void resetRecording(int width, int height,
+ uirenderer::RenderNode* renderNode = nullptr) = 0;
virtual uirenderer::DisplayList* finishRecording() = 0;
virtual void insertReorderBarrier(bool enableReorder) = 0;
@@ -174,7 +193,7 @@
// ----------------------------------------------------------------------------
// Canvas draw operations
// ----------------------------------------------------------------------------
- virtual void drawColor(int color, SkXfermode::Mode mode) = 0;
+ virtual void drawColor(int color, SkBlendMode mode) = 0;
virtual void drawPaint(const SkPaint& paint) = 0;
// Geometry
@@ -199,16 +218,16 @@
const uint16_t* indices, int indexCount, const SkPaint& paint) = 0;
// Bitmap-based
- virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
+ virtual void drawBitmap(Bitmap& bitmap, float left, float top,
const SkPaint* paint) = 0;
- virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
const SkPaint* paint) = 0;
- virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint) = 0;
- virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) = 0;
- virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) = 0;
diff --git a/libs/hwui/hwui/PixelRef.cpp b/libs/hwui/hwui/PixelRef.cpp
deleted file mode 100644
index 795de6b..0000000
--- a/libs/hwui/hwui/PixelRef.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "PixelRef.h"
-
-#include "Caches.h"
-
-#include <cutils/log.h>
-#include <sys/mman.h>
-#include <cutils/ashmem.h>
-
-namespace android {
-
-void PixelRef::reconfigure(const SkImageInfo& newInfo, size_t rowBytes, SkColorTable* ctable) {
- if (kIndex_8_SkColorType != newInfo.colorType()) {
- ctable = nullptr;
- }
- mRowBytes = rowBytes;
- if (mColorTable.get() != ctable) {
- mColorTable.reset(ctable);
- }
-
- // Need to validate the alpha type to filter against the color type
- // to prevent things like a non-opaque RGB565 bitmap
- SkAlphaType alphaType;
- LOG_ALWAYS_FATAL_IF(!SkColorTypeValidateAlphaType(
- newInfo.colorType(), newInfo.alphaType(), &alphaType),
- "Failed to validate alpha type!");
-
- // Dirty hack is dirty
- // TODO: Figure something out here, Skia's current design makes this
- // really hard to work with. Skia really, really wants immutable objects,
- // but with the nested-ref-count hackery going on that's just not
- // feasible without going insane trying to figure it out
- SkImageInfo* myInfo = const_cast<SkImageInfo*>(&this->info());
- *myInfo = newInfo;
- changeAlphaType(alphaType);
-
- // Docs say to only call this in the ctor, but we're going to call
- // it anyway even if this isn't always the ctor.
- // TODO: Fix this too as part of the above TODO
- setPreLocked(getStorage(), mRowBytes, mColorTable.get());
-}
-
-PixelRef::PixelRef(void* address, size_t size, const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
- : SkPixelRef(info)
- , mPixelStorageType(PixelStorageType::Heap) {
- mPixelStorage.heap.address = address;
- mPixelStorage.heap.size = size;
- reconfigure(info, rowBytes, ctable);
-}
-
-PixelRef::PixelRef(void* address, void* context, FreeFunc freeFunc,
- const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
- : SkPixelRef(info)
- , mPixelStorageType(PixelStorageType::External) {
- mPixelStorage.external.address = address;
- mPixelStorage.external.context = context;
- mPixelStorage.external.freeFunc = freeFunc;
- reconfigure(info, rowBytes, ctable);
-}
-
-PixelRef::PixelRef(void* address, int fd, size_t mappedSize,
- const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
- : SkPixelRef(info)
- , mPixelStorageType(PixelStorageType::Ashmem) {
- mPixelStorage.ashmem.address = address;
- mPixelStorage.ashmem.fd = fd;
- mPixelStorage.ashmem.size = mappedSize;
- reconfigure(info, rowBytes, ctable);
-}
-
-PixelRef::~PixelRef() {
- switch (mPixelStorageType) {
- case PixelStorageType::External:
- mPixelStorage.external.freeFunc(mPixelStorage.external.address,
- mPixelStorage.external.context);
- break;
- case PixelStorageType::Ashmem:
- munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
- close(mPixelStorage.ashmem.fd);
- break;
- case PixelStorageType::Heap:
- free(mPixelStorage.heap.address);
- break;
- }
-
- if (android::uirenderer::Caches::hasInstance()) {
- android::uirenderer::Caches::getInstance().textureCache.releaseTexture(getStableID());
- }
-}
-
-bool PixelRef::hasHardwareMipMap() const {
- return mHasHardwareMipMap;
-}
-
-void PixelRef::setHasHardwareMipMap(bool hasMipMap) {
- mHasHardwareMipMap = hasMipMap;
-}
-
-void* PixelRef::getStorage() const {
- switch (mPixelStorageType) {
- case PixelStorageType::External:
- return mPixelStorage.external.address;
- case PixelStorageType::Ashmem:
- return mPixelStorage.ashmem.address;
- case PixelStorageType::Heap:
- return mPixelStorage.heap.address;
- }
-}
-
-bool PixelRef::onNewLockPixels(LockRec* rec) {
- rec->fPixels = getStorage();
- rec->fRowBytes = mRowBytes;
- rec->fColorTable = mColorTable.get();
- return true;
-}
-
-size_t PixelRef::getAllocatedSizeInBytes() const {
- return info().getSafeSize(mRowBytes);
-}
-
-int PixelRef::getAshmemFd() const {
- switch (mPixelStorageType) {
- case PixelStorageType::Ashmem:
- return mPixelStorage.ashmem.fd;
- default:
- return -1;
- }
-}
-
-size_t PixelRef::getAllocationByteCount() const {
- switch (mPixelStorageType) {
- case PixelStorageType::Heap:
- return mPixelStorage.heap.size;
- default:
- return rowBytes() * height();
- }
-}
-
-void PixelRef::reconfigure(const SkImageInfo& info) {
- reconfigure(info, info.minRowBytes(), nullptr);
-}
-
-void PixelRef::setAlphaType(SkAlphaType alphaType) {
- if (!SkColorTypeValidateAlphaType(info().colorType(), alphaType, &alphaType)) {
- return;
- }
-
- changeAlphaType(alphaType);
-}
-
-void PixelRef::getSkBitmap(SkBitmap* outBitmap) {
- outBitmap->setInfo(info(), rowBytes());
- outBitmap->setPixelRef(this);
- outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
-}
-
-} // namespace android
\ No newline at end of file
diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp
index 93f787d..8865c6e 100644
--- a/libs/hwui/renderstate/Blend.cpp
+++ b/libs/hwui/renderstate/Blend.cpp
@@ -25,70 +25,70 @@
* Structure mapping Skia xfermodes to OpenGL blending factors.
*/
struct Blender {
- SkXfermode::Mode mode;
+ SkBlendMode mode;
GLenum src;
GLenum dst;
};
// assumptions made by lookup tables in either this file or ProgramCache
-static_assert(0 == SkXfermode::kClear_Mode, "SkXfermode enums have changed");
-static_assert(1 == SkXfermode::kSrc_Mode, "SkXfermode enums have changed");
-static_assert(2 == SkXfermode::kDst_Mode, "SkXfermode enums have changed");
-static_assert(3 == SkXfermode::kSrcOver_Mode, "SkXfermode enums have changed");
-static_assert(4 == SkXfermode::kDstOver_Mode, "SkXfermode enums have changed");
-static_assert(5 == SkXfermode::kSrcIn_Mode, "SkXfermode enums have changed");
-static_assert(6 == SkXfermode::kDstIn_Mode, "SkXfermode enums have changed");
-static_assert(7 == SkXfermode::kSrcOut_Mode, "SkXfermode enums have changed");
-static_assert(8 == SkXfermode::kDstOut_Mode, "SkXfermode enums have changed");
-static_assert(9 == SkXfermode::kSrcATop_Mode, "SkXfermode enums have changed");
-static_assert(10 == SkXfermode::kDstATop_Mode, "SkXfermode enums have changed");
-static_assert(11 == SkXfermode::kXor_Mode, "SkXfermode enums have changed");
-static_assert(12 == SkXfermode::kPlus_Mode, "SkXfermode enums have changed");
-static_assert(13 == SkXfermode::kModulate_Mode, "SkXfermode enums have changed");
-static_assert(14 == SkXfermode::kScreen_Mode, "SkXfermode enums have changed");
-static_assert(15 == SkXfermode::kOverlay_Mode, "SkXfermode enums have changed");
-static_assert(16 == SkXfermode::kDarken_Mode, "SkXfermode enums have changed");
-static_assert(17 == SkXfermode::kLighten_Mode, "SkXfermode enums have changed");
+static_assert(0 == static_cast<int>(SkBlendMode::kClear), "SkBlendMode enums have changed");
+static_assert(1 == static_cast<int>(SkBlendMode::kSrc), "SkBlendMode enums have changed");
+static_assert(2 == static_cast<int>(SkBlendMode::kDst), "SkBlendMode enums have changed");
+static_assert(3 == static_cast<int>(SkBlendMode::kSrcOver), "SkBlendMode enums have changed");
+static_assert(4 == static_cast<int>(SkBlendMode::kDstOver), "SkBlendMode enums have changed");
+static_assert(5 == static_cast<int>(SkBlendMode::kSrcIn), "SkBlendMode enums have changed");
+static_assert(6 == static_cast<int>(SkBlendMode::kDstIn), "SkBlendMode enums have changed");
+static_assert(7 == static_cast<int>(SkBlendMode::kSrcOut), "SkBlendMode enums have changed");
+static_assert(8 == static_cast<int>(SkBlendMode::kDstOut), "SkBlendMode enums have changed");
+static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "SkBlendMode enums have changed");
+static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "SkBlendMode enums have changed");
+static_assert(11 == static_cast<int>(SkBlendMode::kXor), "SkBlendMode enums have changed");
+static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "SkBlendMode enums have changed");
+static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "SkBlendMode enums have changed");
+static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "SkBlendMode enums have changed");
+static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "SkBlendMode enums have changed");
+static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "SkBlendMode enums have changed");
+static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "SkBlendMode enums have changed");
// In this array, the index of each Blender equals the value of the first
-// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
+// entry. For instance, gBlends[1] == gBlends[SkBlendMode::kSrc]
const Blender kBlends[] = {
- { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_ZERO, GL_SRC_COLOR },
- { SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
+ { SkBlendMode::kClear, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kSrc, GL_ONE, GL_ZERO },
+ { SkBlendMode::kDst, GL_ZERO, GL_ONE },
+ { SkBlendMode::kSrcOver, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kDstOver, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+ { SkBlendMode::kSrcIn, GL_DST_ALPHA, GL_ZERO },
+ { SkBlendMode::kDstIn, GL_ZERO, GL_SRC_ALPHA },
+ { SkBlendMode::kSrcOut, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkBlendMode::kDstOut, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kSrcATop, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kDstATop, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+ { SkBlendMode::kXor, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kPlus, GL_ONE, GL_ONE },
+ { SkBlendMode::kModulate, GL_ZERO, GL_SRC_COLOR },
+ { SkBlendMode::kScreen, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
};
-// This array contains the swapped version of each SkXfermode. For instance
+// This array contains the swapped version of each SkBlendMode. For instance
// this array's SrcOver blending mode is actually DstOver. You can refer to
// createLayer() for more information on the purpose of this array.
const Blender kBlendsSwap[] = {
- { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_DST_COLOR, GL_ZERO },
- { SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE }
+ { SkBlendMode::kClear, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkBlendMode::kSrc, GL_ZERO, GL_ONE },
+ { SkBlendMode::kDst, GL_ONE, GL_ZERO },
+ { SkBlendMode::kSrcOver, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+ { SkBlendMode::kDstOver, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kSrcIn, GL_ZERO, GL_SRC_ALPHA },
+ { SkBlendMode::kDstIn, GL_DST_ALPHA, GL_ZERO },
+ { SkBlendMode::kSrcOut, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kDstOut, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkBlendMode::kSrcATop, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+ { SkBlendMode::kDstATop, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kXor, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kPlus, GL_ONE, GL_ONE },
+ { SkBlendMode::kModulate, GL_DST_COLOR, GL_ZERO },
+ { SkBlendMode::kScreen, GL_ONE_MINUS_DST_COLOR, GL_ONE }
};
Blend::Blend()
@@ -111,9 +111,10 @@
}
}
-void Blend::getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage, GLenum* outSrc, GLenum* outDst) {
- *outSrc = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[mode].src : kBlends[mode].src;
- *outDst = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[mode].dst : kBlends[mode].dst;
+void Blend::getFactors(SkBlendMode mode, ModeOrderSwap modeUsage, GLenum* outSrc, GLenum* outDst) {
+ int index = static_cast<int>(mode);
+ *outSrc = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[index].src : kBlends[index].src;
+ *outDst = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[index].dst : kBlends[index].dst;
}
void Blend::setFactors(GLenum srcMode, GLenum dstMode) {
diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h
index df9e5a8..ec0e114 100644
--- a/libs/hwui/renderstate/Blend.h
+++ b/libs/hwui/renderstate/Blend.h
@@ -20,7 +20,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-#include <SkXfermode.h>
+#include <SkBlendMode.h>
#include <memory>
namespace android {
@@ -36,7 +36,7 @@
};
void syncEnabled();
- static void getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage,
+ static void getFactors(SkBlendMode mode, ModeOrderSwap modeUsage,
GLenum* outSrc, GLenum* outDst);
void setFactors(GLenum src, GLenum dst);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index fe0f56a..8ed8893 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -97,6 +97,31 @@
}
}
+void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor) {
+ ATRACE_CALL();
+ auto renderType = Properties::getRenderPipelineType();
+ switch (renderType) {
+ case RenderPipelineType::OpenGL:
+ OpenGLPipeline::invokeFunctor(thread, functor);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
+ }
+}
+
+void CanvasContext::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
+ auto renderType = Properties::getRenderPipelineType();
+ switch (renderType) {
+ case RenderPipelineType::OpenGL:
+ OpenGLPipeline::prepareToDraw(thread, bitmap);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
+ }
+}
+
CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory,
std::unique_ptr<IRenderPipeline> renderPipeline)
@@ -425,8 +450,11 @@
GpuMemoryTracker::onFrameCompleted();
#ifdef BUGREPORT_FONT_CACHE_USAGE
- Caches& caches = Caches::getInstance();
- caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted();
+ auto renderType = Properties::getRenderPipelineType();
+ if (RenderPipelineType::OpenGL == renderType) {
+ Caches& caches = Caches::getInstance();
+ caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted();
+ }
#endif
}
@@ -456,16 +484,6 @@
}
}
-void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
- ATRACE_CALL();
- DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
- if (thread.eglManager().hasEglContext()) {
- mode = DrawGlInfo::kModeProcess;
- }
-
- thread.renderState().invokeFunctor(functor, mode, nullptr);
-}
-
void CanvasContext::markLayerInUse(RenderNode* node) {
if (mPrefetchedLayers.erase(node)) {
node->decStrong(nullptr);
@@ -520,10 +538,6 @@
for (const sp<RenderNode>& node : mRenderNodes) {
node->destroyHardwareResources(observer);
}
- Caches& caches = Caches::getInstance();
- // Make sure to release all the textures we were owning as there won't
- // be another draw
- caches.textureCache.resetMarkInUse(this);
mRenderPipeline->onDestroyHardwareResources();
}
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 559ac87..b61eef2 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -85,11 +85,15 @@
*/
static void destroyLayer(RenderNode* node);
+ static void invokeFunctor(const RenderThread& thread, Functor* functor);
+
+ static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap);
+
/*
* If Properties::isSkiaEnabled() is true then this will return the Skia
* grContext associated with the current RenderPipeline.
*/
- GrContext* getGrContext() const { return mRenderPipeline->getGrContext(); }
+ GrContext* getGrContext() const { return mRenderThread.getGrContext(); }
// Won't take effect until next EGLSurface creation
void setSwapBehavior(SwapBehavior swapBehavior);
@@ -121,8 +125,6 @@
void destroyHardwareResources(TreeObserver* observer);
static void trimMemory(RenderThread& thread, int level);
- static void invokeFunctor(RenderThread& thread, Functor* functor);
-
DeferredLayerUpdater* createTextureLayer();
void stopDrawing();
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index beda045..ce48bc0 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -25,6 +25,8 @@
#include <cutils/log.h>
#include <cutils/properties.h>
#include <EGL/eglext.h>
+#include <GrContextOptions.h>
+#include <gl/GrGLInterface.h>
#include <string>
#define GLES_VERSION 2
@@ -126,6 +128,17 @@
makeCurrent(mPBufferSurface);
DeviceInfo::initialize();
mRenderThread.renderState().onGLContextCreated();
+
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+ sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+ LOG_ALWAYS_FATAL_IF(!glInterface.get());
+
+ GrContextOptions options;
+ options.fDisableDistanceFieldPaths = true;
+ options.fAllowPathMaskCaching = true;
+ mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend,
+ (GrBackendContext)glInterface.get(), options));
+ }
}
void EglManager::initExtensions() {
@@ -235,6 +248,7 @@
void EglManager::destroy() {
if (mEglDisplay == EGL_NO_DISPLAY) return;
+ mRenderThread.setGrContext(nullptr);
mRenderThread.renderState().onGLContextDestroyed();
eglDestroyContext(mEglDisplay, mEglContext);
eglDestroySurface(mEglDisplay, mPBufferSurface);
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index f96c2fd..52894ad 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -73,7 +73,6 @@
virtual TaskManager* getTaskManager() = 0;
virtual bool createOrUpdateLayer(RenderNode* node,
const DamageAccumulator& damageAccumulator) = 0;
- virtual GrContext* getGrContext() = 0;
virtual ~IRenderPipeline() {}
};
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index c758f6c..cca0fca 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -18,6 +18,7 @@
#include "DeferredLayerUpdater.h"
#include "EglManager.h"
+#include "ProfileRenderer.h"
#include "renderstate/RenderState.h"
#include "Readback.h"
@@ -76,7 +77,8 @@
BakedOpRenderer renderer(caches, mRenderThread.renderState(),
opaque, lightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
- profiler->draw(&renderer);
+ ProfileRenderer profileRenderer(renderer);
+ profiler->draw(profileRenderer);
drew = renderer.didDraw();
// post frame cleanup
@@ -163,6 +165,10 @@
}
void OpenGLPipeline::onDestroyHardwareResources() {
+ Caches& caches = Caches::getInstance();
+ // Make sure to release all the textures we were owning as there won't
+ // be another draw
+ caches.textureCache.resetMarkInUse(this);
mRenderThread.renderState().flush(Caches::FlushMode::Layers);
}
@@ -223,6 +229,21 @@
}
}
+void OpenGLPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
+ if (Caches::hasInstance() && thread.eglManager().hasEglContext()) {
+ ATRACE_NAME("Bitmap#prepareToDraw task");
+ Caches::getInstance().textureCache.prefetch(bitmap);
+ }
+}
+
+void OpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (thread.eglManager().hasEglContext()) {
+ mode = DrawGlInfo::kModeProcess;
+ }
+ thread.renderState().invokeFunctor(functor, mode, nullptr);
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h
index d024aec..8722d59 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.h
+++ b/libs/hwui/renderthread/OpenGLPipeline.h
@@ -56,7 +56,8 @@
bool createOrUpdateLayer(RenderNode* node,
const DamageAccumulator& damageAccumulator) override;
static void destroyLayer(RenderNode* node);
- GrContext* getGrContext() override { return nullptr; }
+ static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap);
+ static void invokeFunctor(const RenderThread& thread, Functor* functor);
private:
EglManager& mEglManager;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c2ed864..6cb2b9c 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -617,17 +617,14 @@
reinterpret_cast<intptr_t>( staticPostAndWait(task) ));
}
-CREATE_BRIDGE2(prepareToDraw, RenderThread* thread, SkBitmap* bitmap) {
- if (Caches::hasInstance() && args->thread->eglManager().hasEglContext()) {
- ATRACE_NAME("Bitmap#prepareToDraw task");
- Caches::getInstance().textureCache.prefetch(args->bitmap);
- }
- delete args->bitmap;
+CREATE_BRIDGE2(prepareToDraw, RenderThread* thread, Bitmap* bitmap) {
+ CanvasContext::prepareToDraw(*args->thread, args->bitmap);
+ args->bitmap->unref();
args->bitmap = nullptr;
return nullptr;
}
-void RenderProxy::prepareToDraw(const SkBitmap& bitmap) {
+void RenderProxy::prepareToDraw(Bitmap& bitmap) {
// If we haven't spun up a hardware accelerated window yet, there's no
// point in precaching these bitmaps as it can't impact jank.
// We also don't know if we even will spin up a hardware-accelerated
@@ -636,7 +633,8 @@
RenderThread* renderThread = &RenderThread::getInstance();
SETUP_TASK(prepareToDraw);
args->thread = renderThread;
- args->bitmap = new SkBitmap(bitmap);
+ bitmap.ref();
+ args->bitmap = &bitmap;
nsecs_t lastVsync = renderThread->timeLord().latestVsync();
nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos();
nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(CLOCK_MONOTONIC);
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 50a6f64..ae9330d 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -128,7 +128,7 @@
ANDROID_API static int copySurfaceInto(sp<Surface>& surface,
int left, int top, int right, int bottom, SkBitmap* bitmap);
- ANDROID_API static void prepareToDraw(const SkBitmap& bitmap);
+ ANDROID_API static void prepareToDraw(Bitmap& bitmap);
private:
RenderThread& mRenderThread;
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 076e3d4..d8677e13 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -22,6 +22,7 @@
#include "../JankTracker.h"
#include "TimeLord.h"
+#include <GrContext.h>
#include <cutils/compiler.h>
#include <ui/DisplayInfo.h>
#include <utils/Looper.h>
@@ -88,12 +89,15 @@
void pushBackFrameCallback(IFrameCallback* callback);
TimeLord& timeLord() { return mTimeLord; }
- RenderState& renderState() { return *mRenderState; }
- EglManager& eglManager() { return *mEglManager; }
+ RenderState& renderState() const { return *mRenderState; }
+ EglManager& eglManager() const { return *mEglManager; }
JankTracker& jankTracker() { return *mJankTracker; }
const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; }
+ GrContext* getGrContext() const { return mGrContext.get(); }
+ void setGrContext(GrContext* cxt) { mGrContext.reset(cxt); }
+
protected:
virtual bool threadLoop() override;
@@ -144,6 +148,8 @@
EglManager* mEglManager;
JankTracker* mJankTracker = nullptr;
+
+ sk_sp<GrContext> mGrContext;
};
} /* namespace renderthread */
diff --git a/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp
index b8484b9..6d2e8599 100644
--- a/libs/hwui/tests/common/TestListViewSceneBase.cpp
+++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp
@@ -47,7 +47,7 @@
}
});
- canvas.drawColor(Color::Grey_500, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver);
canvas.drawRenderNode(mListView.get());
}
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 51c0a05..cdaa705 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -21,6 +21,7 @@
#include <Matrix.h>
#include <Rect.h>
#include <RenderNode.h>
+#include <hwui/Bitmap.h>
#include <renderstate/RenderState.h>
#include <renderthread/RenderThread.h>
#include <Snapshot.h>
@@ -121,15 +122,16 @@
return snapshot;
}
- static SkBitmap createSkBitmap(int width, int height,
+ static sk_sp<Bitmap> createBitmap(int width, int height,
SkColorType colorType = kN32_SkColorType) {
- SkBitmap bitmap;
- sk_sp<SkColorSpace> colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
- SkImageInfo info = SkImageInfo::Make(width, height,
- colorType, kPremul_SkAlphaType, colorSpace);
- bitmap.setInfo(info);
- bitmap.allocPixels(info);
- return bitmap;
+ SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType);
+ return Bitmap::allocateHeapBitmap(info);
+ }
+
+ static sk_sp<Bitmap> createBitmap(int width, int height, SkBitmap* outBitmap) {
+ SkImageInfo info = SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
+ outBitmap->setInfo(info);
+ return Bitmap::allocateHeapBitmap(outBitmap, nullptr);
}
static sp<DeferredLayerUpdater> createTextureLayerUpdater(
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index 47f40a1..8f2ba2d 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -29,7 +29,7 @@
public:
sp<RenderNode> card;
void createContent(int width, int height, Canvas& canvas) override {
- canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
card = TestUtils::createNode(0, 0, 200, 400,
[](RenderProperties& props, Canvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
@@ -39,7 +39,7 @@
canvas.rotate(45);
canvas.translate(-100, -100);
canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
- canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
}
canvas.restore();
@@ -48,7 +48,7 @@
SkPath clipCircle;
clipCircle.addCircle(100, 300, 100);
canvas.clipPath(&clipCircle, SkRegion::kIntersect_Op);
- canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
}
canvas.restore();
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index 9d6aa53..c0d9450 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -37,7 +37,7 @@
container = TestUtils::createNode(0, 0, width, height, nullptr);
doFrame(0); // update container
- canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
canvas.drawRenderNode(container.get());
}
diff --git a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
index 00dba78..3a230ae 100644
--- a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
@@ -32,9 +32,9 @@
card = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, Canvas& canvas) {
props.mutateLayerProperties().setType(LayerType::RenderLayer);
- canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFF0000FF, SkBlendMode::kSrcOver);
});
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); // background
canvas.drawRenderNode(card.get());
}
void doFrame(int frameNr) override {
diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
index a5e91e4..b7357e1 100644
--- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
@@ -31,7 +31,7 @@
class ListOfFadedTextAnimation : public TestListViewSceneBase {
void createListItem(RenderProperties& props, Canvas& canvas, int id,
int itemWidth, int itemHeight) override {
- canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
int length = dp(100);
canvas.saveLayer(0, 0, length, itemHeight, nullptr, SaveFlags::HasAlphaLayer);
SkPaint textPaint;
@@ -44,16 +44,15 @@
pts[1].set(0, 1);
SkColor colors[2] = {Color::Black, Color::Transparent};
- SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+ sk_sp<SkShader> s(SkGradientShader::MakeLinear(pts, colors, NULL, 2,
SkShader::kClamp_TileMode));
SkMatrix matrix;
matrix.setScale(1, length);
matrix.postRotate(-90);
SkPaint fadingPaint;
- fadingPaint.setShader(s->newWithLocalMatrix(matrix))->unref();
- SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstOut_Mode);
- fadingPaint.setXfermode(mode);
+ fadingPaint.setShader(s->makeWithLocalMatrix(matrix));
+ fadingPaint.setBlendMode(SkBlendMode::kDstOut);
canvas.drawRect(0, 0, length, itemHeight, fadingPaint);
canvas.restore();
}
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index 24c3b23..c1144be 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -29,10 +29,11 @@
});
class ListViewAnimation : public TestListViewSceneBase {
- SkBitmap createRandomCharIcon(int cardHeight) {
+ sk_sp<Bitmap> createRandomCharIcon(int cardHeight) {
+ SkBitmap skBitmap;
int size = cardHeight - (dp(10) * 2);
- SkBitmap bitmap = TestUtils::createSkBitmap(size, size);
- SkCanvas canvas(bitmap);
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(size, size, &skBitmap));
+ SkCanvas canvas(skBitmap);
canvas.clear(0);
SkPaint paint;
@@ -54,11 +55,12 @@
return bitmap;
}
- static SkBitmap createBoxBitmap(bool filled) {
+ static sk_sp<Bitmap> createBoxBitmap(bool filled) {
int size = dp(20);
int stroke = dp(2);
- SkBitmap bitmap = TestUtils::createSkBitmap(size, size);
- SkCanvas canvas(bitmap);
+ SkBitmap skBitmap;
+ auto bitmap = TestUtils::createBitmap(size, size, &skBitmap);
+ SkCanvas canvas(skBitmap);
canvas.clear(Color::Transparent);
SkPaint paint;
@@ -72,8 +74,8 @@
void createListItem(RenderProperties& props, Canvas& canvas, int cardId,
int itemWidth, int itemHeight) override {
- static SkBitmap filledBox = createBoxBitmap(true);
- static SkBitmap strokedBox = createBoxBitmap(false);
+ static sk_sp<Bitmap> filledBox(createBoxBitmap(true));
+ static sk_sp<Bitmap> strokedBox(createBoxBitmap(false));
// TODO: switch to using round rect clipping, once merging correctly handles that
SkPaint roundRectPaint;
roundRectPaint.setAntiAlias(true);
@@ -92,9 +94,10 @@
TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint,
itemHeight, dp(45));
- canvas.drawBitmap(createRandomCharIcon(itemHeight), dp(10), dp(10), nullptr);
+ auto randomIcon = createRandomCharIcon(itemHeight);
+ canvas.drawBitmap(*randomIcon, dp(10), dp(10), nullptr);
- const SkBitmap& boxBitmap = rand() % 2 ? filledBox : strokedBox;
- canvas.drawBitmap(boxBitmap, itemWidth - dp(10) - boxBitmap.width(), dp(10), nullptr);
+ auto box = rand() % 2 ? filledBox : strokedBox;
+ canvas.drawBitmap(*box, itemWidth - dp(10) - box->width(), dp(10), nullptr);
}
};
diff --git a/libs/hwui/tests/common/scenes/OpPropAnimation.cpp b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp
index c8e124c..68051d6 100644
--- a/libs/hwui/tests/common/scenes/OpPropAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp
@@ -53,7 +53,7 @@
mCircleX->value = width * 0.75;
mCircleY->value = height * 0.75;
- canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
canvas.drawRoundRect(mRoundRectLeft.get(), mRoundRectTop.get(),
mRoundRectRight.get(), mRoundRectBottom.get(),
mRoundRectRx.get(), mRoundRectRy.get(), mPaint.get());
diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
index f37c00c..d6fd604 100644
--- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
@@ -29,7 +29,7 @@
public:
sp<RenderNode> card;
void createContent(int width, int height, Canvas& canvas) override {
- canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
card = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, Canvas& canvas) {
SkPaint paint;
diff --git a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
index bc2dc75..bc04d81 100644
--- a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
@@ -37,7 +37,7 @@
0xFF4CAF50,
};
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
@@ -45,7 +45,7 @@
sp<RenderNode> card = TestUtils::createNode(x, y,
x + dp(100), y + dp(100),
[color](RenderProperties& props, Canvas& canvas) {
- canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(color, SkBlendMode::kSrcOver);
});
canvas.drawRenderNode(card.get());
cards.push_back(card);
@@ -61,7 +61,7 @@
TestUtils::recordNode(*cards[0], [curFrame](Canvas& canvas) {
SkColor color = TestUtils::interpolateColor(
curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0);
- canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(color, SkBlendMode::kSrcOver);
});
}
};
diff --git a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
index 3d4397a..8256024 100644
--- a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
@@ -39,18 +39,20 @@
thumbnailSize = std::min(std::min(width, height) / 2, 720);
int cardsize = std::min(width, height) - dp(64);
- renderer.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+ renderer.drawColor(Color::White, SkBlendMode::kSrcOver);
renderer.insertReorderBarrier(true);
int x = dp(32);
for (int i = 0; i < 4; i++) {
int y = (height / 4) * i;
- SkBitmap thumb = TestUtils::createSkBitmap(thumbnailSize, thumbnailSize);
- thumb.eraseColor(COLORS[i]);
- sp<RenderNode> card = createCard(x, y, cardsize, cardsize, thumb);
+ SkBitmap bitmap;
+ sk_sp<Bitmap> thumb(TestUtils::createBitmap(thumbnailSize, thumbnailSize, &bitmap));
+
+ bitmap.eraseColor(COLORS[i]);
+ sp<RenderNode> card = createCard(x, y, cardsize, cardsize, *thumb);
card->mutateStagingProperties().setElevation(i * dp(8));
renderer.drawRenderNode(card.get());
- mThumbnail = thumb;
+ mThumbnail = bitmap;
mCards.push_back(card);
}
@@ -68,15 +70,14 @@
}
private:
- sp<RenderNode> createCard(int x, int y, int width, int height,
- const SkBitmap& thumb) {
+ sp<RenderNode> createCard(int x, int y, int width, int height, Bitmap& thumb) {
return TestUtils::createNode(x, y, x + width, y + height,
[&thumb, width, height](RenderProperties& props, Canvas& canvas) {
props.setElevation(dp(16));
props.mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
props.mutableOutline().setShouldClip(true);
- canvas.drawColor(Color::Grey_200, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Grey_200, SkBlendMode::kSrcOver);
canvas.drawBitmap(thumb, 0, 0, thumb.width(), thumb.height(),
0, 0, width, height, nullptr);
});
diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
index e1d323e..668eec6 100644
--- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
@@ -30,12 +30,12 @@
public:
sp<RenderNode> card;
void createContent(int width, int height, Canvas& canvas) override {
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
canvas.insertReorderBarrier(true);
card = TestUtils::createNode(50, 50, 250, 250,
[](RenderProperties& props, Canvas& canvas) {
- canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFFF00FF, SkBlendMode::kSrcOver);
SkRegion region;
for (int xOffset = 0; xOffset < 200; xOffset+=2) {
diff --git a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
index 0f8906e..4b6632d 100644
--- a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
@@ -28,7 +28,7 @@
std::vector< sp<RenderNode> > cards;
void createContent(int width, int height, Canvas& canvas) override {
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
canvas.insertReorderBarrier(true);
int ci = 0;
@@ -37,7 +37,7 @@
auto color = BrightColors[ci++ % BrightColorsCount];
auto card = TestUtils::createNode(x, y, x + mSize, y + mSize,
[&](RenderProperties& props, Canvas& canvas) {
- canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(color, SkBlendMode::kSrcOver);
props.mutableOutline().setRoundRect(0, 0,
props.getWidth(), props.getHeight(), mSize * .25, 1);
props.mutableOutline().setShouldClip(true);
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index cd00ed3..3630935 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -29,16 +29,16 @@
public:
sp<RenderNode> card;
void createContent(int width, int height, Canvas& canvas) override {
- canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); // background
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver); // background
card = TestUtils::createNode(0, 0, 400, 800,
[](RenderProperties& props, Canvas& canvas) {
// nested clipped saveLayers
canvas.saveLayerAlpha(0, 0, 400, 400, 200, SaveFlags::ClipToLayer);
- canvas.drawColor(Color::Green_700, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Green_700, SkBlendMode::kSrcOver);
canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op);
canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
- canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
canvas.restore();
canvas.restore();
diff --git a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
index d7d0c512..0a69b62 100644
--- a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
@@ -29,7 +29,7 @@
public:
std::vector< sp<RenderNode> > cards;
void createContent(int width, int height, Canvas& canvas) override {
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
canvas.insertReorderBarrier(true);
for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
@@ -57,7 +57,7 @@
props.setElevation(dp(16));
props.mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
props.mutableOutline().setShouldClip(true);
- canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFEEEEEE, SkBlendMode::kSrcOver);
});
}
};
diff --git a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
index 75362dd..4a02429 100644
--- a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
@@ -29,7 +29,7 @@
public:
std::vector< sp<RenderNode> > cards;
void createContent(int width, int height, Canvas& canvas) override {
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
canvas.insertReorderBarrier(true);
for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
@@ -57,7 +57,7 @@
props.setElevation(dp(16));
props.mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
props.mutableOutline().setShouldClip(true);
- canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFEEEEEE, SkBlendMode::kSrcOver);
});
}
};
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index e2370f7..5ef8773 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -83,7 +83,7 @@
for (auto op : ops) {
int innerCount = canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(0, 0, cellSize, cellSize, SkRegion::kIntersect_Op);
- canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
op(canvas, cellSize, paint);
canvas.restoreToCount(innerCount);
canvas.translate(cellSize + cellSpace, 0);
@@ -94,7 +94,7 @@
}
canvas.restoreToCount(outerCount);
});
- canvas.drawColor(Color::Grey_500, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver);
canvas.drawRenderNode(card.get());
}
diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp
index 2933402..438f877 100644
--- a/libs/hwui/tests/common/scenes/TextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp
@@ -29,7 +29,7 @@
public:
sp<RenderNode> card;
void createContent(int width, int height, Canvas& canvas) override {
- canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
card = TestUtils::createNode(0, 0, width, height,
[](RenderProperties& props, Canvas& canvas) {
SkPaint paint;
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 2787fba..da724a9 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -93,7 +93,7 @@
delete canvas->finishRecording();
SkPaint rectPaint;
- SkBitmap iconBitmap = TestUtils::createSkBitmap(80, 80);
+ sk_sp<Bitmap> iconBitmap(TestUtils::createBitmap(80, 80));
while (benchState.KeepRunning()) {
canvas->resetRecording(100, 100);
@@ -105,7 +105,7 @@
{
canvas->save(SaveFlags::MatrixClip);
canvas->translate(10, 10);
- canvas->drawBitmap(iconBitmap, 0, 0, nullptr);
+ canvas->drawBitmap(*iconBitmap, 0, 0, nullptr);
canvas->restore();
}
benchmark::DoNotOptimize(canvas.get());
@@ -163,7 +163,7 @@
void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) {
sp<RenderNode> child = TestUtils::createNode(50, 50, 100, 100,
[](auto& props, auto& canvas) {
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
});
std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 93aa574..d68f5bd 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -41,7 +41,7 @@
static sp<RenderNode> createTestNode() {
auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(10, 10));
SkPaint paint;
// Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
@@ -50,7 +50,7 @@
for (int i = 0; i < 30; i++) {
canvas.translate(0, 10);
canvas.drawRect(0, 0, 10, 10, paint);
- canvas.drawBitmap(bitmap, 5, 0, nullptr);
+ canvas.drawBitmap(*bitmap, 5, 0, nullptr);
}
canvas.restore();
});
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
index 9deb441..d44be7d 100644
--- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -86,9 +86,7 @@
strokePaint.setStrokeWidth(4);
float intervals[] = {1.0f, 1.0f};
- auto dashEffect = SkDashPathEffect::Create(intervals, 2, 0);
- strokePaint.setPathEffect(dashEffect);
- dashEffect->unref();
+ strokePaint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
auto textureGlopVerifier = [] (const Glop& glop) {
// validate glop produced by renderPathTexture (so texture, unit quad)
@@ -167,7 +165,7 @@
shadowPaint.setColor(SK_ColorRED);
SkScalar sigma = Blur::convertRadiusToSigma(5);
- shadowPaint.setLooper(SkBlurDrawLooper::Create(SK_ColorWHITE, sigma, 3, 3))->unref();
+ shadowPaint.setLooper(SkBlurDrawLooper::Make(SK_ColorWHITE, sigma, 3, 3));
TestUtils::drawUtf8ToCanvas(&canvas, "A", shadowPaint, 25, 25);
TestUtils::drawUtf8ToCanvas(&canvas, "B", shadowPaint, 50, 50);
@@ -202,8 +200,8 @@
props.mutateLayerProperties().setType(LayerType::RenderLayer);
// provide different blend mode, so decoration draws contrast
- props.mutateLayerProperties().setXferMode(SkXfermode::Mode::kSrc_Mode);
- canvas.drawColor(Color::Black, SkXfermode::Mode::kSrcOver_Mode);
+ props.mutateLayerProperties().setXferMode(SkBlendMode::kSrc);
+ canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
});
OffscreenBuffer** layerHandle = node->getLayerHandle();
@@ -287,4 +285,4 @@
EXPECT_EQ(1, reinterpret_cast<PathTexture*>(texture)->left);
EXPECT_EQ(3, reinterpret_cast<PathTexture*>(texture)->top);
});
-}
\ No newline at end of file
+}
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
new file mode 100644
index 0000000..3cc7189
--- /dev/null
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "AnimationContext.h"
+#include "IContextFactory.h"
+#include "renderthread/CanvasContext.h"
+#include "tests/common/TestUtils.h"
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+ return new AnimationContext(clock);
+ }
+};
+
+RENDERTHREAD_TEST(CanvasContext, create) {
+ auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
+ ContextFactory contextFactory;
+ std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
+ renderThread, false, rootNode.get(), &contextFactory));
+
+ ASSERT_FALSE(canvasContext->hasSurface());
+
+ canvasContext->destroy(nullptr);
+}
+
+class TestFunctor : public Functor {
+public:
+ bool didProcess = false;
+
+ virtual status_t operator ()(int what, void* data) {
+ if (what == DrawGlInfo::kModeProcess) { didProcess = true; }
+ return DrawGlInfo::kStatusDone;
+ }
+};
+
+RENDERTHREAD_TEST(CanvasContext, invokeFunctor) {
+ TestFunctor functor;
+ ASSERT_FALSE(functor.didProcess);
+ CanvasContext::invokeFunctor(renderThread, &functor);
+ ASSERT_TRUE(functor.didProcess);
+}
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 347d4a5..01046e1 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -129,9 +129,9 @@
auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
canvas.drawRect(0, 0, 100, 200, SkPaint());
- canvas.drawBitmap(bitmap, 10, 10, nullptr);
+ canvas.drawBitmap(*bitmap, 10, 10, nullptr);
});
FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
sLightGeometry, Caches::getInstance());
@@ -200,8 +200,9 @@
auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- SkBitmap bitmap = TestUtils::createSkBitmap(10, 10,
- kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap
+
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(10, 10,
+ kAlpha_8_SkColorType)); // Disable merging by using alpha 8 bitmap
// Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
// Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
@@ -209,7 +210,7 @@
for (int i = 0; i < LOOPS; i++) {
canvas.translate(0, 10);
canvas.drawRect(0, 0, 10, 10, SkPaint());
- canvas.drawBitmap(bitmap, 5, 0, nullptr);
+ canvas.drawBitmap(*bitmap, 5, 0, nullptr);
}
canvas.restore();
});
@@ -393,19 +394,19 @@
}
RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
- static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50,
- SkColorType::kRGB_565_SkColorType);
- static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50,
- SkColorType::kAlpha_8_SkColorType);
+ static sk_sp<Bitmap> opaqueBitmap(TestUtils::createBitmap(50, 50,
+ SkColorType::kRGB_565_SkColorType));
+ static sk_sp<Bitmap> transpBitmap(TestUtils::createBitmap(50, 50,
+ SkColorType::kAlpha_8_SkColorType));
class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
public:
void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
switch(mIndex++) {
case 0:
- EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef());
+ EXPECT_EQ(opaqueBitmap.get(), op.bitmap);
break;
case 1:
- EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef());
+ EXPECT_EQ(transpBitmap.get(), op.bitmap);
break;
default:
ADD_FAILURE() << "Only two ops expected.";
@@ -417,11 +418,11 @@
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 50, 50, SkPaint());
canvas.drawRect(0, 0, 50, 50, SkPaint());
- canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
+ canvas.drawBitmap(*transpBitmap, 0, 0, nullptr);
// only the below draws should remain, since they're
- canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
- canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
+ canvas.drawBitmap(*opaqueBitmap, 0, 0, nullptr);
+ canvas.drawBitmap(*transpBitmap, 0, 0, nullptr);
});
FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50,
sLightGeometry, Caches::getInstance());
@@ -449,23 +450,23 @@
};
auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
[](RenderProperties& props, RecordingCanvas& canvas) {
- SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20));
// left side clipped (to inset left half)
canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
- canvas.drawBitmap(bitmap, 0, 40, nullptr);
+ canvas.drawBitmap(*bitmap, 0, 40, nullptr);
// top side clipped (to inset top half)
canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
- canvas.drawBitmap(bitmap, 40, 0, nullptr);
+ canvas.drawBitmap(*bitmap, 40, 0, nullptr);
// right side clipped (to inset right half)
canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
- canvas.drawBitmap(bitmap, 80, 40, nullptr);
+ canvas.drawBitmap(*bitmap, 80, 40, nullptr);
// bottom not clipped, just abutting (inset bottom half)
canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
- canvas.drawBitmap(bitmap, 40, 70, nullptr);
+ canvas.drawBitmap(*bitmap, 40, 70, nullptr);
});
FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
@@ -750,7 +751,7 @@
auto unclippedColorView = TestUtils::createNode<RecordingCanvas>(0, 0, 10, 10,
[](RenderProperties& props, RecordingCanvas& canvas) {
props.setClipToBounds(false);
- canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
});
FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
@@ -822,8 +823,8 @@
auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
- canvas.drawBitmap(bitmap, 0, 0, nullptr);
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(200, 200));
+ canvas.drawBitmap(*bitmap, 0, 0, nullptr);
});
// clip to small area, should see in receiver
@@ -1003,7 +1004,7 @@
void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
EXPECT_EQ(1, mIndex++);
ASSERT_NE(nullptr, op.paint);
- ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
+ ASSERT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint));
}
void onRectOp(const RectOp& op, const BakedOpState& state) override {
EXPECT_EQ(2, mIndex++);
@@ -1137,7 +1138,7 @@
void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
EXPECT_EQ(1, mIndex++);
ASSERT_NE(nullptr, op.paint);
- EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
+ EXPECT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint));
EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
<< "Expect dirty rect as clip";
ASSERT_NE(nullptr, state.computedState.clipState);
@@ -1462,7 +1463,7 @@
auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
[](RenderProperties& props, RecordingCanvas& canvas) {
props.mutateLayerProperties().setType(LayerType::RenderLayer);
- canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
});
OffscreenBuffer** layerHandle = node->getLayerHandle();
@@ -2225,7 +2226,7 @@
auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.clipRect(0, -20, 10, 30, SkRegion::kReplace_Op);
- canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
});
FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index edc7191..134497c 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -249,7 +249,7 @@
TEST(RecordingCanvas, drawColor) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.drawColor(Color::Black, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
});
ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
@@ -261,8 +261,7 @@
TEST(RecordingCanvas, backgroundAndImage) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
- SkBitmap bitmap;
- bitmap.setInfo(SkImageInfo::MakeUnknown(25, 25));
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
SkPaint paint;
paint.setColor(SK_ColorBLUE);
@@ -278,7 +277,7 @@
canvas.save(SaveFlags::MatrixClip);
canvas.translate(25, 25);
canvas.scale(2, 2);
- canvas.drawBitmap(bitmap, 0, 0, nullptr);
+ canvas.drawBitmap(*bitmap, 0, 0, nullptr);
canvas.restore();
}
canvas.restore();
@@ -639,7 +638,7 @@
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(-10, -10, 110, 110, SkRegion::kReplace_Op);
- canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
canvas.restore();
});
ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
@@ -728,19 +727,21 @@
}
TEST(RecordingCanvas, refBitmap) {
- SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(100, 100));
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
- canvas.drawBitmap(bitmap, 0, 0, nullptr);
+ canvas.drawBitmap(*bitmap, 0, 0, nullptr);
});
auto& bitmaps = dl->getBitmapResources();
EXPECT_EQ(1u, bitmaps.size());
}
TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
- SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
SkPaint paint;
- sk_sp<SkShader> shader = SkMakeBitmapShader(bitmap,
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ sk_sp<SkShader> shader = SkMakeBitmapShader(skBitmap,
SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode,
nullptr,
@@ -754,10 +755,12 @@
}
TEST(RecordingCanvas, refBitmapInShader_composeShader) {
- SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
SkPaint paint;
- sk_sp<SkShader> shader1 = SkMakeBitmapShader(bitmap,
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ sk_sp<SkShader> shader1 = SkMakeBitmapShader(skBitmap,
SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode,
nullptr,
@@ -773,7 +776,7 @@
SkShader::TileMode::kRepeat_TileMode);
sk_sp<SkShader> composeShader = SkShader::MakeComposeShader(std::move(shader1), std::move(shader2),
- SkXfermode::Mode::kMultiply_Mode);
+ SkXfermode::kMultiply_Mode);
paint.setShader(std::move(composeShader));
canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
});
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 39c7f32..331a6ac 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -41,7 +41,7 @@
TEST(RenderNode, hasParents) {
auto child = TestUtils::createNode(0, 0, 200, 400,
[](RenderProperties& props, Canvas& canvas) {
- canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
});
auto parent = TestUtils::createNode(0, 0, 200, 400,
[&child](RenderProperties& props, Canvas& canvas) {
@@ -54,7 +54,7 @@
EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents";
TestUtils::recordNode(*parent, [](Canvas& canvas) {
- canvas.drawColor(Color::Amber_500, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Amber_500, SkBlendMode::kSrcOver);
});
EXPECT_TRUE(child->hasParents()) << "Child should still have a parent";
@@ -117,7 +117,7 @@
{
auto nonNullDLNode = TestUtils::createNode(0, 0, 200, 400,
[](RenderProperties& props, Canvas& canvas) {
- canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
});
TestUtils::syncHierarchyPropertiesAndDisplayList(nonNullDLNode);
EXPECT_TRUE(nonNullDLNode->getDisplayList());
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index 5cab04d..49c4da6 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -25,12 +25,20 @@
using namespace android;
using namespace android::uirenderer;
+SkBitmap createSkBitmap(int width, int height) {
+ SkBitmap bitmap;
+ SkImageInfo info = SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
+ bitmap.setInfo(info);
+ bitmap.allocPixels(info);
+ return bitmap;
+}
+
/**
* 1x1 bitmaps must not be optimized into solid color shaders, since HWUI can't
* compose/render color shaders
*/
TEST(SkiaBehavior, CreateBitmapShader1x1) {
- SkBitmap origBitmap = TestUtils::createSkBitmap(1, 1);
+ SkBitmap origBitmap = createSkBitmap(1, 1);
sk_sp<SkShader> s = SkMakeBitmapShader(
origBitmap,
SkShader::kClamp_TileMode,
@@ -49,7 +57,7 @@
}
TEST(SkiaBehavior, genIds) {
- SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ SkBitmap bitmap = createSkBitmap(100, 100);
uint32_t genId = bitmap.getGenerationID();
bitmap.notifyPixelsChanged();
EXPECT_NE(genId, bitmap.getGenerationID());
@@ -57,8 +65,8 @@
TEST(SkiaBehavior, lightingColorFilter_simplify) {
{
- SkAutoTUnref<SkColorFilter> filter(
- SkColorMatrixFilter::CreateLightingFilter(0x11223344, 0));
+ sk_sp<SkColorFilter> filter(
+ SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0));
SkColor observedColor;
SkXfermode::Mode observedMode;
@@ -68,20 +76,20 @@
}
{
- SkAutoTUnref<SkColorFilter> failFilter(
- SkColorMatrixFilter::CreateLightingFilter(0x11223344, 0x1));
+ sk_sp<SkColorFilter> failFilter(
+ SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0x1));
EXPECT_FALSE(failFilter->asColorMode(nullptr, nullptr));
}
}
TEST(SkiaBehavior, porterDuffCreateIsCached) {
SkPaint paint;
- paint.setXfermodeMode(SkXfermode::kOverlay_Mode);
- auto expected = paint.getXfermode();
- paint.setXfermodeMode(SkXfermode::kClear_Mode);
- ASSERT_NE(expected, paint.getXfermode());
- paint.setXfermodeMode(SkXfermode::kOverlay_Mode);
- ASSERT_EQ(expected, paint.getXfermode());
+ paint.setBlendMode(SkBlendMode::kOverlay);
+ auto expected = paint.getBlendMode();
+ paint.setBlendMode(SkBlendMode::kClear);
+ ASSERT_NE(expected, paint.getBlendMode());
+ paint.setBlendMode(SkBlendMode::kOverlay);
+ ASSERT_EQ(expected, paint.getBlendMode());
}
TEST(SkiaBehavior, srgbColorSpaceIsSingleton) {
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 5a01193..451596d 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -45,7 +45,7 @@
SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0);
std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas));
TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25);
- SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
+ sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
canvas.asSkCanvas()->drawPicture(picture);
});
diff --git a/libs/hwui/utils/NinePatch.h b/libs/hwui/utils/NinePatch.h
deleted file mode 100644
index 323e563..0000000
--- a/libs/hwui/utils/NinePatch.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-**
-** Copyright 2015, 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_GRAPHICS_NINEPATCH_H
-#define ANDROID_GRAPHICS_NINEPATCH_H
-
-#include <androidfw/ResourceTypes.h>
-#include <cutils/compiler.h>
-
-#include "SkCanvas.h"
-#include "SkRegion.h"
-
-namespace android {
-
-class ANDROID_API NinePatch {
-public:
- static void Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap,
- const Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion);
-};
-
-} // namespace android
-
-#endif // ANDROID_GRAPHICS_NINEPATCH_H
diff --git a/libs/hwui/utils/NinePatchImpl.cpp b/libs/hwui/utils/NinePatchImpl.cpp
deleted file mode 100644
index d37126c..0000000
--- a/libs/hwui/utils/NinePatchImpl.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include "utils/NinePatch.h"
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkColorPriv.h"
-#include "SkPaint.h"
-#include "SkUnPreMultiply.h"
-
-#include <utils/Log.h>
-
-namespace android {
-
-static const bool kUseTrace = true;
-static bool gTrace = false;
-
-static bool getColor(const SkBitmap& bitmap, int x, int y, SkColor* c) {
- switch (bitmap.colorType()) {
- case kN32_SkColorType:
- *c = SkUnPreMultiply::PMColorToColor(*bitmap.getAddr32(x, y));
- break;
- case kRGB_565_SkColorType:
- *c = SkPixel16ToPixel32(*bitmap.getAddr16(x, y));
- break;
- case kARGB_4444_SkColorType:
- *c = SkUnPreMultiply::PMColorToColor(
- SkPixel4444ToPixel32(*bitmap.getAddr16(x, y)));
- break;
- case kIndex_8_SkColorType: {
- SkColorTable* ctable = bitmap.getColorTable();
- *c = SkUnPreMultiply::PMColorToColor(
- (*ctable)[*bitmap.getAddr8(x, y)]);
- break;
- }
- default:
- return false;
- }
- return true;
-}
-
-static SkColor modAlpha(SkColor c, int alpha) {
- int scale = alpha + (alpha >> 7);
- int a = SkColorGetA(c) * scale >> 8;
- return SkColorSetA(c, a);
-}
-
-static void drawStretchyPatch(SkCanvas* canvas, SkIRect& src, const SkRect& dst,
- const SkBitmap& bitmap, const SkPaint& paint,
- SkColor initColor, uint32_t colorHint,
- bool hasXfer) {
- if (colorHint != android::Res_png_9patch::NO_COLOR) {
- ((SkPaint*)&paint)->setColor(modAlpha(colorHint, paint.getAlpha()));
- canvas->drawRect(dst, paint);
- ((SkPaint*)&paint)->setColor(initColor);
- } else if (src.width() == 1 && src.height() == 1) {
- SkColor c;
- if (!getColor(bitmap, src.fLeft, src.fTop, &c)) {
- goto SLOW_CASE;
- }
- if (0 != c || hasXfer) {
- SkColor prev = paint.getColor();
- ((SkPaint*)&paint)->setColor(c);
- canvas->drawRect(dst, paint);
- ((SkPaint*)&paint)->setColor(prev);
- }
- } else {
- SLOW_CASE:
- canvas->drawBitmapRect(bitmap, SkRect::Make(src), dst, &paint);
- }
-}
-
-SkScalar calculateStretch(SkScalar boundsLimit, SkScalar startingPoint,
- int srcSpace, int numStrechyPixelsRemaining,
- int numFixedPixelsRemaining) {
- SkScalar spaceRemaining = boundsLimit - startingPoint;
- SkScalar stretchySpaceRemaining =
- spaceRemaining - SkIntToScalar(numFixedPixelsRemaining);
- return srcSpace * stretchySpaceRemaining / numStrechyPixelsRemaining;
-}
-
-void NinePatch::Draw(SkCanvas* canvas, const SkRect& bounds,
- const SkBitmap& bitmap, const Res_png_9patch& chunk,
- const SkPaint* paint, SkRegion** outRegion) {
- if (canvas && canvas->quickReject(bounds)) {
- return;
- }
-
- SkPaint defaultPaint;
- if (NULL == paint) {
- // matches default dither in NinePatchDrawable.java.
- defaultPaint.setDither(true);
- paint = &defaultPaint;
- }
-
- const int32_t* xDivs = chunk.getXDivs();
- const int32_t* yDivs = chunk.getYDivs();
-
- if (kUseTrace) {
- gTrace = true;
- }
-
- SkASSERT(canvas || outRegion);
-
- if (kUseTrace) {
- if (canvas) {
- const SkMatrix& m = canvas->getTotalMatrix();
- ALOGV("ninepatch [%g %g %g] [%g %g %g]\n",
- SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]),
- SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5]));
- }
-
- ALOGV("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()),
- SkScalarToFloat(bounds.height()));
- ALOGV("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height());
- ALOGV("======== ninepatch xDivs [%d,%d]\n", xDivs[0], xDivs[1]);
- ALOGV("======== ninepatch yDivs [%d,%d]\n", yDivs[0], yDivs[1]);
- }
-
- if (bounds.isEmpty() ||
- bitmap.width() == 0 || bitmap.height() == 0 ||
- (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0))
- {
- if (kUseTrace) {
- ALOGV("======== abort ninepatch draw\n");
- }
- return;
- }
-
- // should try a quick-reject test before calling lockPixels
-
- SkAutoLockPixels alp(bitmap);
- // after the lock, it is valid to check getPixels()
- if (bitmap.getPixels() == NULL)
- return;
-
- const bool hasXfer = paint->getXfermode() != NULL;
- SkRect dst;
- SkIRect src;
-
- const int32_t x0 = xDivs[0];
- const int32_t y0 = yDivs[0];
- const SkColor initColor = ((SkPaint*)paint)->getColor();
- const uint8_t numXDivs = chunk.numXDivs;
- const uint8_t numYDivs = chunk.numYDivs;
- int i;
- int j;
- int colorIndex = 0;
- uint32_t color;
- bool xIsStretchable;
- const bool initialXIsStretchable = (x0 == 0);
- bool yIsStretchable = (y0 == 0);
- const int bitmapWidth = bitmap.width();
- const int bitmapHeight = bitmap.height();
-
- // Number of bytes needed for dstRights array.
- // Need to cast numXDivs to a larger type to avoid overflow.
- const size_t dstBytes = ((size_t) numXDivs + 1) * sizeof(SkScalar);
- SkScalar* dstRights = (SkScalar*) alloca(dstBytes);
- bool dstRightsHaveBeenCached = false;
-
- int numStretchyXPixelsRemaining = 0;
- for (i = 0; i < numXDivs; i += 2) {
- numStretchyXPixelsRemaining += xDivs[i + 1] - xDivs[i];
- }
- int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining;
- int numStretchyYPixelsRemaining = 0;
- for (i = 0; i < numYDivs; i += 2) {
- numStretchyYPixelsRemaining += yDivs[i + 1] - yDivs[i];
- }
- int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining;
-
- if (kUseTrace) {
- ALOGV("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n",
- bitmap.width(), bitmap.height(),
- SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
- SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()),
- numXDivs, numYDivs);
- }
-
- src.fTop = 0;
- dst.fTop = bounds.fTop;
- // The first row always starts with the top being at y=0 and the bottom
- // being either yDivs[1] (if yDivs[0]=0) or yDivs[0]. In the former case
- // the first row is stretchable along the Y axis, otherwise it is fixed.
- // The last row always ends with the bottom being bitmap.height and the top
- // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
- // yDivs[numYDivs-1]. In the former case the last row is stretchable along
- // the Y axis, otherwise it is fixed.
- //
- // The first and last columns are similarly treated with respect to the X
- // axis.
- //
- // The above is to help explain some of the special casing that goes on the
- // code below.
-
- // The initial yDiv and whether the first row is considered stretchable or
- // not depends on whether yDiv[0] was zero or not.
- for (j = yIsStretchable ? 1 : 0;
- j <= numYDivs && src.fTop < bitmapHeight;
- j++, yIsStretchable = !yIsStretchable) {
- src.fLeft = 0;
- dst.fLeft = bounds.fLeft;
- if (j == numYDivs) {
- src.fBottom = bitmapHeight;
- dst.fBottom = bounds.fBottom;
- } else {
- src.fBottom = yDivs[j];
- const int srcYSize = src.fBottom - src.fTop;
- if (yIsStretchable) {
- dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop,
- srcYSize,
- numStretchyYPixelsRemaining,
- numFixedYPixelsRemaining);
- numStretchyYPixelsRemaining -= srcYSize;
- } else {
- dst.fBottom = dst.fTop + SkIntToScalar(srcYSize);
- numFixedYPixelsRemaining -= srcYSize;
- }
- }
-
- xIsStretchable = initialXIsStretchable;
- // The initial xDiv and whether the first column is considered
- // stretchable or not depends on whether xDiv[0] was zero or not.
- const uint32_t* colors = chunk.getColors();
- for (i = xIsStretchable ? 1 : 0;
- i <= numXDivs && src.fLeft < bitmapWidth;
- i++, xIsStretchable = !xIsStretchable) {
- color = colors[colorIndex++];
- if (i == numXDivs) {
- src.fRight = bitmapWidth;
- dst.fRight = bounds.fRight;
- } else {
- src.fRight = xDivs[i];
- if (dstRightsHaveBeenCached) {
- dst.fRight = dstRights[i];
- } else {
- const int srcXSize = src.fRight - src.fLeft;
- if (xIsStretchable) {
- dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft,
- srcXSize,
- numStretchyXPixelsRemaining,
- numFixedXPixelsRemaining);
- numStretchyXPixelsRemaining -= srcXSize;
- } else {
- dst.fRight = dst.fLeft + SkIntToScalar(srcXSize);
- numFixedXPixelsRemaining -= srcXSize;
- }
- dstRights[i] = dst.fRight;
- }
- }
- // If this horizontal patch is too small to be displayed, leave
- // the destination left edge where it is and go on to the next patch
- // in the source.
- if (src.fLeft >= src.fRight) {
- src.fLeft = src.fRight;
- continue;
- }
- // Make sure that we actually have room to draw any bits
- if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) {
- goto nextDiv;
- }
- // If this patch is transparent, skip and don't draw.
- if (color == android::Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) {
- if (outRegion) {
- if (*outRegion == NULL) {
- *outRegion = new SkRegion();
- }
- SkIRect idst;
- dst.round(&idst);
- //ALOGI("Adding trans rect: (%d,%d)-(%d,%d)\n",
- // idst.fLeft, idst.fTop, idst.fRight, idst.fBottom);
- (*outRegion)->op(idst, SkRegion::kUnion_Op);
- }
- goto nextDiv;
- }
- if (canvas) {
- if (kUseTrace) {
- ALOGV("-- src [%d %d %d %d] dst [%g %g %g %g]\n",
- src.fLeft, src.fTop, src.width(), src.height(),
- SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop),
- SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height()));
- if (2 == src.width() && SkIntToScalar(5) == dst.width()) {
- ALOGV("--- skip patch\n");
- }
- }
- drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor,
- color, hasXfer);
- }
-
-nextDiv:
- src.fLeft = src.fRight;
- dst.fLeft = dst.fRight;
- }
- src.fTop = src.fBottom;
- dst.fTop = dst.fBottom;
- dstRightsHaveBeenCached = true;
- }
-}
-
-} // namespace android
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index 4faab9a..710e063 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -33,18 +33,6 @@
class PaintUtils {
public:
- /**
- * Safely retrieves the mode from the specified xfermode. If the specified
- * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode.
- */
- static inline SkXfermode::Mode getXfermode(SkXfermode* mode) {
- SkXfermode::Mode resultMode;
- if (!SkXfermode::AsMode(mode, &resultMode)) {
- resultMode = SkXfermode::kSrcOver_Mode;
- }
- return resultMode;
- }
-
static inline GLenum getFilter(const SkPaint* paint) {
if (!paint || paint->getFilterQuality() != kNone_SkFilterQuality) {
return GL_LINEAR;
@@ -56,7 +44,7 @@
static inline bool paintWillNotDraw(const SkPaint& paint) {
return paint.getAlpha() == 0
&& !paint.getColorFilter()
- && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
+ && paint.getBlendMode() == SkBlendMode::kSrcOver;
}
// TODO: move to a method on android:Paint? replace with SkPaint::nothingToDraw()?
@@ -64,7 +52,7 @@
return paint.getAlpha() == 0
&& paint.getLooper() == nullptr
&& !paint.getColorFilter()
- && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
+ && paint.getBlendMode() == SkBlendMode::kSrcOver;
}
static bool isOpaquePaint(const SkPaint* paint) {
@@ -77,9 +65,9 @@
}
// Only let simple srcOver / src blending modes declare opaque, since behavior is clear.
- SkXfermode::Mode mode = getXfermode(paint->getXfermode());
- return mode == SkXfermode::Mode::kSrcOver_Mode
- || mode == SkXfermode::Mode::kSrc_Mode;
+ SkBlendMode mode = paint->getBlendMode();
+ return mode == SkBlendMode::kSrcOver
+ || mode == SkBlendMode::kSrc;
}
static bool isBlendedShader(const SkShader* shader) {
@@ -121,8 +109,8 @@
return getTextShadow(paint, nullptr);
}
- static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
- return paint ? getXfermode(paint->getXfermode()) : SkXfermode::kSrcOver_Mode;
+ static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) {
+ return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver;
}
static inline int getAlphaDirect(const SkPaint* paint) {
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 0bc832a..6941dba 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -216,7 +216,7 @@
SkCanvas surfaceCanvas(surfaceBitmap);
SkPaint paint;
- paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ paint.setBlendMode(SkBlendMode::kSrc);
surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
if (outBuffer.width > update.state.icon.bitmap.width()) {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 2b3ed87..da0e515 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1465,7 +1465,7 @@
mGpsNmeaListener = null;
mNmeaBuffer = null;
mOldGnssCallback = null;
- mGnssCallback = new GnssStatus.Callback() {
+ mGnssCallback = mGpsListener != null ? new GnssStatus.Callback() {
@Override
public void onStarted() {
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
@@ -1485,7 +1485,7 @@
public void onSatelliteStatusChanged(GnssStatus status) {
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
}
- };
+ } : null;
mOldGnssNmeaListener = null;
mGnssNmeaListener = null;
}
@@ -1502,12 +1502,12 @@
mOldGnssCallback = null;
mGnssCallback = null;
mOldGnssNmeaListener = null;
- mGnssNmeaListener = new OnNmeaMessageListener() {
+ mGnssNmeaListener = mGpsNmeaListener != null ? new OnNmeaMessageListener() {
@Override
public void onNmeaMessage(String nmea, long timestamp) {
mGpsNmeaListener.onNmeaReceived(timestamp, nmea);
}
- };
+ } : null;
}
GnssStatusListenerTransport(GnssStatusCallback callback) {
@@ -1516,7 +1516,7 @@
GnssStatusListenerTransport(GnssStatusCallback callback, Handler handler) {
mOldGnssCallback = callback;
- mGnssCallback = new GnssStatus.Callback() {
+ mGnssCallback = mOldGnssCallback != null ? new GnssStatus.Callback() {
@Override
public void onStarted() {
mOldGnssCallback.onStarted();
@@ -1536,7 +1536,7 @@
public void onSatelliteStatusChanged(GnssStatus status) {
mOldGnssCallback.onSatelliteStatusChanged(status);
}
- };
+ } : null;
mGnssHandler = new GnssHandler(handler);
mOldGnssNmeaListener = null;
mGnssNmeaListener = null;
@@ -1569,12 +1569,12 @@
mOldGnssCallback = null;
mGnssHandler = new GnssHandler(handler);
mOldGnssNmeaListener = listener;
- mGnssNmeaListener = new OnNmeaMessageListener() {
+ mGnssNmeaListener = mOldGnssNmeaListener != null ? new OnNmeaMessageListener() {
@Override
public void onNmeaMessage(String message, long timestamp) {
mOldGnssNmeaListener.onNmeaReceived(timestamp, message);
}
- };
+ } : null;
mGpsListener = null;
mGpsNmeaListener = null;
mNmeaBuffer = new ArrayList<Nmea>();
@@ -1597,7 +1597,7 @@
@Override
public void onGnssStarted() {
- if (mGpsListener != null) {
+ if (mGnssCallback != null) {
Message msg = Message.obtain();
msg.what = GpsStatus.GPS_EVENT_STARTED;
mGnssHandler.sendMessage(msg);
@@ -1606,7 +1606,7 @@
@Override
public void onGnssStopped() {
- if (mGpsListener != null) {
+ if (mGnssCallback != null) {
Message msg = Message.obtain();
msg.what = GpsStatus.GPS_EVENT_STOPPED;
mGnssHandler.sendMessage(msg);
@@ -1615,7 +1615,7 @@
@Override
public void onFirstFix(int ttff) {
- if (mGpsListener != null) {
+ if (mGnssCallback != null) {
mTimeToFirstFix = ttff;
Message msg = Message.obtain();
msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 754fe45..7767712 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -473,9 +473,7 @@
synchronized (sActiveRingtones) {
sActiveRingtones.remove(Ringtone.this);
}
- if (mLocalPlayer != null) {
- mLocalPlayer.setOnCompletionListener(null);
- }
+ mp.setOnCompletionListener(null); // Help the Java GC: break the refcount cycle.
}
}
}
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 44f7e56..faefc9f 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -31,7 +31,6 @@
libutils \
libbinder \
libmedia \
- libmediadrm \
libskia \
libui \
liblog \
@@ -41,7 +40,6 @@
libstagefright_foundation \
libcamera_client \
libmtp \
- libusbhost \
libexif \
libpiex
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index bbc249f..4f2fdff 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -257,6 +257,16 @@
/*
* (non-Javadoc)
+ * @see android.hardware.camera2.ICameraDeviceCallbacks#onRequestQueueEmpty()
+ */
+ @Override
+ public void onRequestQueueEmpty() throws RemoteException {
+ // TODO Auto-generated method stub
+
+ }
+
+ /*
+ * (non-Javadoc)
* @see android.hardware.camera2.ICameraDeviceCallbacks#onRepeatingRequestError()
*/
@Override
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 6c879b9..832363c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -148,6 +148,16 @@
/*
* (non-Javadoc)
+ * @see android.hardware.camera2.ICameraDeviceCallbacks#onRequestQueueEmpty()
+ */
+ @Override
+ public void onRequestQueueEmpty() throws RemoteException {
+ // TODO Auto-generated method stub
+
+ }
+
+ /*
+ * (non-Javadoc)
* @see android.hardware.camera2.ICameraDeviceCallbacks#onRepeatingRequestError()
*/
@Override
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
index cdd4065..195df78 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
@@ -415,7 +415,7 @@
in = getContext().getAssets().open(imageFile.getName());
ExifInterface exifInterface = new ExifInterface(in);
exifInterface.saveAttributes();
- } catch (UnsupportedOperationException e) {
+ } catch (IOException e) {
// Expected. saveAttributes is not supported with an ExifInterface object which was
// created with InputStream.
return;
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index b58c87a..bb8eb2c 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -115,6 +115,7 @@
myWebView.clearCache(true);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
+ webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
mWebViewClient = new MyWebViewClient();
myWebView.setWebViewClient(mWebViewClient);
myWebView.setWebChromeClient(new MyWebChromeClient());
diff --git a/packages/ExtServices/src/android/ext/services/notification/Ranker.java b/packages/ExtServices/src/android/ext/services/notification/Ranker.java
index 2ce667c..63fc157 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Ranker.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Ranker.java
@@ -35,180 +35,15 @@
import android.ext.services.R;
/**
- * Class that provides an updatable ranker module for the notification manager..
+ * Class that provides an updatable ranker module for the notification manager.
*/
public final class Ranker extends NotificationRankerService {
private static final String TAG = "RocketRanker";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final int AUTOBUNDLE_AT_COUNT = 4;
- private static final String AUTOBUNDLE_KEY = "ranker_bundle";
-
- // Map of user : <Map of package : notification keys>. Only contains notifications that are not
- // bundled by the app (aka no group or sort key).
- Map<Integer, Map<String, LinkedHashSet<String>>> mUnbundledNotifications;
-
@Override
public Adjustment onNotificationEnqueued(StatusBarNotification sbn, int importance,
boolean user) {
- if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey());
return null;
}
-
- @Override
- public void onNotificationPosted(StatusBarNotification sbn) {
- if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
- try {
- List<String> notificationsToBundle = new ArrayList<>();
- if (!sbn.isAppGroup()) {
- // Not grouped by the app, add to the list of notifications for the app;
- // send bundling update if app exceeds the autobundling limit.
- synchronized (mUnbundledNotifications) {
- Map<String, LinkedHashSet<String>> unbundledNotificationsByUser
- = mUnbundledNotifications.get(sbn.getUserId());
- if (unbundledNotificationsByUser == null) {
- unbundledNotificationsByUser = new HashMap<>();
- }
- mUnbundledNotifications.put(sbn.getUserId(), unbundledNotificationsByUser);
- LinkedHashSet<String> notificationsForPackage
- = unbundledNotificationsByUser.get(sbn.getPackageName());
- if (notificationsForPackage == null) {
- notificationsForPackage = new LinkedHashSet<>();
- }
-
- notificationsForPackage.add(sbn.getKey());
- unbundledNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage);
-
- if (notificationsForPackage.size() >= AUTOBUNDLE_AT_COUNT) {
- for (String key : notificationsForPackage) {
- notificationsToBundle.add(key);
- }
- }
- }
- if (notificationsToBundle.size() > 0) {
- adjustAutobundlingSummary(sbn.getPackageName(), notificationsToBundle.get(0),
- true, sbn.getUserId());
- adjustNotificationBundling(sbn.getPackageName(), notificationsToBundle, true,
- sbn.getUserId());
- }
- } else {
- // Grouped, but not by us. Send updates to unautobundle, if we bundled it.
- maybeUnbundle(sbn, false, sbn.getUserId());
- }
- } catch (Exception e) {
- Slog.e(TAG, "Failure processing new notification", e);
- }
- }
-
- @Override
- public void onNotificationRemoved(StatusBarNotification sbn) {
- try {
- maybeUnbundle(sbn, true, sbn.getUserId());
- } catch (Exception e) {
- Slog.e(TAG, "Error processing canceled notification", e);
- }
- }
-
- /**
- * Un-autobundles notifications that are now grouped by the app. Additionally cancels
- * autobundling if the status change of this notification resulted in the loose notification
- * count being under the limit.
- */
- private void maybeUnbundle(StatusBarNotification sbn, boolean notificationGone, int user) {
- List<String> notificationsToUnAutobundle = new ArrayList<>();
- boolean removeSummary = false;
- synchronized (mUnbundledNotifications) {
- Map<String, LinkedHashSet<String>> unbundledNotificationsByUser
- = mUnbundledNotifications.get(sbn.getUserId());
- if (unbundledNotificationsByUser == null || unbundledNotificationsByUser.size() == 0) {
- return;
- }
- LinkedHashSet<String> notificationsForPackage
- = unbundledNotificationsByUser.get(sbn.getPackageName());
- if (notificationsForPackage == null || notificationsForPackage.size() == 0) {
- return;
- }
- if (notificationsForPackage.remove(sbn.getKey())) {
- if (!notificationGone) {
- // Add the current notification to the unbundling list if it still exists.
- notificationsToUnAutobundle.add(sbn.getKey());
- }
- // If the status change of this notification has brought the number of loose
- // notifications back below the limit, remove the summary and un-autobundle.
- if (notificationsForPackage.size() == AUTOBUNDLE_AT_COUNT - 1) {
- removeSummary = true;
- for (String key : notificationsForPackage) {
- notificationsToUnAutobundle.add(key);
- }
- }
- }
- }
- if (notificationsToUnAutobundle.size() > 0) {
- if (removeSummary) {
- adjustAutobundlingSummary(sbn.getPackageName(), null, false, user);
- }
- adjustNotificationBundling(sbn.getPackageName(), notificationsToUnAutobundle, false,
- user);
- }
- }
-
- @Override
- public void onListenerConnected() {
- if (DEBUG) Log.i(TAG, "CONNECTED");
- mUnbundledNotifications = new HashMap<>();
- for (StatusBarNotification sbn : getActiveNotifications()) {
- onNotificationPosted(sbn);
- }
- }
-
- private void adjustAutobundlingSummary(String packageName, String key, boolean summaryNeeded,
- int user) {
- Bundle signals = new Bundle();
- if (summaryNeeded) {
- signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, true);
- signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY);
- } else {
- signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false);
- }
- Adjustment adjustment = new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals,
- getContext().getString(R.string.notification_ranker_autobundle_explanation), null,
- user);
- if (DEBUG) {
- Log.i(TAG, "Summary update for: " + packageName + " "
- + (summaryNeeded ? "adding" : "removing"));
- }
- try {
- adjustNotification(adjustment);
- } catch (Exception e) {
- Slog.e(TAG, "Adjustment failed", e);
- }
-
- }
- private void adjustNotificationBundling(String packageName, List<String> keys, boolean bundle,
- int user) {
- List<Adjustment> adjustments = new ArrayList<>();
- for (String key : keys) {
- adjustments.add(createBundlingAdjustment(packageName, key, bundle, user));
- if (DEBUG) Log.i(TAG, "Sending bundling adjustment for: " + key);
- }
- try {
- adjustNotifications(adjustments);
- } catch (Exception e) {
- Slog.e(TAG, "Adjustments failed", e);
- }
- }
-
- private Adjustment createBundlingAdjustment(String packageName, String key, boolean bundle,
- int user) {
- Bundle signals = new Bundle();
- if (bundle) {
- signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY);
- } else {
- signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
- }
- return new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals,
- getContext().getString(R.string.notification_ranker_autobundle_explanation),
- null, user);
- }
-
}
\ No newline at end of file
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 3b575a8..662a1cd 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -16,6 +16,7 @@
package com.android.externalstorage;
+import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -40,8 +41,8 @@
import android.os.storage.VolumeInfo;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract.Path;
+import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
import android.provider.MediaStore;
import android.provider.Settings;
@@ -325,14 +326,19 @@
}
private File getFileForDocId(String docId, boolean visible) throws FileNotFoundException {
- return resolveDocId(docId, visible).second;
+ RootInfo root = getRootFromDocId(docId);
+ return buildFile(root, docId, visible);
}
private Pair<RootInfo, File> resolveDocId(String docId, boolean visible)
throws FileNotFoundException {
+ RootInfo root = getRootFromDocId(docId);
+ return Pair.create(root, buildFile(root, docId, visible));
+ }
+
+ private RootInfo getRootFromDocId(String docId) throws FileNotFoundException {
final int splitIndex = docId.indexOf(':', 1);
final String tag = docId.substring(0, splitIndex);
- final String path = docId.substring(splitIndex + 1);
RootInfo root;
synchronized (mRootsLock) {
@@ -342,6 +348,14 @@
throw new FileNotFoundException("No root for " + tag);
}
+ return root;
+ }
+
+ private File buildFile(RootInfo root, String docId, boolean visible)
+ throws FileNotFoundException {
+ final int splitIndex = docId.indexOf(':', 1);
+ final String path = docId.substring(splitIndex + 1);
+
File target = visible ? root.visiblePath : root.path;
if (target == null) {
return null;
@@ -353,7 +367,7 @@
if (!target.exists()) {
throw new FileNotFoundException("Missing file for " + docId + " at " + target);
}
- return Pair.create(root, target);
+ return target;
}
private void includeFile(MatrixCursor result, String docId, File file)
@@ -430,25 +444,33 @@
}
@Override
- public Path findPath(String documentId)
+ public Path findPath(String childDocId, @Nullable String parentDocId)
throws FileNotFoundException {
LinkedList<String> path = new LinkedList<>();
- final Pair<RootInfo, File> resolvedDocId = resolveDocId(documentId, false);
- RootInfo root = resolvedDocId.first;
- File file = resolvedDocId.second;
+ final Pair<RootInfo, File> resolvedDocId = resolveDocId(childDocId, false);
+ final RootInfo root = resolvedDocId.first;
+ File child = resolvedDocId.second;
- if (!file.exists()) {
- throw new FileNotFoundException();
+ final File parent = TextUtils.isEmpty(parentDocId)
+ ? root.path
+ : getFileForDocId(parentDocId);
+
+ if (!child.exists()) {
+ throw new FileNotFoundException(childDocId + " is not found.");
}
- while (file != null && file.getAbsolutePath().startsWith(root.path.getAbsolutePath())) {
- path.addFirst(getDocIdForFile(file));
-
- file = file.getParentFile();
+ if (!child.getAbsolutePath().startsWith(parent.getAbsolutePath())) {
+ throw new FileNotFoundException(childDocId + " is not found under " + parentDocId);
}
- return new Path(root.rootId, path);
+ while (child != null && child.getAbsolutePath().startsWith(parent.getAbsolutePath())) {
+ path.addFirst(getDocIdForFile(child));
+
+ child = child.getParentFile();
+ }
+
+ return new Path(parentDocId == null ? root.rootId : null, path);
}
@Override
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
index caeb74c..d19821f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -41,21 +41,12 @@
private static final long ANNOUNCEMENT_DELAY = 250;
private static final int DEFAULT_COLOR = -1;
- private final KeyguardUpdateMonitor mUpdateMonitor;
private final Handler mHandler;
private final int mDefaultColor;
- CharSequence mMessage;
+ private CharSequence mMessage;
private int mNextMessageColor = DEFAULT_COLOR;
- private final Runnable mClearMessageRunnable = new Runnable() {
- @Override
- public void run() {
- mMessage = null;
- update();
- }
- };
-
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
public void onFinishedGoingToSleep(int why) {
setSelected(false);
@@ -70,11 +61,14 @@
}
public KeyguardMessageArea(Context context, AttributeSet attrs) {
+ this(context, attrs, KeyguardUpdateMonitor.getInstance(context));
+ }
+
+ public KeyguardMessageArea(Context context, AttributeSet attrs, KeyguardUpdateMonitor monitor) {
super(context, attrs);
setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
- mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
- mUpdateMonitor.registerCallback(mInfoCallback);
+ monitor.registerCallback(mInfoCallback);
mHandler = new Handler(Looper.myLooper());
mDefaultColor = getCurrentTextColor();
@@ -137,8 +131,8 @@
}
private void clearMessage() {
- mHandler.removeCallbacks(mClearMessageRunnable);
- mHandler.post(mClearMessageRunnable);
+ mMessage = null;
+ update();
}
private void update() {
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 2da4d6a..4b9415e 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -71,6 +71,7 @@
android:name=".ui.SelectPrinterActivity"
android:label="@string/all_printers_label"
android:theme="@style/Theme.SelectPrinterActivity"
+ android:parentActivityName=".ui.PrintActivity"
android:exported="false">
</activity>
@@ -78,6 +79,7 @@
android:name=".ui.AddPrinterActivity"
android:label="@string/print_add_printer"
android:theme="@style/Theme.AddPrinterActivity"
+ android:parentActivityName=".ui.SelectPrinterActivity"
android:exported="false">
</activity>
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 31a776c..94519d4 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -49,8 +49,10 @@
android:id="@+id/summary_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
+ android:paddingTop="2dip"
android:paddingStart="16dip"
android:paddingEnd="16dip"
+ android:paddingBottom="8dip"
android:orientation="horizontal"
android:elevation="@dimen/preview_controls_elevation"
android:background="?android:attr/colorPrimary">
diff --git a/packages/PrintSpooler/res/values-kn-rIN/strings.xml b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
index c6e36d2..64cb540 100644
--- a/packages/PrintSpooler/res/values-kn-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
@@ -81,7 +81,7 @@
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ರದ್ದು ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"ಮುದ್ರಕ ದೋಷ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="blocked_notification_title_template" msgid="1175435827331588646">"ಮುದ್ರಕವು <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ನಿರ್ಬಂಧಿಸಿದೆ"</string>
- <string name="cancel" msgid="4373674107267141885">"ರದ್ದುಮಾಡು"</string>
+ <string name="cancel" msgid="4373674107267141885">"ರದ್ದುಮಾಡಿ"</string>
<string name="restart" msgid="2472034227037808749">"ಮರುಪ್ರಾರಂಭಿಸು"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ಮುದ್ರಕಕ್ಕೆ ಸಂಪರ್ಕವಿಲ್ಲ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ಅಪರಿಚಿತ"</string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index cc69089..74582f3 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -17,6 +17,7 @@
package com.android.printspooler.ui;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.LoaderManager;
import android.content.ComponentName;
@@ -24,13 +25,17 @@
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.content.Loader;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.print.PrintManager;
import android.print.PrintServicesLoader;
import android.print.PrinterId;
import android.print.PrinterInfo;
+import android.printservice.PrintService;
import android.printservice.PrintServiceInfo;
import android.provider.Settings;
import android.text.TextUtils;
@@ -76,6 +81,8 @@
private static final int LOADER_ID_PRINT_REGISTRY_INT = 2;
private static final int LOADER_ID_ENABLED_PRINT_SERVICES = 3;
+ private static final int INFO_INTENT_REQUEST_CODE = 1;
+
public static final String INTENT_EXTRA_PRINTER = "INTENT_EXTRA_PRINTER";
private static final String EXTRA_PRINTER = "EXTRA_PRINTER";
@@ -83,6 +90,7 @@
private static final String KEY_NOT_FIRST_CREATE = "KEY_NOT_FIRST_CREATE";
private static final String KEY_DID_SEARCH = "DID_SEARCH";
+ private static final String KEY_PRINTER_FOR_INFO_INTENT = "KEY_PRINTER_FOR_INFO_INTENT";
// Constants for MetricsLogger.count and MetricsLogger.histo
private static final String PRINTERS_LISTED_COUNT = "printers_listed";
@@ -100,6 +108,12 @@
private boolean mDidSearch;
+ /**
+ * Printer we are currently in the info intent for. This is only non-null while this activity
+ * started an info intent that has not yet returned
+ */
+ private @Nullable PrinterInfo mPrinterForInfoIntent;
+
private void startAddPrinterActivity() {
MetricsLogger.action(this, MetricsEvent.ACTION_PRINT_SERVICE_ADD);
startActivity(new Intent(this, AddPrinterActivity.class));
@@ -112,6 +126,8 @@
setContentView(R.layout.select_printer_activity);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+
mEnabledPrintServices = new ArrayMap<>();
mPrinterRegistry = new PrinterRegistry(this, null, LOADER_ID_PRINT_REGISTRY,
@@ -200,6 +216,7 @@
if (savedInstanceState != null) {
mDidSearch = savedInstanceState.getBoolean(KEY_DID_SEARCH);
+ mPrinterForInfoIntent = savedInstanceState.getParcelable(KEY_PRINTER_FOR_INFO_INTENT);
}
}
@@ -208,6 +225,7 @@
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_NOT_FIRST_CREATE, true);
outState.putBoolean(KEY_DID_SEARCH, mDidSearch);
+ outState.putParcelable(KEY_PRINTER_FOR_INFO_INTENT, mPrinterForInfoIntent);
}
@Override
@@ -252,6 +270,16 @@
}
@Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
if (view == mListView) {
final int position = ((AdapterContextMenuInfo) menuInfo).position;
@@ -353,6 +381,24 @@
super.onDestroy();
}
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case INFO_INTENT_REQUEST_CODE:
+ if (resultCode == RESULT_OK &&
+ data != null &&
+ data.getBooleanExtra(PrintService.EXTRA_SELECT_PRINTER, false) &&
+ mPrinterForInfoIntent != null &&
+ mPrinterForInfoIntent.getStatus() != PrinterInfo.STATUS_UNAVAILABLE) {
+ onPrinterSelected(mPrinterForInfoIntent);
+ }
+ mPrinterForInfoIntent = null;
+ break;
+ default:
+ // not reached
+ }
+ }
+
private void onPrinterSelected(PrinterInfo printer) {
Intent intent = new Intent();
intent.putExtra(INTENT_EXTRA_PRINTER, printer);
@@ -418,6 +464,26 @@
}
}
+ /**
+ * Return the target SDK of the package that defined the printer.
+ *
+ * @param printer The printer
+ *
+ * @return The target SDK that defined a printer.
+ */
+ private int getTargetSDKOfPrintersService(@NonNull PrinterInfo printer) {
+ ApplicationInfo serviceAppInfo;
+ try {
+ serviceAppInfo = getPackageManager().getApplicationInfo(
+ printer.getId().getServiceName().getPackageName(), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Could not find package that defined the printer", e);
+ return Build.VERSION_CODES.KITKAT;
+ }
+
+ return serviceAppInfo.targetSdkVersion;
+ }
+
private final class DestinationAdapter extends BaseAdapter implements Filterable {
private final Object mLock = new Object();
@@ -638,15 +704,17 @@
LinearLayout moreInfoView = (LinearLayout) convertView.findViewById(R.id.more_info);
if (printer.getInfoIntent() != null) {
moreInfoView.setVisibility(View.VISIBLE);
- moreInfoView.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- try {
- startIntentSender(printer.getInfoIntent().getIntentSender(), null, 0, 0,
- 0);
- } catch (SendIntentException e) {
- Log.e(LOG_TAG, "Could not execute pending info intent: %s", e);
- }
+ moreInfoView.setOnClickListener(v -> {
+ Intent fillInIntent = new Intent();
+ fillInIntent.putExtra(PrintService.EXTRA_CAN_SELECT_PRINTER, true);
+
+ try {
+ mPrinterForInfoIntent = printer;
+ startIntentSenderForResult(printer.getInfoIntent().getIntentSender(),
+ INFO_INTENT_REQUEST_CODE, fillInIntent, 0, 0, 0);
+ } catch (SendIntentException e) {
+ mPrinterForInfoIntent = null;
+ Log.e(LOG_TAG, "Could not execute pending info intent: %s", e);
}
});
} else {
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 017fff8..3c0247d 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -286,8 +286,8 @@
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Μετατροπή…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Με κρυπτογράφηση αρχείου"</string>
<string name="title_convert_fbe" msgid="1263622876196444453">"Μετατροπή σε κρυπτογράφηση βάσει αρχείου…"</string>
- <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Μετατροπή τμήματος δεδομένων σε κρυπτογράφηση βάσει αρχείου.\n !!Προσοχή!! Με αυτήν την ενέργεια, θα διαγραφούν όλα τα δεδομένα σας.\n Αυτή η λειτουργία βρίσκεται σε δοκιμαστικό στάδιο alpha και ενδέχεται να μην λειτουργεί σωστά.\n Πατήστε \"Εκκαθάριση και μετατροπή…\" για να συνεχίσετε."</string>
- <string name="button_convert_fbe" msgid="5152671181309826405">"Εκκαθάριση και μετατροπή…"</string>
+ <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Μετατροπή τμήματος δεδομένων σε κρυπτογράφηση βάσει αρχείου.\n !!Προσοχή!! Με αυτήν την ενέργεια, θα διαγραφούν όλα τα δεδομένα σας.\n Αυτή η λειτουργία βρίσκεται σε δοκιμαστικό στάδιο alpha και ενδέχεται να μην λειτουργεί σωστά.\n Πατήστε \"Διαγραφή και μετατροπή…\" για να συνεχίσετε."</string>
+ <string name="button_convert_fbe" msgid="5152671181309826405">"Διαγραφή και μετατροπή…"</string>
<string name="picture_color_mode" msgid="4560755008730283695">"Λειτουργία χρώματος εικόνας"</string>
<string name="picture_color_mode_desc" msgid="1141891467675548590">"Χρήση sRGB"</string>
<string name="daltonizer_mode_disabled" msgid="7482661936053801862">"Απενεργοποιημένο"</string>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index b89bad8..d7c8d03 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -72,7 +72,7 @@
<string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ಇನ್ಪುಟ್ಗಾಗಿ ಬಳಸು"</string>
<string name="bluetooth_pairing_accept" msgid="6163520056536604875">"ಜೋಡಿ"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ಜೋಡಿ ಮಾಡು"</string>
- <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ರದ್ದುಮಾಡು"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ರದ್ದುಮಾಡಿ"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"ಸಂಪರ್ಕಪಡಿಸಿದಾಗ, ಜೋಡಿಸುವಿಕೆಯು ನಿಮ್ಮ ಸಂಪರ್ಕಗಳು ಮತ್ತು ಕರೆ ಇತಿಹಾಸಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಜೊತೆಗೆ ಜೋಡಣೆ ಮಾಡಲಾಗಲಿಲ್ಲ."</string>
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ತಪ್ಪಾಗಿರುವ ಪಿನ್ ಅಥವಾ ಪಾಸ್ಕೀ ಕಾರಣದಿಂದಾಗಿ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಜೊತೆಗೆ ಜೋಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ."</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index a6d0cba..1f192c1 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -245,7 +245,7 @@
<string name="animator_duration_scale_title" msgid="3406722410819934083">"Длительность анимации"</string>
<string name="overlay_display_devices_title" msgid="5364176287998398539">"Эмуляция доп. экранов"</string>
<string name="debug_applications_category" msgid="4206913653849771549">"Приложения"</string>
- <string name="immediately_destroy_activities" msgid="1579659389568133959">"Не сохранять действия"</string>
+ <string name="immediately_destroy_activities" msgid="1579659389568133959">"Не сохранять активности"</string>
<string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"Удалять сводку действий после их завершения"</string>
<string name="app_process_limit_title" msgid="4280600650253107163">"Лимит фоновых процессов"</string>
<string name="show_all_anrs" msgid="28462979638729082">"Все ANR"</string>
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
index e2e721c..0aa76a0 100755
--- a/packages/SettingsLib/res/values/config.xml
+++ b/packages/SettingsLib/res/values/config.xml
@@ -23,8 +23,8 @@
<!-- Default data warning level in mb -->
<integer name="default_data_warning_level_mb">2048</integer>
- <!-- Whether to send a custom package name with the PSD. translatable="false"-->
- <bool name="config_sendPackageName">true</bool>
+ <!-- Whether to send a custom package name with the PSD.-->
+ <bool name="config_sendPackageName">false</bool>
<!-- Name for the set of keys associating package names -->
<string name="config_helpPackageNameKey" translatable="false"></string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index 4ec4f4f..c1f5660 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -24,6 +24,8 @@
public static final String CATEGORY_NETWORK = "com.android.settings.category.ia.wireless";
public static final String CATEGORY_DEVICE = "com.android.settings.category.ia.device";
public static final String CATEGORY_APPS = "com.android.settings.category.ia.apps";
+ public static final String CATEGORY_APPS_DEFAULT =
+ "com.android.settings.category.ia.apps.default";
public static final String CATEGORY_BATTERY = "com.android.settings.category.ia.battery";
public static final String CATEGORY_DISPLAY = "com.android.settings.category.ia.display";
public static final String CATEGORY_SOUND = "com.android.settings.category.ia.sound";
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index b2ce13f..ac10ca8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -142,9 +142,9 @@
// Only add Settings for this user.
getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true);
getTilesForAction(context, user, OPERATOR_SETTINGS, cache,
- OPERATOR_DEFAULT_CATEGORY, tiles, false);
+ OPERATOR_DEFAULT_CATEGORY, tiles, false, true);
getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,
- MANUFACTURER_DEFAULT_CATEGORY, tiles, false);
+ MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true);
}
if (setup) {
getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);
@@ -211,12 +211,20 @@
private static void getTilesForAction(Context context,
UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings) {
+ getTilesForAction(context, user, action, addedCache, defaultCategory, outTiles,
+ requireSettings, requireSettings);
+ }
+
+ private static void getTilesForAction(Context context,
+ UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
+ String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
+ boolean usePriority) {
Intent intent = new Intent(action);
if (requireSettings) {
intent.setPackage(SETTING_PKG);
}
getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,
- requireSettings, true);
+ usePriority, true);
}
public static void getTilesForIntent(Context context, UserHandle user, Intent intent,
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index c6b9c1a0..c2161ae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -407,7 +407,11 @@
}
public DetailedState getDetailedState() {
- return mNetworkInfo != null ? mNetworkInfo.getDetailedState() : null;
+ if (mNetworkInfo != null) {
+ return mNetworkInfo.getDetailedState();
+ }
+ Log.w(TAG, "NetworkInfo is null, cannot return detailed state");
+ return null;
}
public String getSavedNetworkSummary() {
@@ -847,7 +851,10 @@
return context.getString(R.string.wifi_connected_no_internet);
}
}
-
+ if (state == null) {
+ Log.w(TAG, "state is null, returning empty summary");
+ return "";
+ }
String[] formats = context.getResources().getStringArray((ssid == null)
? R.array.wifi_status : R.array.wifi_status_with_ssid);
int index = state.ordinal();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index bfe8c07..77a45b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -484,7 +484,8 @@
mMainHandler.scheduleAPCopyingAndCloseWriteLock();
}
- private AccessPoint getCachedOrCreate(ScanResult result, List<AccessPoint> cache) {
+ @VisibleForTesting
+ AccessPoint getCachedOrCreate(ScanResult result, List<AccessPoint> cache) {
final int N = cache.size();
for (int i = 0; i < N; i++) {
if (cache.get(i).matches(result)) {
@@ -493,10 +494,13 @@
return ret;
}
}
- return new AccessPoint(mContext, result);
+ final AccessPoint accessPoint = new AccessPoint(mContext, result);
+ accessPoint.setListener(mAccessPointListenerAdapter);
+ return accessPoint;
}
- private AccessPoint getCachedOrCreate(WifiConfiguration config, List<AccessPoint> cache) {
+ @VisibleForTesting
+ AccessPoint getCachedOrCreate(WifiConfiguration config, List<AccessPoint> cache) {
final int N = cache.size();
for (int i = 0; i < N; i++) {
if (cache.get(i).matches(config)) {
@@ -505,7 +509,7 @@
return ret;
}
}
- AccessPoint accessPoint = new AccessPoint(mContext, config);
+ final AccessPoint accessPoint = new AccessPoint(mContext, config);
accessPoint.setListener(mAccessPointListenerAdapter);
return accessPoint;
}
diff --git a/packages/SettingsLib/tests/AndroidManifest.xml b/packages/SettingsLib/tests/AndroidManifest.xml
index 9fd5a41..00b2164 100644
--- a/packages/SettingsLib/tests/AndroidManifest.xml
+++ b/packages/SettingsLib/tests/AndroidManifest.xml
@@ -21,6 +21,8 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY"/>
<uses-permission android:name="android.permission.SET_TIME_ZONE" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+
<application>
<uses-library android:name="android.test.runner" />
diff --git a/packages/SettingsLib/tests/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/src/com/android/settingslib/wifi/WifiTrackerTest.java
new file mode 100644
index 0000000..c650190
--- /dev/null
+++ b/packages/SettingsLib/tests/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.settingslib.wifi;
+
+
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.List;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WifiTrackerTest {
+
+ @Test
+ public void testAccessPointListenerSetWhenLookingUpUsingScanResults() {
+ ScanResult scanResult = new ScanResult();
+ scanResult.level = 123;
+ scanResult.BSSID = "bssid-" + 111;
+ scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
+ scanResult.capabilities = "";
+
+ WifiTracker tracker = new WifiTracker(
+ InstrumentationRegistry.getTargetContext(), null, true, true);
+
+ AccessPoint result = tracker.getCachedOrCreate(scanResult, new ArrayList<AccessPoint>());
+ assertTrue(result.mAccessPointListener != null);
+ }
+
+ @Test
+ public void testAccessPointListenerSetWhenLookingUpUsingWifiConfiguration() {
+ WifiConfiguration configuration = new WifiConfiguration();
+ configuration.SSID = "test123";
+ configuration.BSSID="bssid";
+ configuration.networkId = 123;
+ configuration.allowedKeyManagement = new BitSet();
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+
+ WifiTracker tracker = new WifiTracker(
+ InstrumentationRegistry.getTargetContext(), null, true, true);
+
+ AccessPoint result = tracker.getCachedOrCreate(configuration, new ArrayList<AccessPoint>());
+ assertTrue(result.mAccessPointListener != null);
+ }
+}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 672f88d..bb85de2 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -69,6 +69,7 @@
<integer name="def_power_sounds_enabled">1</integer>
<string name="def_low_battery_sound" translatable="false">/system/media/audio/ui/LowBattery.ogg</string>
<integer name="def_dock_sounds_enabled">0</integer>
+ <integer name="def_dock_sounds_enabled_when_accessibility">0</integer>
<string name="def_desk_dock_sound" translatable="false">/system/media/audio/ui/Dock.ogg</string>
<string name="def_desk_undock_sound" translatable="false">/system/media/audio/ui/Undock.ogg</string>
<string name="def_car_dock_sound" translatable="false">/system/media/audio/ui/Dock.ogg</string>
@@ -222,4 +223,7 @@
<!-- Default setting for ability to add users from the lock screen -->
<bool name="def_add_users_from_lockscreen">false</bool>
+
+ <!-- default setting for Settings.System.END_BUTTON_BEHAVIOR : END_BUTTON_BEHAVIOR_SLEEP -->
+ <integer name="def_end_button_behavior">0x2</integer>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index c1a1f84..d55bb4f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2662,6 +2662,8 @@
R.string.def_low_battery_sound);
loadIntegerSetting(stmt, Settings.Global.DOCK_SOUNDS_ENABLED,
R.integer.def_dock_sounds_enabled);
+ loadIntegerSetting(stmt, Settings.Global.DOCK_SOUNDS_ENABLED_WHEN_ACCESSIBILITY,
+ R.integer.def_dock_sounds_enabled_when_accessibility);
loadStringSetting(stmt, Settings.Global.DESK_DOCK_SOUND,
R.string.def_desk_dock_sound);
loadStringSetting(stmt, Settings.Global.DESK_UNDOCK_SOUND,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index afc524c..e7f5f4f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2137,7 +2137,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 132;
+ private static final int SETTINGS_VERSION = 134;
private final int mUserId;
@@ -2452,6 +2452,21 @@
}
if (currentVersion == 130) {
+ // Split Ambient settings
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ boolean dozeExplicitlyDisabled = "0".equals(secureSettings.
+ getSettingLocked(Settings.Secure.DOZE_ENABLED).getValue());
+
+ if (dozeExplicitlyDisabled) {
+ secureSettings.insertSettingLocked(Settings.Secure.DOZE_PULSE_ON_PICK_UP,
+ "0", SettingsState.SYSTEM_PACKAGE_NAME);
+ secureSettings.insertSettingLocked(Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
+ "0", SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ currentVersion = 131;
+ }
+
+ if (currentVersion == 131) {
// Initialize new multi-press timeout to default value
final SettingsState systemSecureSettings = getSecureSettingsLocked(userId);
final String oldValue = systemSecureSettings.getSettingLocked(
@@ -2464,11 +2479,11 @@
SettingsState.SYSTEM_PACKAGE_NAME);
}
- currentVersion = 131;
+ currentVersion = 132;
}
- if (currentVersion == 131) {
- // Version 131: Allow managed profile to optionally use the parent's ringtones
+ if (currentVersion == 132) {
+ // Version 132: Allow managed profile to optionally use the parent's ringtones
final SettingsState systemSecureSettings = getSecureSettingsLocked(userId);
String defaultSyncParentSounds = (getContext().getResources()
.getBoolean(R.bool.def_sync_parent_sounds) ? "1" : "0");
@@ -2476,7 +2491,20 @@
Settings.Secure.SYNC_PARENT_SOUNDS,
defaultSyncParentSounds,
SettingsState.SYSTEM_PACKAGE_NAME);
- currentVersion = 132;
+ currentVersion = 133;
+ }
+
+ if (currentVersion == 133) {
+ // Version 133: Add default end button behavior
+ final SettingsState systemSettings = getSystemSettingsLocked(userId);
+ if (systemSettings.getSettingLocked(Settings.System.END_BUTTON_BEHAVIOR) ==
+ null) {
+ String defaultEndButtonBehavior = Integer.toString(getContext()
+ .getResources().getInteger(R.integer.def_end_button_behavior));
+ systemSettings.insertSettingLocked(Settings.System.END_BUTTON_BEHAVIOR,
+ defaultEndButtonBehavior, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ currentVersion = 134;
}
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 02518f2..4d59d57 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -389,7 +389,7 @@
<!-- started from PipUI -->
<activity
- android:name="com.android.systemui.tv.pip.PipMenuActivity"
+ android:name=".pip.tv.PipMenuActivity"
android:exported="true"
android:theme="@style/PipTheme"
android:launchMode="singleTop"
@@ -400,7 +400,7 @@
androidprv:alwaysFocusable="true"
android:excludeFromRecents="true" />
<activity
- android:name="com.android.systemui.tv.pip.PipOverlayActivity"
+ android:name=".pip.tv.PipOverlayActivity"
android:exported="true"
android:theme="@style/PipTheme"
android:taskAffinity=""
@@ -409,7 +409,7 @@
android:supportsPictureInPicture="true"
android:excludeFromRecents="true" />
<activity
- android:name="com.android.systemui.tv.pip.PipOnboardingActivity"
+ android:name=".pip.tv.PipOnboardingActivity"
android:exported="true"
android:theme="@style/PipTheme"
android:launchMode="singleTop"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
new file mode 100644
index 0000000..0728482
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.plugins.statusbar.phone;
+
+import android.view.MotionEvent;
+
+import com.android.systemui.plugins.Plugin;
+
+public interface NavGesture extends Plugin {
+
+ public static final String ACTION = "com.android.systemui.action.PLUGIN_NAV_GESTURE";
+
+ public static final int VERSION = 1;
+
+ public GestureHelper getGestureHelper();
+
+ public interface GestureHelper {
+ public boolean onTouchEvent(MotionEvent event);
+
+ public boolean onInterceptTouchEvent(MotionEvent event);
+
+ public void setBarState(boolean vertical, boolean isRtl);
+ }
+
+}
diff --git a/packages/SystemUI/res/drawable/pip_dismiss.xml b/packages/SystemUI/res/drawable/pip_dismiss.xml
new file mode 100644
index 0000000..f656eeb
--- /dev/null
+++ b/packages/SystemUI/res/drawable/pip_dismiss.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="42.0dp"
+ android:height="42.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M38.000000,12.800000l-2.799999,-2.800000 -11.200001,11.200001 -11.200000,-11.200001 -2.800000,2.800000 11.200001,11.200000 -11.200001,11.200001 2.800000,2.799999 11.200000,-11.200001 11.200001,11.200001 2.799999,-2.799999 -11.200001,-11.200001z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/pip_dismiss_background.xml b/packages/SystemUI/res/drawable/pip_dismiss_background.xml
new file mode 100644
index 0000000..3a75296
--- /dev/null
+++ b/packages/SystemUI/res/drawable/pip_dismiss_background.xml
@@ -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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <corners
+ android:radius="100dp" />
+ <solid
+ android:color="#66000000" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
index f7f673d..999dbac 100644
--- a/packages/SystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/SystemUI/res/layout/car_navigation_bar.xml
@@ -21,6 +21,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
+ android:gravity="center"
android:background="@drawable/system_bar_background">
<!-- phone.NavigationBarView has rot0 and rot90 but we expect the car head unit to have a fixed
@@ -28,7 +29,7 @@
-->
<LinearLayout
android:layout_height="match_parent"
- android:layout_width="match_parent"
+ android:layout_width="@dimen/car_navigation_bar_width"
android:orientation="horizontal"
android:clipChildren="false"
android:clipToPadding="false"
diff --git a/packages/SystemUI/res/layout/pip_dismiss_view.xml b/packages/SystemUI/res/layout/pip_dismiss_view.xml
new file mode 100644
index 0000000..141e610
--- /dev/null
+++ b/packages/SystemUI/res/layout/pip_dismiss_view.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pip_dismiss_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/pip_dismiss_background"
+ android:foreground="@drawable/pip_dismiss"
+ android:alpha="0"
+ android:forceHasOverlappingRendering="false" />
diff --git a/packages/SystemUI/res/layout/tv_pip_control_button.xml b/packages/SystemUI/res/layout/tv_pip_control_button.xml
index 096dda8..b9b0154 100644
--- a/packages/SystemUI/res/layout/tv_pip_control_button.xml
+++ b/packages/SystemUI/res/layout/tv_pip_control_button.xml
@@ -17,7 +17,7 @@
*/
-->
-<!-- Layout for {@link com.android.systemui.tv.pip.PipControlButtonView}. -->
+<!-- Layout for {@link com.android.systemui.pip.tv.PipControlButtonView}. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView android:id="@+id/button"
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml
index 49119fb..c6bcd32 100644
--- a/packages/SystemUI/res/layout/tv_pip_controls.xml
+++ b/packages/SystemUI/res/layout/tv_pip_controls.xml
@@ -17,17 +17,17 @@
*/
-->
-<!-- Layout for {@link com.android.systemui.tv.pip.PipControlsView}. -->
+<!-- Layout for {@link com.android.systemui.pip.tv.PipControlsView}. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <com.android.systemui.tv.pip.PipControlButtonView
+ <com.android.systemui.pip.tv.PipControlButtonView
android:id="@+id/full_button"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:src="@drawable/ic_fullscreen_white_24dp"
android:text="@string/pip_fullscreen" />
- <com.android.systemui.tv.pip.PipControlButtonView
+ <com.android.systemui.pip.tv.PipControlButtonView
android:id="@+id/close_button"
android:layout_width="100dp"
android:layout_height="wrap_content"
@@ -35,7 +35,7 @@
android:src="@drawable/ic_close_white"
android:text="@string/pip_close" />
- <com.android.systemui.tv.pip.PipControlButtonView
+ <com.android.systemui.pip.tv.PipControlButtonView
android:id="@+id/play_pause_button"
android:layout_width="100dp"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
index 72a4929..35f2af4 100644
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -26,7 +26,7 @@
android:gravity="top|center_horizontal"
android:clipChildren="false">
- <com.android.systemui.tv.pip.PipControlsView
+ <com.android.systemui.pip.tv.PipControlsView
android:id="@+id/pip_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
index f157fd5..949400c 100644
--- a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
+++ b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
@@ -20,7 +20,7 @@
android:gravity="top|center_horizontal"
android:orientation="vertical">
- <com.android.systemui.tv.pip.PipRecentsControlsView
+ <com.android.systemui.pip.tv.PipRecentsControlsView
android:id="@+id/pip_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -35,13 +35,13 @@
android:layout_gravity="top|center_horizontal"
android:background="@drawable/tv_pip_recents_overlay_scrim"
android:alpha="0" />
- <com.android.systemui.tv.pip.PipControlsView
+ <com.android.systemui.pip.tv.PipControlsView
android:id="@+id/pip_control_contents"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_gravity="top|center_horizontal" />
- </com.android.systemui.tv.pip.PipRecentsControlsView>
+ </com.android.systemui.pip.tv.PipRecentsControlsView>
<!-- Placeholder view to handle focus change between Recents row and PIP controls
in talkback mode -->
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 82229e3..1131b2a 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Blaaier"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakte"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pos"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Kitsboodskappe"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musiek"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 35ed921..0dc05e6 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"አሳሽ"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"እውቂያዎች"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ኢሜይል"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"ፈጣን መልዕክት"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"ኤስኤምኤስ"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"ሙዚቃ"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"የቀን መቁጠሪያ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 5fc9976..755438e 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -570,7 +570,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"المتصفح"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"جهات الاتصال"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"البريد الإلكتروني"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"الرسائل الفورية"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"الرسائل القصيرة SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"الموسيقى"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"التقويم"</string>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index 451f205..87b4433 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Brauzer"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktlar"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-poçt"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musiqi"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Təqvim"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index a64028d..e173890 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Pregledač"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakti"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Imejl"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Razmena trenutnih poruka"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzika"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendar"</string>
diff --git a/packages/SystemUI/res/values-be-rBY/strings.xml b/packages/SystemUI/res/values-be-rBY/strings.xml
index 81d3e8f..207e223 100644
--- a/packages/SystemUI/res/values-be-rBY/strings.xml
+++ b/packages/SystemUI/res/values-be-rBY/strings.xml
@@ -568,7 +568,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Браўзер"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Кантакты"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Электронная пошта"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Iмгненныя паведамленнi"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS-паведамленні"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музыка"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Каляндар"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index c70a377..16c94d7 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Браузър"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакти"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Електронна поща"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Незабавни съобщения"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музика"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календар"</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 7bd6193..feaa453 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ব্রাউজার"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"পরিচিতি"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ইমেল"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"সংগীত"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ক্যালেন্ডার"</string>
diff --git a/packages/SystemUI/res/values-bs-rBA/strings.xml b/packages/SystemUI/res/values-bs-rBA/strings.xml
index 1699581..f361b93 100644
--- a/packages/SystemUI/res/values-bs-rBA/strings.xml
+++ b/packages/SystemUI/res/values-bs-rBA/strings.xml
@@ -566,7 +566,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Preglednik"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakti"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pošta"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzika"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendar"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 260be35..8bedecd 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactes"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Correu electrònic"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"MI"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendari"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 87fb004..0ff0f93 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -568,7 +568,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Prohlížeč"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakty"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Chat"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Hudba"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendář"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index fb3e16b..b112405 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -476,7 +476,7 @@
<string name="zen_alarm_warning" msgid="444533119582244293">"Du vil ikke kunne høre din næste alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="3980063409350522735">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="4242179982586714810">"på <xliff:g id="WHEN">%1$s</xliff:g>"</string>
- <string name="accessibility_quick_settings_detail" msgid="2579369091672902101">"Hurtigindstillinger <xliff:g id="TITLE">%s</xliff:g>."</string>
+ <string name="accessibility_quick_settings_detail" msgid="2579369091672902101">"Hurtige indstillinger <xliff:g id="TITLE">%s</xliff:g>."</string>
<string name="accessibility_status_bar_hotspot" msgid="4099381329956402865">"Hotspot"</string>
<string name="accessibility_managed_profile" msgid="6613641363112584120">"Arbejdsprofil"</string>
<string name="tuner_warning_title" msgid="7094689930793031682">"Sjovt for nogle, men ikke for alle"</string>
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktpersoner"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Chat"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"Sms"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musik"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 68af5e5..9721362 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakte"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-Mail"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index d8d5e09..276b856 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -163,7 +163,7 @@
<string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Φόρτιση μπαταρίας, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> τοις εκατό."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Ρυθμίσεις συστήματος."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Ειδοποιήσεις."</string>
- <string name="accessibility_remove_notification" msgid="3603099514902182350">"Εκκαθάριση ειδοποίησης."</string>
+ <string name="accessibility_remove_notification" msgid="3603099514902182350">"Διαγραφή ειδοποίησης."</string>
<string name="accessibility_gps_enabled" msgid="3511469499240123019">"Το GPS ενεργοποιήθηκε."</string>
<string name="accessibility_gps_acquiring" msgid="8959333351058967158">"Προσδιορισμός GPS."</string>
<string name="accessibility_tty_enabled" msgid="4613200365379426561">"Το TeleTypewriter ενεργοποιήθηκε."</string>
@@ -244,7 +244,7 @@
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Αναζήτηση για GPS"</string>
<string name="gps_notification_found_text" msgid="4619274244146446464">"Ρύθμιση τοποθεσίας με GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Τα αιτήματα τοποθεσίας έχουν ενεργοποιηθεί"</string>
- <string name="accessibility_clear_all" msgid="5235938559247164925">"Εκκαθάριση όλων των ειδοποιήσεων."</string>
+ <string name="accessibility_clear_all" msgid="5235938559247164925">"Διαγραφή όλων των ειδοποιήσεων."</string>
<string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<plurals name="notification_group_overflow_description" formatted="false" msgid="4579313201268495404">
<item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> επιπλέον ειδοποιήσεις εντός της ομάδας.</item>
@@ -320,7 +320,7 @@
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Λειτουργία εργασίας"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Νυχτερινός φωτισμός"</string>
<string name="recents_empty_message" msgid="808480104164008572">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
- <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Έχει γίνει εκκαθάριση όλων των στοιχείων"</string>
+ <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Έχει γίνει διαγραφή όλων των στοιχείων"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Πληροφορίες εφαρμογής"</string>
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"καρφίτσωμα οθόνης"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"αναζήτηση"</string>
@@ -435,7 +435,7 @@
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Θα εμφανιστεί ξανά την επόμενη φορά που θα το ενεργοποιήσετε στις ρυθμίσεις."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Απόκρυψη"</string>
<string name="volumeui_prompt_message" msgid="918680947433389110">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> θέλει να γίνει το παράθυρο διαλόγου ελέγχου έντασης"</string>
- <string name="volumeui_prompt_allow" msgid="7954396902482228786">"Να επιτραπεί"</string>
+ <string name="volumeui_prompt_allow" msgid="7954396902482228786">"Να επιτρέπεται"</string>
<string name="volumeui_prompt_deny" msgid="5720663643411696731">"Απόρριψη"</string>
<string name="volumeui_notification_title" msgid="4906770126345910955">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> αποτελεί το παράθυρο διαλόγου ελέγχου έντασης"</string>
<string name="volumeui_notification_text" msgid="8819536904234337445">"Πατήστε για να επαναφέρετε την αρχική μορφή της εικόνας."</string>
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Πρόγραμμα περιήγησης"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Επαφές"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Ηλεκτρονικό ταχυδρομείο"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Άμεσα μηνύματα (ΙΜ)"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Μουσική"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Ημερολόγιο"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 570ee82..ca2e5b3 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacts"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 570ee82..ca2e5b3 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacts"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 570ee82..ca2e5b3 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacts"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 7b09b25..81696f6 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactos"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Correo electrónico"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendario"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 004a2b4..aa9578a 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactos"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Correo electrónico"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"MI"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendario"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 10be679..ab81af5 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Brauser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktid"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-post"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM (kiirsuhtlus)"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muusika"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 7514e8e..3617074 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Arakatzailea"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktuak"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Posta"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Istanteko mezularitza"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS mezuak"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musika"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index a4b9798..48f0b954 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"مرورگر"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"مخاطبین"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"رایانامه"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"پیام فوری"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"پیامک"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"موسیقی"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"تقویم"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 42c8ae0..5e1ca4b 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Selain"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Yhteystiedot"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Sähköposti"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Pikaviesti"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"Tekstiviesti"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musiikki"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalenteri"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 8b4faef..765e0aa 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navigateur"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacts"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Courriel"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"MI"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"Message texte"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musique"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Agenda"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 52728bd..e66d342 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navigateur"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacts"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Messagerie"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Messagerie instantanée"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musique"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Agenda"</string>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index d489a64..5ccf4a0 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactos"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Correo electrónico"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"MI"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendario"</string>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index 42546f6e..84f9105 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"બ્રાઉઝર"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"સંપર્કો"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ઇમેઇલ"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"સંગીત"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"કૅલેન્ડર"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 49a4650..89b5a6f 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ब्राउज़र"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"संपर्क"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ईमेल"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS करें"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"संगीत"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"कैलेंडर"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 1bbae8b..3c5812f 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Preglednik"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakti"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pošta"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Izravna poruka"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Glazba"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendar"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 7792ac1..f83ba28 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Böngésző"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Névjegyek"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Azonnali üzenetküldés"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS-üzenetek"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Zene"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Naptár"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 9d07927..4ce22e4 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Դիտարկիչ"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Կոնտակտներ"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Էլփոստ"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Երաժշտություն"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Օրացույց"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 20ef37d..4efffc56 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontak"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musik"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 6a5de8a..4875888 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Vafri"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Tengiliðir"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Tölvupóstur"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Spjall"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS-skilaboð"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Tónlist"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Dagatal"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 39221ad..98a2424 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -344,7 +344,7 @@
<string name="description_target_search" msgid="3091587249776033139">"Ricerca"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Su per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"A sinistra per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="zen_priority_introduction" msgid="3070506961866919502">"Non verrai disturbato da suoni e vibrazioni, ad eccezione di sveglie, promemoria, eventi e chiamate da contatti da te specificati."</string>
+ <string name="zen_priority_introduction" msgid="3070506961866919502">"Non ti disturberanno: suoni e vibrazioni, ad eccezione di sveglie, promemoria, eventi e chiamate da contatti da te specificati."</string>
<string name="zen_priority_customize_button" msgid="7948043278226955063">"Personalizza"</string>
<string name="zen_silence_introduction_voice" msgid="2284540992298200729">"Verranno bloccati TUTTI i suoni e le vibrazioni, anche di sveglie, musica, video e giochi. Potrai ancora telefonare."</string>
<string name="zen_silence_introduction" msgid="3137882381093271568">"Verranno bloccati TUTTI i suoni e le vibrazioni, anche di sveglie, musica, video e giochi."</string>
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contatti"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Chat"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musica"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendario"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 43eeae4..ce73705 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -566,7 +566,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"דפדפן"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"אנשי קשר"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"אימייל"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"שליחת הודעות מיידיות"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"מוזיקה"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"יומן"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7df0a9e..c45fa4f 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -196,13 +196,13 @@
<string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"機内モードがONです。"</string>
<string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"機内モードをOFFにしました。"</string>
<string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"機内モードをONにしました。"</string>
- <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"[通知を非表示]はONで、優先する通知のみです。"</string>
- <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"[通知を非表示]はONで、サイレントです。"</string>
- <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"[通知を非表示]はONで、アラームのみです。"</string>
- <string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"通知を非表示"</string>
- <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"[通知を非表示]はOFFです。"</string>
- <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"[通知を非表示]をOFFにしました。"</string>
- <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"[通知を非表示]をONにしました。"</string>
+ <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"マナーモードは ON で、優先する通知のみ許可します。"</string>
+ <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"マナーモードは ON で、サイレント モードです。"</string>
+ <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"マナーモードは ON で、アラームのみ許可します。"</string>
+ <string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"マナーモード"</string>
+ <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"マナーモードは OFF です。"</string>
+ <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"マナーモードを OFF にしました。"</string>
+ <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"マナーモードを ON にしました。"</string>
<string name="accessibility_quick_settings_bluetooth" msgid="6341675755803320038">"Bluetooth"</string>
<string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"BluetoothがOFFです。"</string>
<string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"BluetoothがONです。"</string>
@@ -263,7 +263,7 @@
<string name="dessert_case" msgid="1295161776223959221">"デザートケース"</string>
<string name="start_dreams" msgid="5640361424498338327">"スクリーン セーバー"</string>
<string name="ethernet_label" msgid="7967563676324087464">"イーサネット"</string>
- <string name="quick_settings_dnd_label" msgid="8735855737575028208">"通知を非表示"</string>
+ <string name="quick_settings_dnd_label" msgid="8735855737575028208">"マナーモード"</string>
<string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"優先する通知のみ"</string>
<string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"アラームのみ"</string>
<string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"サイレント"</string>
@@ -564,14 +564,14 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ブラウザ"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"連絡先"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"メール"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"音楽"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"カレンダー"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"音量調節を表示"</string>
- <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"通知の非表示"</string>
+ <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"マナーモード"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"音量ボタンのショートカット"</string>
- <string name="volume_up_silent" msgid="7141255269783588286">"音量上げボタンで [通知を非表示] を OFF にする"</string>
+ <string name="volume_up_silent" msgid="7141255269783588286">"音量上げボタンでマナーモードを OFF にする"</string>
<string name="battery" msgid="7498329822413202973">"電池"</string>
<string name="clock" msgid="7416090374234785905">"時計"</string>
<string name="headset" msgid="4534219457597457353">"ヘッドセット"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index a63de78..a151b8f 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ბრაუზერი"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"კონტაქტები"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ელფოსტა"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"მუსიკა"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"კალენდარი"</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index f39e44a..6c0c967 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Браузер"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контактілер"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Электрондық пошта"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"Мәтіндік хабар"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Mузыка"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Күнтізбе"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 36d0e44..d3e97cc 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"កម្មវិធីរុករក"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"ទំនាក់ទំនង"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"អ៊ីមែល"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"សារ SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"តន្ត្រី"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ប្រតិទិន"</string>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 3f74b1d..cbd71f9 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -96,7 +96,7 @@
<string name="voice_assist_label" msgid="3956854378310019854">"ಧ್ವನಿ ಸಹಾಯಕವನ್ನು ತೆರೆ"</string>
<string name="camera_label" msgid="7261107956054836961">"ಕ್ಯಾಮರಾ ತೆರೆಯಿರಿ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ಹೊಸ ಕಾರ್ಯ ವಿನ್ಯಾಸವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡು"</string>
+ <string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡಿ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ಹೊಂದಾಣಿಕೆಯ ಝೂಮ್ ಬಟನ್."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ಚಿಕ್ಕ ಪರದೆಯಿಂದ ದೊಡ್ಡ ಪರದೆಗೆ ಝೂಮ್ ಮಾಡು."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ಬ್ಲೂಟೂತ್ ಸಂಪರ್ಕಗೊಂಡಿದೆ."</string>
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ಬ್ರೌಸರ್"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"ಸಂಪರ್ಕಗಳು"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ಇಮೇಲ್"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"ಎಸ್ಎಂಎಸ್"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"ಸಂಗೀತ"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ಕ್ಯಾಲೆಂಡರ್"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 5f1f5cd..3827dba 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -464,7 +464,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"충전 중이 아닌 경우 상태 표시줄 아이콘 내에 배터리 잔량 비율 표시"</string>
<string name="quick_settings" msgid="10042998191725428">"빠른 설정"</string>
<string name="status_bar" msgid="4877645476959324760">"상태 표시줄"</string>
- <string name="overview" msgid="4018602013895926956">"개요"</string>
+ <string name="overview" msgid="4018602013895926956">"최근 사용"</string>
<string name="demo_mode" msgid="2389163018533514619">"데모 모드"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"데모 모드 사용"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"데모 모드 표시"</string>
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"브라우저"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"주소록"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"이메일"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"메신저"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"음악"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"캘린더"</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 9b35ef7..56c8360 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Серепчи"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Байланыштар"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Электрондук почта"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музыка"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Жылнаама"</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index d4acc77..47a49da 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ໂປຣແກຣມທ່ອງເວັບ"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"ລາຍຊື່ຜູ້ຕິດຕໍ່"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ອີເມວ"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"ຂໍ້ຄວາມສັ້ນ(SMS)"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"ດົນຕີ"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ປະຕິທິນ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 5de6b7f..c4676fa 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -566,7 +566,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Naršyklė"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktai"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"El. paštas"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"TP"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzika"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendorius"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 091c47f..a8266dd 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Pārlūkprogramma"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktpersonas"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pasts"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Tūlītējā ziņojumapmaiņa"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"Īsziņas"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Mūzika"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendārs"</string>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 020118b..c5e2500 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Прелистувач"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакти"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Е-пошта"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музика"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календар"</string>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index e07a758..a1a3b31 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ബ്രൗസർ"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"കോൺടാക്റ്റുകൾ"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ഇമെയിൽ"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS:"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"സംഗീതം"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"കലണ്ടർ"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index fcd39d9..9b83058 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Хөтөч"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Харилцагчид"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Имэйл"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Хөгжим"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Хуанли"</string>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 285fd57..8003890 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ब्राउझर"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"संपर्क"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ईमेल"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"संगीत"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"कॅलेंडर"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 88894b5..1a16967 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Penyemak imbas"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kenalan"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mel"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzik"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendar"</string>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index a79c1ae..17b6802 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ဘရောင်ဇာ"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"အဆက်အသွယ်များ"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"အီးမေးလ်"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"အမြန်စာတိုစနစ်"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS စာတိုစနစ်"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 1784641..cb691c5 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Nettleser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakter"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-post"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Chat"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musikk"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 625f959..97f5f5e 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ब्राउजर"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"सम्पर्कहरू"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"इमेल"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"संगीत"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"पात्रो"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index f7e180f..43c577d 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacten"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Chat"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"Sms"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muziek"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Agenda"</string>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index 0587023..6ef8674 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ਬ੍ਰਾਊਜ਼ਰ"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"ਸੰਪਰਕ"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ਈਮੇਲ"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"ਸੰਗੀਤ"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ਕੈਲੰਡਰ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 5bf6a99..03e599c 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -566,7 +566,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Przeglądarka"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakty"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Komunikator"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzyka"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendarz"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index aa1e718..0ae421d 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contatos"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Mensagens instantâneas"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Agenda"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 6037da1..6ad19f9 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactos"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendário"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index aa1e718..0ae421d 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contatos"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Mensagens instantâneas"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Agenda"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index da6a32d..70ef6ea 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -566,7 +566,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Agendă"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzică"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 8257b87..41ec367 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -568,7 +568,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Браузер"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакты"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Эл. почта"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Чат"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музыка"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календарь"</string>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 7ee1e6b..8f65531 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"බ්රවුසරය"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"සම්බන්ධතා"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ඊ-තැපෑල"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"සංගීතය"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"දින දර්ශනය"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 9b68465..276c266 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -568,7 +568,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Prehliadač"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakty"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Čet"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Hudba"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendár"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index fccd6f9..cda2ac9 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -568,7 +568,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Brskalnik"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Stiki"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pošta"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Neposredno sporočanje"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"Sporočila SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Glasba"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Koledar"</string>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index 855c96c..33b2d5b 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Shfletuesi"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktet"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Mail-i"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Mesazh i çastit"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzikë"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendari"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 22b0ba1..11d05b7 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Прегледач"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакти"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Имејл"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Размена тренутних порука"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музика"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календар"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 830df0c..46e3f05 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Webbläsare"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakter"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-post"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Snabbmeddelanden"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"Sms"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musik"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 9ec6ca8..43aa36d 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Kivinjari"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Anwani"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Barua pepe"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Ujumbe wa papo kwa papo"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muziki"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalenda"</string>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index 30223c7..364173e 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"உலாவி"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"தொடர்புகள்"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"மின்னஞ்சல்"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"மியூசிக்"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"கேலெண்டர்"</string>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 4613354..c1e55c8 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"బ్రౌజర్"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"పరిచయాలు"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ఇమెయిల్"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"సంగీతం"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"క్యాలెండర్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 804bdef..f2b1772 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"เบราว์เซอร์"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"รายชื่อติดต่อ"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"อีเมล"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"เพลง"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ปฏิทิน"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index e2e8c27..f58ed17 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Mga Contact"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendaryo"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 8ee4e1f..e7b1abb 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Tarayıcı"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kişiler"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-posta"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Müzik"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Takvim"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 7327a08..886a138 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -568,7 +568,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Веб-переглядач"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакти"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Електронна пошта"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Чат"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музика"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календар"</string>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 8048289..31d90e2 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"براؤزر"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"رابطے"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ای میل"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"موسیقی"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"کیلنڈر"</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index c14ad93..78ad148 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Brauzer"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktlar"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pochta"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musiqa"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Taqvim"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 062c6c2..ba49055 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Trình duyệt"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Danh bạ"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Nhắn tin nhanh"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Âm nhạc"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Lịch"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index b98bf14..7a7991f 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"浏览器"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"通讯录"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"电子邮件"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"即时通讯"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"短信"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"音乐"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"日历"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index d581592..4f7ffe5 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -564,7 +564,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"瀏覽器"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"通訊錄"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"電郵"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"即時通訊"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"短訊"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"音樂"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"日曆"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index fa4cacb..2de07e3 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"瀏覽器"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"聯絡人"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"電子郵件"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"即時訊息"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"簡訊"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"音樂"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"日曆"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 97aa758..6be736a 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -562,7 +562,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Isiphequluli"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Oxhumana nabo"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"I-imeyili"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"I-IM"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"I-SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Umculo"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"I-YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Ikhalenda"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index eb1a1eb..9eea375 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -209,9 +209,6 @@
<!-- Doze: should the significant motion sensor be used as a pulse signal? -->
<bool name="doze_pulse_on_significant_motion">false</bool>
- <!-- Doze: should the pickup sensor be used as a pulse signal? -->
- <bool name="doze_pulse_on_pick_up">false</bool>
-
<!-- Doze: check proximity sensor before pulsing? -->
<bool name="doze_proximity_check_before_pulse">true</bool>
@@ -240,9 +237,6 @@
-->
<string name="doze_pickup_subtype_performs_proximity_check"></string>
- <!-- Type of the double tap sensor. Empty if double tap is not supported. -->
- <string name="doze_double_tap_sensor_type" translatable="false"></string>
-
<!-- Doze: pulse parameter - how long does it take to fade in? -->
<integer name="doze_pulse_duration_in">900</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 549d50e..12f7881 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -694,4 +694,7 @@
<!-- The alpha to apply to the recents row when it doesn't have focus -->
<item name="recents_recents_row_dim_alpha" format="float" type="dimen">0.5</item>
+
+ <!-- The size of the PIP dismiss target. -->
+ <dimen name="pip_dismiss_target_size">48dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml
index 04402b7..988caf5 100644
--- a/packages/SystemUI/res/values/dimens_car.xml
+++ b/packages/SystemUI/res/values/dimens_car.xml
@@ -29,4 +29,5 @@
<dimen name="car_fullscreen_user_pod_image_avatar_height">128dp</dimen>
<dimen name="car_fullscreen_user_pod_text_size">24sp</dimen>
<dimen name="car_navigation_button_width">64dp</dimen>
+ <dimen name="car_navigation_bar_width">760dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 92e10d2..1b7a922 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1667,4 +1667,24 @@
not appear on production builds ever. -->
<string name="plugins" translatable="false">Plugins</string>
+ <!-- PIP section of the tuner. Non-translatable since it should
+ not appear on production builds ever. -->
+ <string name="picture_in_picture" translatable="false">Picture-in-Picture</string>
+
+ <!-- PIP swipe to dismiss title. Non-translatable since it should
+ not appear on production builds ever. -->
+ <string name="pip_swipe_to_dismiss_title" translatable="false">Swipe to dismiss</string>
+
+ <!-- PIP swipe to dismiss description. Non-translatable since it should
+ not appear on production builds ever. -->
+ <string name="pip_swipe_to_dismiss_summary" translatable="false">Swipe left or right off screen to close the PIP</string>
+
+ <!-- PIP drag to dismiss title. Non-translatable since it should
+ not appear on production builds ever. -->
+ <string name="pip_drag_to_dismiss_title" translatable="false">Drag to dismiss</string>
+
+ <!-- PIP drag to dismiss description. Non-translatable since it should
+ not appear on production builds ever. -->
+ <string name="pip_drag_to_dismiss_summary" translatable="false">Drag to the dismiss target at the bottom of the screen to close the PIP</string>
+
</resources>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 211f8e8..46a9ee3 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -121,6 +121,24 @@
</PreferenceScreen>
+ <PreferenceScreen
+ android:key="picture_in_picture"
+ android:title="@string/picture_in_picture">
+
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="pip_swipe_to_dismiss"
+ android:title="@string/pip_swipe_to_dismiss_title"
+ android:summary="@string/pip_swipe_to_dismiss_summary"
+ sysui:defValue="true" />
+
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="pip_drag_to_dismiss"
+ android:title="@string/pip_drag_to_dismiss_title"
+ android:summary="@string/pip_drag_to_dismiss_summary"
+ sysui:defValue="true" />
+
+ </PreferenceScreen>
+
<!--
<Preference
android:key="nav_bar"
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index f76a68c..a4d78c5 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -173,11 +173,7 @@
if (DEBUG) {
Log.d(TAG, "trimMemory");
}
- mBackground.recycle();
- mBackground = null;
- mBackgroundWidth = -1;
- mBackgroundHeight = -1;
- mWallpaperManager.forgetLoadedWallpaper();
+ unloadWallpaper(true /* forgetSize */);
}
}
@@ -199,7 +195,7 @@
public void onDestroy() {
super.onDestroy();
mBackground = null;
- mWallpaperManager.forgetLoadedWallpaper();
+ unloadWallpaper(true /* forgetSize */);
}
boolean updateSurfaceSize(SurfaceHolder surfaceHolder, DisplayInfo displayInfo,
@@ -209,8 +205,7 @@
// Load background image dimensions, if we haven't saved them yet
if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) {
// Need to load the image to get dimensions
- mWallpaperManager.forgetLoadedWallpaper();
- loadWallpaper(forDraw);
+ loadWallpaper(forDraw, true /* needsReset */);
if (DEBUG) {
Log.d(TAG, "Reloading, redoing updateSurfaceSize later.");
}
@@ -366,8 +361,7 @@
((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
dw + ", " + dh);
}
- mWallpaperManager.forgetLoadedWallpaper();
- loadWallpaper(true /* needDraw */);
+ loadWallpaper(true /* needDraw */, true /* needReset */);
if (DEBUG) {
Log.d(TAG, "Reloading, resuming draw later");
}
@@ -426,8 +420,7 @@
// position it appropriately. As such, we no longer needed
// the loaded bitmap. Yay!
// hw-accelerated renderer retains bitmap for faster rotation
- mBackground = null;
- mWallpaperManager.forgetLoadedWallpaper();
+ unloadWallpaper(false /* forgetSize */);
}
}
}
@@ -438,25 +431,39 @@
*
* If loading is already in-flight, subsequent loads are ignored (but needDraw is or-ed to
* the active request).
+ *
+ * If {@param needsReset} is set also clears the cache in WallpaperManager first.
*/
- private void loadWallpaper(boolean needsDraw) {
+ private void loadWallpaper(boolean needsDraw, boolean needsReset) {
mNeedsDrawAfterLoadingWallpaper |= needsDraw;
if (mLoader != null) {
- if (DEBUG) {
- Log.d(TAG, "Skipping loadWallpaper, already in flight ");
+ if (needsReset) {
+ mLoader.cancel(false /* interrupt */);
+ mLoader = null;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Skipping loadWallpaper, already in flight ");
+ }
+ return;
}
- return;
}
mLoader = new AsyncTask<Void, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(Void... params) {
Throwable exception;
try {
+ if (needsReset) {
+ mWallpaperManager.forgetLoadedWallpaper();
+ }
return mWallpaperManager.getBitmap();
} catch (RuntimeException | OutOfMemoryError e) {
exception = e;
}
+ if (isCancelled()) {
+ return null;
+ }
+
if (exception != null) {
// Note that if we do fail at this, and the default wallpaper can't
// be loaded, we will go into a cycle. Don't do a build where the
@@ -469,6 +476,10 @@
Log.w(TAG, "Unable reset to default wallpaper!", ex);
}
+ if (isCancelled()) {
+ return null;
+ }
+
try {
return mWallpaperManager.getBitmap();
} catch (RuntimeException | OutOfMemoryError e) {
@@ -505,6 +516,26 @@
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
+ private void unloadWallpaper(boolean forgetSize) {
+ if (mLoader != null) {
+ mLoader.cancel(false);
+ mLoader = null;
+ }
+ mBackground = null;
+ if (forgetSize) {
+ mBackgroundWidth = -1;
+ mBackgroundHeight = -1;
+ }
+
+ mLoader = new AsyncTask<Void, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(Void... params) {
+ mWallpaperManager.forgetLoadedWallpaper();
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
@Override
protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
super.dump(prefix, fd, out, args);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index bfc8642..99e7876 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -42,7 +42,7 @@
import com.android.systemui.statusbar.SystemBars;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tv.pip.PipUI;
+import com.android.systemui.pip.PipUI;
import com.android.systemui.usb.StorageNotification;
import com.android.systemui.volume.VolumeUI;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 56f6b8d..6c35243 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -43,6 +43,7 @@
import android.util.Log;
import android.view.Display;
+import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.SystemUIApplication;
@@ -85,6 +86,8 @@
private boolean mCarMode;
private long mNotificationPulseTime;
+ private AmbientDisplayConfiguration mConfig;
+
public DozeService() {
if (DEBUG) Log.d(mTag, "new DozeService()");
setDebug(DEBUG);
@@ -125,6 +128,7 @@
setWindowless(true);
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+ mConfig = new AmbientDisplayConfiguration(mContext);
mSensors = new TriggerSensor[] {
new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
@@ -135,12 +139,12 @@
mPickupSensor = new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
Settings.Secure.DOZE_PULSE_ON_PICK_UP,
- mDozeParameters.getPulseOnPickup(), mDozeParameters.getVibrateOnPickup(),
+ mConfig.pulseOnPickupAvailable(), mDozeParameters.getVibrateOnPickup(),
DozeLog.PULSE_REASON_SENSOR_PICKUP),
new TriggerSensor(
- findSensorWithType(mDozeParameters.getDoubleTapSensorType()),
- Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
- mDozeParameters.getPulseOnPickup(), mDozeParameters.getVibrateOnPickup(),
+ findSensorWithType(mConfig.doubleTapSensorType()),
+ Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, true,
+ mDozeParameters.getVibrateOnPickup(),
DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP)
};
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -359,7 +363,7 @@
private void requestNotificationPulse() {
if (DEBUG) Log.d(mTag, "requestNotificationPulse");
- if (!mDozeParameters.getPulseOnNotifications()) return;
+ if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) return;
mNotificationPulseTime = SystemClock.elapsedRealtime();
requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
new file mode 100644
index 0000000..617d8ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip;
+
+import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+
+import com.android.systemui.SystemUI;
+
+/**
+ * Controls the picture-in-picture window.
+ */
+public class PipUI extends SystemUI {
+
+ private boolean mSupportsPip;
+ private boolean mIsLeanBackOnly;
+
+ @Override
+ public void start() {
+ PackageManager pm = mContext.getPackageManager();
+ mSupportsPip = pm.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
+ mIsLeanBackOnly = pm.hasSystemFeature(FEATURE_LEANBACK_ONLY);
+ if (!mSupportsPip) {
+ return;
+ }
+ if (mIsLeanBackOnly) {
+ com.android.systemui.pip.tv.PipManager.getInstance().initialize(mContext);
+ } else {
+ com.android.systemui.pip.phone.PipManager.getInstance().initialize(mContext);
+ }
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (!mSupportsPip) {
+ return;
+ }
+ if (mIsLeanBackOnly) {
+ com.android.systemui.pip.tv.PipManager.getInstance().onConfigurationChanged();
+ } else {
+ com.android.systemui.pip.phone.PipManager.getInstance().onConfigurationChanged();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
new file mode 100644
index 0000000..a7ac719
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip.phone;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnLayoutChangeListener;
+import android.view.WindowManager;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+
+public class PipDismissViewController {
+
+ // This delay controls how long to wait before we show the target when the user first moves
+ // the PIP, to prevent the target from animating if the user just wants to fling the PIP
+ private static final int SHOW_TARGET_DELAY = 100;
+ private static final int SHOW_TARGET_DURATION = 200;
+
+ private Context mContext;
+ private WindowManager mWindowManager;
+
+ private View mDismissView;
+ private Rect mDismissTargetScreenBounds = new Rect();
+
+ public PipDismissViewController(Context context) {
+ mContext = context;
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ }
+
+ /**
+ * Creates the dismiss target for showing via {@link #showDismissTarget()}.
+ */
+ public void createDismissTarget() {
+ if (mDismissView == null) {
+ // Create a new view for the dismiss target
+ int dismissTargetSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.pip_dismiss_target_size);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ mDismissView = inflater.inflate(R.layout.pip_dismiss_view, null);
+ mDismissView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (mDismissView != null) {
+ mDismissView.getBoundsOnScreen(mDismissTargetScreenBounds);
+ }
+ }
+ });
+
+ // Add the target to the window
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ dismissTargetSize,
+ dismissTargetSize,
+ WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ lp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ mWindowManager.addView(mDismissView, lp);
+ }
+ mDismissView.animate().cancel();
+ }
+
+ /**
+ * Shows the dismiss target.
+ */
+ public void showDismissTarget() {
+ mDismissView.animate()
+ .alpha(1f)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+ .setStartDelay(SHOW_TARGET_DELAY)
+ .setDuration(SHOW_TARGET_DURATION)
+ .start();
+ }
+
+ /**
+ * Hides and destroys the dismiss target.
+ */
+ public void destroyDismissTarget() {
+ if (mDismissView != null) {
+ mDismissView.animate()
+ .alpha(0f)
+ .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+ .setStartDelay(0)
+ .setDuration(SHOW_TARGET_DURATION)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mWindowManager.removeView(mDismissView);
+ mDismissView = null;
+ }
+ })
+ .start();
+ }
+ }
+
+ /**
+ * @return the dismiss target screen bounds.
+ */
+ public Rect getDismissBounds() {
+ return mDismissTargetScreenBounds;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
new file mode 100644
index 0000000..f9a4f7c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip.phone;
+
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+/**
+ * Manages the picture-in-picture (PIP) UI and states for Phones.
+ */
+public class PipManager {
+ private static final String TAG = "PipManager";
+
+ private static PipManager sPipController;
+
+ private Context mContext;
+ private IActivityManager mActivityManager;
+ private IWindowManager mWindowManager;
+
+ private PipTouchHandler mTouchHandler;
+
+ private PipManager() {}
+
+ /**
+ * Initializes {@link PipManager}.
+ */
+ public void initialize(Context context) {
+ mContext = context;
+ mActivityManager = ActivityManagerNative.getDefault();
+ mWindowManager = WindowManagerGlobal.getWindowManagerService();
+
+ mTouchHandler = new PipTouchHandler(context, mActivityManager, mWindowManager);
+ }
+
+ /**
+ * Updates the PIP per configuration changed.
+ */
+ public void onConfigurationChanged() {
+ mTouchHandler.onConfigurationChanged();
+ }
+
+ /**
+ * Gets an instance of {@link PipManager}.
+ */
+ public static PipManager getInstance() {
+ if (sPipController == null) {
+ sPipController = new PipManager();
+ }
+ return sPipController;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
new file mode 100644
index 0000000..ffe99c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip.phone;
+
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+
+import static com.android.systemui.Interpolators.FAST_OUT_LINEAR_IN;
+import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.systemui.recents.misc.Utilities.RECT_EVALUATOR;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager.StackInfo;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IWindowManager;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+import android.view.animation.Interpolator;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.policy.PipSnapAlgorithm;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.tuner.TunerService;
+
+/**
+ * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
+ * the PIP.
+ */
+public class PipTouchHandler implements TunerService.Tunable {
+ private static final String TAG = "PipTouchHandler";
+ private static final boolean DEBUG_ALLOW_OUT_OF_BOUNDS_STACK = false;
+
+ private static final String TUNER_KEY_SWIPE_TO_DISMISS = "pip_swipe_to_dismiss";
+ private static final String TUNER_KEY_DRAG_TO_DISMISS = "pip_drag_to_dismiss";
+
+ private static final int SNAP_STACK_DURATION = 225;
+ private static final int DISMISS_STACK_DURATION = 375;
+ private static final int EXPAND_STACK_DURATION = 225;
+
+ private final Context mContext;
+ private final IActivityManager mActivityManager;
+ private final ViewConfiguration mViewConfig;
+ private final InputChannel mInputChannel = new InputChannel();
+
+ private final PipInputEventReceiver mInputEventReceiver;
+ private PipDismissViewController mDismissViewController;
+ private PipSnapAlgorithm mSnapAlgorithm;
+
+ private boolean mEnableSwipeToDismiss = true;
+ private boolean mEnableDragToDismiss = true;
+
+ private final Rect mPinnedStackBounds = new Rect();
+ private final Rect mBoundedPinnedStackBounds = new Rect();
+ private ValueAnimator mPinnedStackBoundsAnimator = null;
+
+ private final PointF mDownTouch = new PointF();
+ private final PointF mLastTouch = new PointF();
+ private boolean mIsDragging;
+ private boolean mIsSwipingToDismiss;
+ private int mActivePointerId;
+
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private VelocityTracker mVelocityTracker;
+
+ /**
+ * Input handler used for Pip windows.
+ */
+ private final class PipInputEventReceiver extends InputEventReceiver {
+ public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ boolean handled = true;
+ try {
+ // To be implemented for input handling over Pip windows
+ if (event instanceof MotionEvent) {
+ MotionEvent ev = (MotionEvent) event;
+ handleTouchEvent(ev);
+ }
+ } finally {
+ finishInputEvent(event, handled);
+ }
+ }
+ }
+
+ public PipTouchHandler(Context context, IActivityManager activityManager,
+ IWindowManager windowManager) {
+
+ // Initialize the Pip input consumer
+ try {
+ windowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+ windowManager.createInputConsumer(INPUT_CONSUMER_PIP, mInputChannel);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to create PIP input consumer", e);
+ }
+ mContext = context;
+ mActivityManager = activityManager;
+ mViewConfig = ViewConfiguration.get(context);
+ mInputEventReceiver = new PipInputEventReceiver(mInputChannel, Looper.myLooper());
+ if (mEnableDragToDismiss) {
+ mDismissViewController = new PipDismissViewController(context);
+ }
+ mFlingAnimationUtils = new FlingAnimationUtils(context, 2f);
+
+ // Register any tuner settings changes
+ TunerService.get(context).addTunable(this, TUNER_KEY_SWIPE_TO_DISMISS,
+ TUNER_KEY_DRAG_TO_DISMISS);
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ switch (key) {
+ case TUNER_KEY_SWIPE_TO_DISMISS:
+ mEnableSwipeToDismiss = (newValue != null) && Integer.parseInt(newValue) != 0;
+ break;
+ case TUNER_KEY_DRAG_TO_DISMISS:
+ mEnableDragToDismiss = (newValue != null) && Integer.parseInt(newValue) != 0;
+ break;
+ }
+ }
+
+ public void onConfigurationChanged() {
+ updateBoundedPinnedStackBounds();
+ }
+
+ private void handleTouchEvent(MotionEvent ev) {
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ // Cancel any existing animations on the pinned stack
+ if (mPinnedStackBoundsAnimator != null) {
+ mPinnedStackBoundsAnimator.cancel();
+ }
+
+ updateBoundedPinnedStackBounds();
+ initOrResetVelocityTracker();
+ mVelocityTracker.addMovement(ev);
+ mActivePointerId = ev.getPointerId(0);
+ mLastTouch.set(ev.getX(), ev.getY());
+ mDownTouch.set(mLastTouch);
+ mIsDragging = false;
+ if (mEnableDragToDismiss) {
+ // TODO: Consider setting a timer such at after X time, we show the dismiss
+ // target if the user hasn't already dragged some distance
+ mDismissViewController.createDismissTarget();
+ }
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ // Update the velocity tracker
+ mVelocityTracker.addMovement(ev);
+
+ int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+ float x = ev.getX(activePointerIndex);
+ float y = ev.getY(activePointerIndex);
+ float left = mPinnedStackBounds.left + (x - mLastTouch.x);
+ float top = mPinnedStackBounds.top + (y - mLastTouch.y);
+
+ if (!mIsDragging) {
+ // Check if the pointer has moved far enough
+ float movement = PointF.length(mDownTouch.x - x, mDownTouch.y - y);
+ if (movement > mViewConfig.getScaledTouchSlop()) {
+ mIsDragging = true;
+ if (mEnableSwipeToDismiss) {
+ // TODO: this check can have some buffer so that we only start swiping
+ // after a significant move out of bounds
+ mIsSwipingToDismiss = !(mBoundedPinnedStackBounds.left <= left &&
+ left <= mBoundedPinnedStackBounds.right) &&
+ Math.abs(mDownTouch.x - x) > Math.abs(y - mLastTouch.y);
+ }
+ if (mEnableDragToDismiss) {
+ mDismissViewController.showDismissTarget();
+ }
+ }
+ }
+
+ if (mIsSwipingToDismiss) {
+ // Ignore the vertical movement
+ top = mPinnedStackBounds.top;
+ movePinnedStack(left, top);
+ } else if (mIsDragging) {
+ // Move the pinned stack
+ if (!DEBUG_ALLOW_OUT_OF_BOUNDS_STACK) {
+ left = Math.max(mBoundedPinnedStackBounds.left, Math.min(
+ mBoundedPinnedStackBounds.right, left));
+ top = Math.max(mBoundedPinnedStackBounds.top, Math.min(
+ mBoundedPinnedStackBounds.bottom, top));
+ }
+ movePinnedStack(left, top);
+ }
+ mLastTouch.set(ev.getX(), ev.getY());
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP: {
+ // Update the velocity tracker
+ mVelocityTracker.addMovement(ev);
+
+ int pointerIndex = ev.getActionIndex();
+ int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // Select a new active pointer id and reset the movement state
+ final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ mLastTouch.set(ev.getX(newPointerIndex), ev.getY(newPointerIndex));
+ }
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ // Update the velocity tracker
+ mVelocityTracker.addMovement(ev);
+ mVelocityTracker.computeCurrentVelocity(1000,
+ ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
+ float velocityX = mVelocityTracker.getXVelocity();
+ float velocityY = mVelocityTracker.getYVelocity();
+ float velocity = PointF.length(velocityX, velocityY);
+
+ if (mIsSwipingToDismiss) {
+ if (Math.abs(velocityX) > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+ flingToDismiss(velocityX);
+ } else {
+ animateToClosestSnapTarget();
+ }
+ } else if (mIsDragging) {
+ if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+ flingToSnapTarget(velocity, velocityX, velocityY);
+ } else {
+ int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+ int x = (int) ev.getX(activePointerIndex);
+ int y = (int) ev.getY(activePointerIndex);
+ Rect dismissBounds = mEnableDragToDismiss
+ ? mDismissViewController.getDismissBounds()
+ : null;
+ if (dismissBounds != null && dismissBounds.contains(x, y)) {
+ animateDismissPinnedStack(dismissBounds);
+ } else {
+ animateToClosestSnapTarget();
+ }
+ }
+ } else {
+ expandPinnedStackToFullscreen();
+ }
+ if (mEnableDragToDismiss) {
+ mDismissViewController.destroyDismissTarget();
+ }
+
+ // Fall through to clean up
+ }
+ case MotionEvent.ACTION_CANCEL: {
+ mIsDragging = false;
+ mIsSwipingToDismiss = false;
+ recycleVelocityTracker();
+ break;
+ }
+ }
+ }
+
+ private void initOrResetVelocityTracker() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ } else {
+ mVelocityTracker.clear();
+ }
+ }
+
+ private void recycleVelocityTracker() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+
+ /**
+ * Flings the PIP to the closest snap target.
+ */
+ private void flingToSnapTarget(float velocity, float velocityX, float velocityY) {
+ Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
+ mPinnedStackBounds, velocityX, velocityY);
+ if (!mPinnedStackBounds.equals(toBounds)) {
+ mPinnedStackBoundsAnimator = createResizePinnedStackAnimation(
+ toBounds, 0, FAST_OUT_SLOW_IN);
+ mFlingAnimationUtils.apply(mPinnedStackBoundsAnimator, 0,
+ distanceBetweenRectOffsets(mPinnedStackBounds, toBounds),
+ velocity);
+ mPinnedStackBoundsAnimator.start();
+ }
+ }
+
+ /**
+ * Animates the PIP to the closest snap target.
+ */
+ private void animateToClosestSnapTarget() {
+ Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
+ mPinnedStackBounds);
+ if (!mPinnedStackBounds.equals(toBounds)) {
+ mPinnedStackBoundsAnimator = createResizePinnedStackAnimation(
+ toBounds, SNAP_STACK_DURATION, FAST_OUT_SLOW_IN);
+ mPinnedStackBoundsAnimator.start();
+ }
+ }
+
+ /**
+ * Flings the PIP to dismiss it offscreen.
+ */
+ private void flingToDismiss(float velocityX) {
+ float offsetX = velocityX > 0
+ ? mBoundedPinnedStackBounds.right + 2 * mPinnedStackBounds.width()
+ : mBoundedPinnedStackBounds.left - 2 * mPinnedStackBounds.width();
+ Rect toBounds = new Rect(mPinnedStackBounds);
+ toBounds.offsetTo((int) offsetX, toBounds.top);
+ if (!mPinnedStackBounds.equals(toBounds)) {
+ mPinnedStackBoundsAnimator = createResizePinnedStackAnimation(
+ toBounds, 0, FAST_OUT_SLOW_IN);
+ mFlingAnimationUtils.apply(mPinnedStackBoundsAnimator, 0,
+ distanceBetweenRectOffsets(mPinnedStackBounds, toBounds),
+ velocityX);
+ mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ BackgroundThread.getHandler().post(() -> {
+ try {
+ mActivityManager.removeStack(PINNED_STACK_ID);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to remove PIP", e);
+ }
+ });
+ }
+ });
+ mPinnedStackBoundsAnimator.start();
+ }
+ }
+
+ /**
+ * Animates the dismissal of the PIP over the dismiss target bounds.
+ */
+ private void animateDismissPinnedStack(Rect dismissBounds) {
+ Rect toBounds = new Rect(dismissBounds.centerX(),
+ dismissBounds.centerY(),
+ dismissBounds.centerX() + 1,
+ dismissBounds.centerY() + 1);
+ mPinnedStackBoundsAnimator = createResizePinnedStackAnimation(
+ toBounds, DISMISS_STACK_DURATION, FAST_OUT_LINEAR_IN);
+ mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ BackgroundThread.getHandler().post(() -> {
+ try {
+ mActivityManager.removeStack(PINNED_STACK_ID);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to remove PIP", e);
+ }
+ });
+ }
+ });
+ mPinnedStackBoundsAnimator.start();
+ }
+
+ /**
+ * Resizes the pinned stack back to fullscreen.
+ */
+ private void expandPinnedStackToFullscreen() {
+ BackgroundThread.getHandler().post(() -> {
+ try {
+ mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
+ true /* allowResizeInDockedMode */, true /* preserveWindows */,
+ true /* animate */, EXPAND_STACK_DURATION);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error showing PIP menu activity", e);
+ }
+ });
+ }
+
+ /**
+ * Updates the movement bounds of the pinned stack.
+ */
+ private void updateBoundedPinnedStackBounds() {
+ try {
+ StackInfo info = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ if (info != null) {
+ mPinnedStackBounds.set(info.bounds);
+ mBoundedPinnedStackBounds.set(mActivityManager.getPictureInPictureMovementBounds(
+ info.displayId));
+ mSnapAlgorithm = new PipSnapAlgorithm(mContext, info.displayId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not fetch PIP movement bounds.", e);
+ }
+ }
+
+ /**
+ * Moves the pinned stack to the given {@param left} and {@param top} offsets.
+ */
+ private void movePinnedStack(float left, float top) {
+ if ((int) left != mPinnedStackBounds.left || (int) top != mPinnedStackBounds.top) {
+ mPinnedStackBounds.offsetTo((int) left, (int) top);
+ BackgroundThread.getHandler().post(() -> {
+ try {
+ mActivityManager.resizePinnedStack(mPinnedStackBounds,
+ null /* tempPinnedBounds */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not move pinned stack to offset: (" + left + ", " + top + ")",
+ e);
+ }
+ });
+ }
+ }
+
+ /**
+ * Resizes the pinned stack to the given {@param bounds}.
+ */
+ private void resizePinnedStack(Rect bounds) {
+ if (!mPinnedStackBounds.equals(bounds)) {
+ mPinnedStackBounds.set(bounds);
+ BackgroundThread.getHandler().post(() -> {
+ try {
+ mActivityManager.resizePinnedStack(bounds, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not resize pinned stack to bounds: (" + bounds + ")");
+ }
+ });
+ }
+ }
+
+ /**
+ * Creates a resize-stack animation.
+ */
+ private ValueAnimator createResizePinnedStackAnimation(Rect toBounds, int duration,
+ Interpolator interpolator) {
+ ValueAnimator anim = ValueAnimator.ofObject(RECT_EVALUATOR,
+ mPinnedStackBounds, toBounds);
+ anim.setDuration(duration);
+ anim.setInterpolator(interpolator);
+ anim.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ resizePinnedStack((Rect) animation.getAnimatedValue());
+ }
+ });
+ return anim;
+ }
+
+ /**
+ * @return the distance between points {@param p1} and {@param p2}.
+ */
+ private float distanceBetweenRectOffsets(Rect r1, Rect r2) {
+ return PointF.length(r1.left - r2.left, r1.top - r2.top);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java
rename to packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java
index 80c593c..59cb086 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.tv.pip;
+package com.android.systemui.pip.tv;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -22,7 +22,6 @@
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
-import android.view.View.OnFocusChangeListener;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
rename to packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
index 71740ce..a2aff2d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.tv.pip;
+package com.android.systemui.pip.tv;
import android.content.Context;
import android.media.session.MediaController;
@@ -22,9 +22,6 @@
import android.view.View;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.View.OnFocusChangeListener;
-import android.widget.ImageView;
-import android.widget.TextView;
import android.widget.LinearLayout;
import android.util.AttributeSet;
@@ -33,10 +30,6 @@
import static android.media.session.PlaybackState.ACTION_PAUSE;
import static android.media.session.PlaybackState.ACTION_PLAY;
-import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PLAYING;
-import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PAUSED;
-import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE;
-
/**
* A view containing PIP controls including fullscreen, close, and media controls.
@@ -145,9 +138,9 @@
}
long actions = mMediaController.getPlaybackState().getActions();
int state = mMediaController.getPlaybackState().getState();
- if (mPipManager.getPlaybackState() == PLAYBACK_STATE_PAUSED) {
+ if (mPipManager.getPlaybackState() == PipManager.PLAYBACK_STATE_PAUSED) {
mMediaController.getTransportControls().play();
- } else if (mPipManager.getPlaybackState() == PLAYBACK_STATE_PLAYING) {
+ } else if (mPipManager.getPlaybackState() == PipManager.PLAYBACK_STATE_PLAYING) {
mMediaController.getTransportControls().pause();
}
// View will be updated later in {@link mMediaControllerCallback}
@@ -188,11 +181,11 @@
private void updatePlayPauseView() {
int state = mPipManager.getPlaybackState();
- if (state == PLAYBACK_STATE_UNAVAILABLE) {
+ if (state == PipManager.PLAYBACK_STATE_UNAVAILABLE) {
mPlayPauseButtonView.setVisibility(View.GONE);
} else {
mPlayPauseButtonView.setVisibility(View.VISIBLE);
- if (state == PLAYBACK_STATE_PLAYING) {
+ if (state == PipManager.PLAYBACK_STATE_PLAYING) {
mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white_24dp);
mPlayPauseButtonView.setText(R.string.pip_pause);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
rename to packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 75c2bcd..894bc53 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.tv.pip;
+package com.android.systemui.pip.tv;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
@@ -25,7 +25,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Rect;
import android.media.session.MediaController;
@@ -33,17 +32,13 @@
import android.media.session.PlaybackState;
import android.os.Debug;
import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
+import android.view.Display;
import android.view.IWindowManager;
-import android.view.InputChannel;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.WindowManagerGlobal;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -53,7 +48,6 @@
import java.util.List;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static com.android.systemui.Prefs.Key.TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN;
/**
@@ -67,8 +61,6 @@
private static PipManager sPipManager;
- private static final int MAX_RUNNING_TASKS_COUNT = 10;
-
/**
* List of package and class name which are considered as Settings,
* so PIP location should be adjusted to the left of the side panel.
@@ -163,9 +155,6 @@
private boolean mOnboardingShown;
private String[] mLastPackagesResourceGranted;
- private InputChannel mInputChannel;
- private PipInputEventReceiver mInputEventReceiver;
-
private final Runnable mResizePinnedStackRunnable = new Runnable() {
@Override
public void run() {
@@ -203,25 +192,6 @@
}
};
- /**
- * Input handler used for Pip windows. Currently eats all the input events.
- */
- private final class PipInputEventReceiver extends InputEventReceiver {
- public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper);
- }
-
- @Override
- public void onInputEvent(InputEvent event) {
- boolean handled = true;
- try {
- // To be implemented for input handling over Pip windows
- } finally {
- finishInputEvent(event, handled);
- }
- }
- }
-
private PipManager() { }
/**
@@ -246,26 +216,16 @@
mPipRecentsOverlayManager = new PipRecentsOverlayManager(context);
mMediaSessionManager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
-
- PackageManager pm = mContext.getPackageManager();
- if (!pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) {
- // Initialize the Pip input consumer
- mInputChannel = new InputChannel();
- try {
- IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
- wm.destroyInputConsumer(INPUT_CONSUMER_PIP);
- wm.createInputConsumer(INPUT_CONSUMER_PIP, mInputChannel);
- mInputEventReceiver = new PipInputEventReceiver(mInputChannel, Looper.myLooper());
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to create Pip input consumer", e);
- }
- }
}
private void loadConfigurationsAndApply() {
Resources res = mContext.getResources();
- mDefaultPipBounds = Rect.unflattenFromString(res.getString(
- com.android.internal.R.string.config_defaultPictureInPictureBounds));
+ try {
+ mDefaultPipBounds = mActivityManager.getDefaultPictureInPictureBounds(
+ Display.DEFAULT_DISPLAY);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get default PIP bounds", e);
+ }
mSettingsPipBounds = Rect.unflattenFromString(res.getString(
R.string.pip_settings_bounds));
mMenuModePipBounds = Rect.unflattenFromString(res.getString(
@@ -287,7 +247,7 @@
/**
* Updates the PIP per configuration changed.
*/
- void onConfigurationChanged() {
+ public void onConfigurationChanged() {
loadConfigurationsAndApply();
mPipRecentsOverlayManager.onConfigurationChanged(mContext);
}
@@ -347,11 +307,7 @@
*/
private void showPipOverlay() {
if (DEBUG) Log.d(TAG, "showPipOverlay()");
- // Temporary workaround to prevent the overlay on phones
- PackageManager pm = mContext.getPackageManager();
- if (pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) {
- PipOverlayActivity.showPipOverlay(mContext);
- }
+ PipOverlayActivity.showPipOverlay(mContext);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
rename to packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index 542a935..01d86b6 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.tv.pip;
+package com.android.systemui.pip.tv;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -23,7 +23,6 @@
import android.view.View;
import com.android.systemui.R;
-import com.android.systemui.Interpolators;
/**
* Activity to show the PIP menu to control PIP.
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipOnboardingActivity.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
rename to packages/SystemUI/src/com/android/systemui/pip/tv/PipOnboardingActivity.java
index 9a87cfc..57952f4 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipOnboardingActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.tv.pip;
+package com.android.systemui.pip.tv;
import android.animation.Animator;
import android.animation.AnimatorInflater;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipOverlayActivity.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
rename to packages/SystemUI/src/com/android/systemui/pip/tv/PipOverlayActivity.java
index 011e159..f52121f 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipOverlayActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.tv.pip;
+package com.android.systemui.pip.tv;
import android.animation.Animator;
import android.animation.AnimatorInflater;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsControlsView.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
rename to packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsControlsView.java
index ffe96af..a891d12 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsControlsView.java
@@ -14,26 +14,20 @@
* limitations under the License.
*/
-package com.android.systemui.tv.pip;
+package com.android.systemui.pip.tv;
import android.animation.Animator;
import android.animation.AnimatorInflater;
-import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
-import android.view.View.OnFocusChangeListener;
import android.widget.FrameLayout;
import com.android.systemui.R;
-import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PLAYING;
-import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PAUSED;
-import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE;
-
/**
* An FrameLayout that contains {@link PipControlsView} with its scrim.
*/
@@ -49,7 +43,6 @@
}
private final PipManager mPipManager = PipManager.getInstance();
- private Listener mListener;
private PipControlsView mPipControlsView;
private View mScrim;
private Animator mFocusGainAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsOverlayManager.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
rename to packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsOverlayManager.java
index 895b8a2..835bcbc 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipRecentsOverlayManager.java
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.tv.pip;
+package com.android.systemui.pip.tv;
import android.content.Context;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -32,9 +31,6 @@
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.Gravity.TOP;
import static android.view.View.MeasureSpec.UNSPECIFIED;
-import static com.android.systemui.tv.pip.PipManager.STATE_PIP_OVERLAY;
-import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS;
-import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS_FOCUSED;
public class PipRecentsOverlayManager {
private static final String TAG = "PipRecentsOverlayManager";
@@ -158,7 +154,7 @@
mIsPipFocusedInRecent = true;
mPipControlsView.startFocusGainAnimation();
mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
- mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
+ mPipManager.resizePinnedStack(PipManager.STATE_PIP_RECENTS_FOCUSED);
if (mTalkBackEnabled) {
mPipControlsView.requestFocus();
mPipControlsView.sendAccessibilityEvent(
@@ -177,7 +173,7 @@
mIsPipFocusedInRecent = false;
mPipControlsView.startFocusLossAnimation();
mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewLayoutParams);
- mPipManager.resizePinnedStack(STATE_PIP_RECENTS);
+ mPipManager.resizePinnedStack(PipManager.STATE_PIP_RECENTS);
if (mCallback != null) {
mCallback.onRecentsFocused();
}
@@ -198,7 +194,7 @@
}
mIsRecentsShown = true;
mIsPipFocusedInRecent = true;
- mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
+ mPipManager.resizePinnedStack(PipManager.STATE_PIP_RECENTS_FOCUSED);
// Overlay view will be added after the resize animation ends, if any.
}
@@ -212,7 +208,7 @@
removePipRecentsOverlayView();
if (mPipManager.isPipShown()) {
- mPipManager.resizePinnedStack(STATE_PIP_OVERLAY);
+ mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 4ac629d..ccb28e9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -188,7 +188,8 @@
profileOwner, profileVpn, primaryVpn);
} else {
if (isBranded) {
- return mContext.getString(R.string.branded_monitoring_description_app_personal);
+ return mContext.getString(R.string.branded_monitoring_description_app_personal,
+ primaryVpn);
} else {
return mContext.getString(R.string.monitoring_description_app_personal,
primaryVpn);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index e1db8c6..c699e27 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -219,7 +219,7 @@
}
public void onCollapse() {
- if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
+ if (mCustomizePanel != null && mCustomizePanel.isShown()) {
mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
}
}
@@ -404,7 +404,7 @@
}
public void closeDetail() {
- if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
+ if (mCustomizePanel != null && mCustomizePanel.isShown()) {
// Treat this as a detail panel for now, to make things easy.
mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 3493d24..cf2c16d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -163,6 +163,10 @@
}
}
+ public boolean isShown() {
+ return isShown;
+ }
+
private void setCustomizing(boolean customizing) {
mCustomizing = customizing;
mQsContainer.notifyCustomizeChanged();
@@ -217,7 +221,9 @@
private final AnimatorListener mExpandAnimationListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- setCustomizing(true);
+ if (isShown) {
+ setCustomizing(true);
+ }
mNotifQsContainer.setCustomizerAnimating(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index e0cdb1a..6a1b477 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -79,6 +79,8 @@
import com.android.internal.app.AssistUtils;
import com.android.internal.os.BackgroundThread;
import com.android.systemui.R;
+import com.android.systemui.pip.tv.PipMenuActivity;
+import com.android.systemui.pip.tv.PipOnboardingActivity;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.RecentsImpl;
import com.android.systemui.recents.model.Task;
@@ -109,8 +111,8 @@
final static List<String> sRecentsBlacklist;
static {
sRecentsBlacklist = new ArrayList<>();
- sRecentsBlacklist.add("com.android.systemui.tv.pip.PipOnboardingActivity");
- sRecentsBlacklist.add("com.android.systemui.tv.pip.PipMenuActivity");
+ sRecentsBlacklist.add(PipOnboardingActivity.class.getName());
+ sRecentsBlacklist.add(PipMenuActivity.class.getName());
}
private static SystemServicesProxy sSystemServicesProxy;
@@ -1082,7 +1084,8 @@
if (mWm == null) return;
try {
- WindowManagerGlobal.getWindowManagerService().getStableInsets(outStableInsets);
+ WindowManagerGlobal.getWindowManagerService().getStableInsets(Display.DEFAULT_DISPLAY,
+ outStableInsets);
} catch (Exception e) {
e.printStackTrace();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index ecb12d3..a2a8199 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -58,8 +58,8 @@
import com.android.systemui.recents.tv.views.TaskStackHorizontalGridView;
import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.tv.pip.PipManager;
-import com.android.systemui.tv.pip.PipRecentsOverlayManager;
+import com.android.systemui.pip.tv.PipManager;
+import com.android.systemui.pip.tv.PipRecentsOverlayManager;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
index ef9de53..ac9a217 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
@@ -25,7 +25,6 @@
import android.os.SystemClock;
import android.os.UserHandle;
-import com.android.systemui.SystemUIApplication;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
@@ -37,8 +36,7 @@
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.model.ThumbnailData;
import com.android.systemui.recents.tv.views.TaskCardView;
-import com.android.systemui.statusbar.tv.TvStatusBar;
-import com.android.systemui.tv.pip.PipManager;
+import com.android.systemui.pip.tv.PipManager;
public class RecentsTvImpl extends RecentsImpl{
public final static String RECENTS_TV_ACTIVITY =
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 24ef433..febeacb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -95,7 +95,6 @@
private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134;
private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100;
- private TaskStack mStack;
private TaskStackView mTaskStackView;
private TextView mStackActionButton;
private TextView mEmptyView;
@@ -195,7 +194,6 @@
* Called from RecentsActivity when the task stack is updated.
*/
public void updateStack(TaskStack stack, boolean setStackViewTasks) {
- mStack = stack;
if (setStackViewTasks) {
mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
}
@@ -212,7 +210,7 @@
* Returns the current TaskStack.
*/
public TaskStack getStack() {
- return mStack;
+ return mTaskStackView.getStack();
}
/*
@@ -251,8 +249,7 @@
/** Launches the task that recents was launched from if possible */
public boolean launchPreviousTask() {
if (mTaskStackView != null) {
- TaskStack stack = mTaskStackView.getStack();
- Task task = stack.getLaunchTarget();
+ Task task = getStack().getLaunchTarget();
if (task != null) {
TaskView taskView = mTaskStackView.getChildViewForTask(task);
EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
@@ -437,8 +434,9 @@
public final void onBusEvent(LaunchTaskEvent event) {
mLastTaskLaunchedWasFreeform = event.task.isFreeformTask();
- mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackView, event.taskView,
- event.screenPinningRequested, event.targetTaskBounds, event.targetTaskStack);
+ mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView,
+ event.taskView, event.screenPinningRequested, event.targetTaskBounds,
+ event.targetTaskStack);
}
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
@@ -514,8 +512,7 @@
EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
// Remove the task and don't bother relaying out, as all the tasks will be
// relaid out when the stack changes on the multiwindow change event
- mTaskStackView.getStack().removeTask(event.task, null,
- true /* fromDockGesture */);
+ getStack().removeTask(event.task, null, true /* fromDockGesture */);
}
};
@@ -536,7 +533,7 @@
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
event.task.getTopComponent().flattenToShortString());
} else {
- EventBus.getDefault().send(new DragEndCancelledEvent(mStack, event.task,
+ EventBus.getDefault().send(new DragEndCancelledEvent(getStack(), event.task,
event.taskView));
}
} else {
@@ -598,7 +595,7 @@
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp
- && mStack.getTaskCount() > 0) {
+ && getStack().getTaskCount() > 0) {
animateBackgroundScrim(1f,
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
}
@@ -797,8 +794,8 @@
writer.print(" [0x"); writer.print(id); writer.print("]");
writer.println();
- if (mStack != null) {
- mStack.dump(innerPrefix, writer);
+ if (getStack() != null) {
+ getStack().dump(innerPrefix, writer);
}
if (mTaskStackView != null) {
mTaskStackView.dump(innerPrefix, writer);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 265f319..d3ec984 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -31,6 +31,7 @@
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
+import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
@@ -139,7 +140,10 @@
/** Handles touch events once we have intercepted them */
public boolean onTouchEvent(MotionEvent ev) {
handleTouchEvent(ev);
- return mDragRequested;
+ if (ev.getAction() == MotionEvent.ACTION_UP && mRv.getStack().getStackTaskCount() == 0) {
+ EventBus.getDefault().send(new HideRecentsEvent(false, true));
+ }
+ return true;
}
/**** Events ****/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index f7d6183..936354e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -682,13 +682,13 @@
for (int i = 0; i < taskViewCount; i++) {
TaskView tv = taskViews.get(i);
Task task = tv.getTask();
- int taskIndex = mStack.indexOfStackTask(task);
- TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
if (mIgnoreTasks.contains(task.key)) {
continue;
}
+ int taskIndex = mStack.indexOfStackTask(task);
+ TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
if (animationOverrides != null && animationOverrides.containsKey(task)) {
animation = animationOverrides.get(task);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index a7331d8..8a5a8a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -248,6 +248,11 @@
}
}
+ @Override
+ public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
+ // Do nothing, we don't want to display media art in the lock screen for a car.
+ }
+
private int startActivityWithOptions(Intent intent, Bundle options) {
int result = ActivityManager.START_CANCELED;
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index d5bf499..9e9bdd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -18,11 +18,13 @@
import android.content.Context;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.MathUtils;
import android.util.SparseBooleanArray;
+import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.R;
import java.io.PrintWriter;
@@ -31,9 +33,6 @@
import java.util.regex.Pattern;
public class DozeParameters {
- private static final String TAG = "DozeParameters";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
private static final int MAX_DURATION = 60 * 1000;
private final Context mContext;
@@ -55,10 +54,8 @@
pw.print(" getPulseOutDuration(): "); pw.println(getPulseOutDuration());
pw.print(" getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion());
pw.print(" getVibrateOnSigMotion(): "); pw.println(getVibrateOnSigMotion());
- pw.print(" getPulseOnPickup(): "); pw.println(getPulseOnPickup());
pw.print(" getVibrateOnPickup(): "); pw.println(getVibrateOnPickup());
pw.print(" getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse());
- pw.print(" getPulseOnNotifications(): "); pw.println(getPulseOnNotifications());
pw.print(" getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
pw.print(" getPickupSubtypePerformsProxCheck(): ");pw.println(
dumpPickupSubtypePerformsProxCheck());
@@ -106,26 +103,14 @@
return SystemProperties.getBoolean("doze.vibrate.sigmotion", false);
}
- public boolean getPulseOnPickup() {
- return getBoolean("doze.pulse.pickup", R.bool.doze_pulse_on_pick_up);
- }
-
public boolean getVibrateOnPickup() {
return SystemProperties.getBoolean("doze.vibrate.pickup", false);
}
- public String getDoubleTapSensorType() {
- return mContext.getString(R.string.doze_double_tap_sensor_type);
- }
-
public boolean getProxCheckBeforePulse() {
return getBoolean("doze.pulse.proxcheck", R.bool.doze_proximity_check_before_pulse);
}
- public boolean getPulseOnNotifications() {
- return getBoolean("doze.pulse.notifications", R.bool.doze_pulse_on_notifications);
- }
-
public int getPickupVibrationThreshold() {
return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 583a63e..0f800bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -31,6 +31,7 @@
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
+import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.tuner.TunerService;
@@ -42,7 +43,7 @@
* Class to detect gestures on the navigation bar.
*/
public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureListener
- implements TunerService.Tunable {
+ implements TunerService.Tunable, GestureHelper {
private static final String KEY_DOCK_WINDOW_GESTURE = "overview_nav_bar_gesture";
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 3a0eb94..798d9df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -44,16 +44,20 @@
import android.view.WindowManagerGlobal;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
+
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.statusbar.phone.NavGesture;
+import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.policy.DeadZone;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-public class NavigationBarView extends FrameLayout {
+public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> {
final static boolean DEBUG = false;
final static String TAG = "StatusBar/NavBarView";
@@ -83,7 +87,7 @@
private Drawable mImeIcon;
private Drawable mMenuIcon;
- private NavigationBarGestureHelper mGestureHelper;
+ private GestureHelper mGestureHelper;
private DeadZone mDeadZone;
private final NavigationBarTransitions mBarTransitions;
@@ -105,6 +109,8 @@
private Configuration mConfiguration;
private NavigationBarInflaterView mNavigationInflaterView;
+ private RecentsComponent mRecentsComponent;
+ private Divider mDivider;
private class NavTransitionListener implements TransitionListener {
private boolean mBackTransitioning;
@@ -212,7 +218,12 @@
}
public void setComponents(RecentsComponent recentsComponent, Divider divider) {
- mGestureHelper.setComponents(recentsComponent, divider, this);
+ mRecentsComponent = recentsComponent;
+ mDivider = divider;
+ if (mGestureHelper instanceof NavigationBarGestureHelper) {
+ ((NavigationBarGestureHelper) mGestureHelper).setComponents(
+ recentsComponent, divider, this);
+ }
}
public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
@@ -700,6 +711,33 @@
}
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ PluginManager.getInstance(getContext()).addPluginListener(NavGesture.ACTION, this,
+ NavGesture.VERSION, false /* Only one */);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ PluginManager.getInstance(getContext()).removePluginListener(this);
+ }
+
+ @Override
+ public void onPluginConnected(NavGesture plugin) {
+ mGestureHelper = plugin.getGestureHelper();
+ updateTaskSwitchHelper();
+ }
+
+ @Override
+ public void onPluginDisconnected(NavGesture plugin) {
+ NavigationBarGestureHelper defaultHelper = new NavigationBarGestureHelper(getContext());
+ defaultHelper.setComponents(mRecentsComponent, mDivider, this);
+ mGestureHelper = defaultHelper;
+ updateTaskSwitchHelper();
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NavigationBarView {");
final Rect r = new Rect();
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 84deabe..28e5f7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -4269,7 +4269,7 @@
}
updateKeyguardState(staying, false /* fromShadeLocked */);
- if (viewToClick != null) {
+ if (viewToClick != null && viewToClick.isAttachedToWindow()) {
viewToClick.callOnClick();
}
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 4b46578..da58d9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1888,24 +1888,34 @@
}
updateBackgroundBounds();
if (!mCurrentBounds.equals(mBackgroundBounds)) {
- if (mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom || areBoundsAnimating()) {
+ boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
+ || areBoundsAnimating();
+ if (!isExpanded()) {
+ abortBackgroundAnimators();
+ animate = false;
+ }
+ if (animate) {
startBackgroundAnimation();
} else {
mCurrentBounds.set(mBackgroundBounds);
applyCurrentBackgroundBounds();
}
} else {
- if (mBottomAnimator != null) {
- mBottomAnimator.cancel();
- }
- if (mTopAnimator != null) {
- mTopAnimator.cancel();
- }
+ abortBackgroundAnimators();
}
mAnimateNextBackgroundBottom = false;
mAnimateNextBackgroundTop = false;
}
+ private void abortBackgroundAnimators() {
+ if (mBottomAnimator != null) {
+ mBottomAnimator.cancel();
+ }
+ if (mTopAnimator != null) {
+ mTopAnimator.cancel();
+ }
+ }
+
private boolean areBoundsAnimating() {
return mBottomAnimator != null || mTopAnimator != null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 3c83921..f5c60a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -27,7 +27,7 @@
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.tv.pip.PipManager;
+import com.android.systemui.pip.tv.PipManager;
/**
* Status bar implementation for "large screen" products that mostly present no on-screen nav
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java
deleted file mode 100644
index 3306cb3..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv.pip;
-
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-
-import com.android.systemui.SystemUI;
-
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-
-/**
- * Controls the picture-in-picture window.
- */
-public class PipUI extends SystemUI {
- private boolean mSupportPip;
-
- @Override
- public void start() {
- PackageManager pm = mContext.getPackageManager();
- mSupportPip = pm.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
- if (!mSupportPip) {
- return;
- }
- PipManager pipManager = PipManager.getInstance();
- pipManager.initialize(mContext);
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (!mSupportPip) {
- return;
- }
- PipManager.getInstance().onConfigurationChanged();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
new file mode 100644
index 0000000..fccb2a2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.keyguard;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static junit.framework.Assert.*;
+import static org.mockito.Mockito.mock;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeyguardMessageAreaTest extends SysuiTestCase {
+ private Context mContext = InstrumentationRegistry.getTargetContext();
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+ private KeyguardMessageArea mMessageArea;
+
+ @Before
+ public void setUp() throws Exception {
+ KeyguardUpdateMonitor monitor = mock(KeyguardUpdateMonitor.class);
+ mHandler.post(()-> mMessageArea = new KeyguardMessageArea(mContext, null, monitor));
+ waitForIdleSync();
+ }
+
+ @Test
+ public void clearFollowedByMessage_keepsMessage() {
+ mHandler.post(()-> {
+ mMessageArea.setMessage("");
+ mMessageArea.setMessage("test");
+ });
+
+ waitForIdleSync();
+
+ CharSequence[] messageText = new CharSequence[1];
+ mHandler.post(()-> {
+ messageText[0] = mMessageArea.getText();
+ });
+
+ waitForIdleSync();
+
+ assertEquals("test", messageText[0]);
+ }
+
+}
diff --git a/services/core/proto/ipconnectivity.proto b/proto/src/ipconnectivity.proto
similarity index 94%
rename from services/core/proto/ipconnectivity.proto
rename to proto/src/ipconnectivity.proto
index e0d7f09..29b318f 100644
--- a/services/core/proto/ipconnectivity.proto
+++ b/proto/src/ipconnectivity.proto
@@ -53,6 +53,7 @@
// The event type code of the probe, represented by constants defined in
// android.net.metrics.IpReachabilityEvent.
+ // NUD_FAILED_ORGANIC and PROVISIONING_LOST_ORGANIC recorded since version 1.
optional int32 event_type = 2;
};
@@ -126,11 +127,12 @@
// Lifetime duration in milliseconds of a DhcpClient state, or transition
// time in milliseconds between specific pairs of DhcpClient's states.
- // Only populated when state_transition is populated.
+ // Only populated since version 1, when state_transition is populated.
optional int32 duration_ms = 4;
}
// Represents the generation of an Android Packet Filter program.
+// Since version 1.
message ApfProgramEvent {
// Lifetime of the program in seconds.
optional int64 lifetime = 1;
@@ -154,6 +156,7 @@
// Represents Router Advertisement listening statistics for an interface with
// Android Packet Filter enabled.
+// Since version 1.
message ApfStatistics {
// The time interval in milliseconds these stastistics cover.
optional int64 duration_ms = 1;
@@ -183,6 +186,7 @@
// Represents the reception of a Router Advertisement packet for an interface
// with Android Packet Filter enabled.
+// Since version 1.
message RaEvent {
// All lifetime values are expressed in seconds. The default value for an
// option lifetime that was not present in the RA option list is -1.
@@ -271,4 +275,11 @@
// The number of events that had to be dropped due to a full buffer.
optional int32 dropped_events = 2;
+
+ // The version number of the metrics events being collected.
+ // nyc-dev: not populated, implicitly 0
+ // nyc-dr1: not populated, implicitly 1 (sailfish and marlin only)
+ // nyc-mr1: not populated, implicitly 1
+ // nyc-mr2: 2
+ optional int32 version = 3;
};
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 64d8a4c..cebde98 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2575,6 +2575,24 @@
// ACTION: Logs extra passed by the dpc while provisioning.
PROVISIONING_EXTRA = 612;
+ // OPEN Settings > Bluetooth > Attempt to connect to device that shows dialog
+ BLUETOOTH_DIALOG_FRAGMENT = 613;
+
+ // ACTION: Logs provisioning started by zero touch.
+ PROVISIONING_ENTRY_POINT_ZERO_TOUCH = 614;
+
+ // ACTION: Logs provisioning started by NFC bump.
+ PROVISIONING_ENTRY_POINT_NFC = 615;
+
+ // ACTION: Logs provisioning started using QR code.
+ PROVISIONING_ENTRY_POINT_QR_CODE = 616;
+
+ // ACTION: Logs provisioning started using adb.
+ PROVISIONING_ENTRY_POINT_ADB = 617;
+
+ // ACTION: Logs provisioning started by trusted source.
+ PROVISIONING_ENTRY_POINT_TRUSTED_SOURCE = 618;
+
// ---- End O Constants, all O constants go above this line ----
// Add new aosp constants above this line.
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
new file mode 100644
index 0000000..9416ac1
--- /dev/null
+++ b/proto/src/wifi.proto
@@ -0,0 +1,443 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package clearcut.connectivity;
+
+option java_package = "com.android.server.wifi";
+option java_outer_classname = "WifiMetricsProto";
+
+// The information about the Wifi events.
+message WifiLog {
+
+ // Session information that gets logged for every Wifi connection.
+ repeated ConnectionEvent connection_event = 1;
+
+ // Number of saved networks in the user profile.
+ optional int32 num_saved_networks = 2;
+
+ // Number of open networks in the saved networks.
+ optional int32 num_open_networks = 3;
+
+ // Number of personal networks.
+ optional int32 num_personal_networks = 4;
+
+ // Number of enterprise networks.
+ optional int32 num_enterprise_networks = 5;
+
+ // Does the user have location setting enabled.
+ optional bool is_location_enabled = 6;
+
+ // Does the user have scanning enabled.
+ optional bool is_scanning_always_enabled = 7;
+
+ // Number of times user toggled wifi using the settings menu.
+ optional int32 num_wifi_toggled_via_settings = 8;
+
+ // Number of times user toggled wifi using the airplane menu.
+ optional int32 num_wifi_toggled_via_airplane = 9;
+
+ // Number of networks added by the user.
+ optional int32 num_networks_added_by_user = 10;
+
+ // Number of networks added by applications.
+ optional int32 num_networks_added_by_apps = 11;
+
+ // Number scans that returned empty results.
+ optional int32 num_empty_scan_results = 12;
+
+ // Number scans that returned at least one result.
+ optional int32 num_non_empty_scan_results = 13;
+
+ // Number of scans that were one time.
+ optional int32 num_oneshot_scans = 14;
+
+ // Number of repeated background scans that were scheduled to the chip.
+ optional int32 num_background_scans = 15;
+
+ // Error codes that a scan can result in.
+ enum ScanReturnCode {
+
+ // Return Code is unknown.
+ SCAN_UNKNOWN = 0;
+
+ // Scan was successful.
+ SCAN_SUCCESS = 1;
+
+ // Scan was successfully started, but was interrupted.
+ SCAN_FAILURE_INTERRUPTED = 2;
+
+ // Scan failed to start because of invalid configuration
+ // (bad channel, etc).
+ SCAN_FAILURE_INVALID_CONFIGURATION = 3;
+
+ // Could not start a scan because wifi is disabled.
+ FAILURE_WIFI_DISABLED = 4;
+
+ }
+
+ // Mapping of error codes to the number of times that scans resulted
+ // in that error.
+ repeated ScanReturnEntry scan_return_entries = 16;
+
+ message ScanReturnEntry {
+
+ // Return code of the scan.
+ optional ScanReturnCode scan_return_code = 1;
+
+ // Number of entries that were found in the scan.
+ optional int32 scan_results_count = 2;
+ }
+
+ // State of the Wifi.
+ enum WifiState {
+
+ // State is unknown.
+ WIFI_UNKNOWN = 0;
+
+ // Wifi is disabled.
+ WIFI_DISABLED = 1;
+
+ // Wifi is enabled.
+ WIFI_DISCONNECTED = 2;
+
+ // Wifi is enabled and associated with an AP.
+ WIFI_ASSOCIATED = 3;
+ }
+
+ // Mapping of system state to the number of times that scans were requested in
+ // that state
+ repeated WifiSystemStateEntry wifi_system_state_entries = 17;
+
+ message WifiSystemStateEntry {
+
+ // Current WiFi state.
+ optional WifiState wifi_state = 1;
+
+ // Count of scans in state.
+ optional int32 wifi_state_count = 2;
+
+ // Is screen on.
+ optional bool is_screen_on = 3;
+ }
+
+ // Mapping of Error/Success codes to the number of background scans that resulted in it
+ repeated ScanReturnEntry background_scan_return_entries = 18;
+
+ // Mapping of system state to the number of times that Background scans were requested in that
+ // state
+ repeated WifiSystemStateEntry background_scan_request_state = 19;
+
+ // Total number of times the Watchdog of Last Resort triggered, resetting the wifi stack
+ optional int32 num_last_resort_watchdog_triggers = 20;
+
+ // Total number of networks over bad association threshold when watchdog triggered
+ optional int32 num_last_resort_watchdog_bad_association_networks_total = 21;
+
+ // Total number of networks over bad authentication threshold when watchdog triggered
+ optional int32 num_last_resort_watchdog_bad_authentication_networks_total = 22;
+
+ // Total number of networks over bad dhcp threshold when watchdog triggered
+ optional int32 num_last_resort_watchdog_bad_dhcp_networks_total = 23;
+
+ // Total number of networks over bad other threshold when watchdog triggered
+ optional int32 num_last_resort_watchdog_bad_other_networks_total = 24;
+
+ // Total count of networks seen when watchdog triggered
+ optional int32 num_last_resort_watchdog_available_networks_total = 25;
+
+ // Total count of triggers with atleast one bad association network
+ optional int32 num_last_resort_watchdog_triggers_with_bad_association = 26;
+
+ // Total count of triggers with atleast one bad authentication network
+ optional int32 num_last_resort_watchdog_triggers_with_bad_authentication = 27;
+
+ // Total count of triggers with atleast one bad dhcp network
+ optional int32 num_last_resort_watchdog_triggers_with_bad_dhcp = 28;
+
+ // Total count of triggers with atleast one bad other network
+ optional int32 num_last_resort_watchdog_triggers_with_bad_other = 29;
+
+ // Count of times connectivity watchdog confirmed pno is working
+ optional int32 num_connectivity_watchdog_pno_good = 30;
+
+ // Count of times connectivity watchdog found pno not working
+ optional int32 num_connectivity_watchdog_pno_bad = 31;
+
+ // Count of times connectivity watchdog confirmed background scan is working
+ optional int32 num_connectivity_watchdog_background_good = 32;
+
+ // Count of times connectivity watchdog found background scan not working
+ optional int32 num_connectivity_watchdog_background_bad = 33;
+
+ // The time duration represented by this wifi log, from start to end of capture
+ optional int32 record_duration_sec = 34;
+
+ // Counts the occurrences of each individual RSSI poll level
+ repeated RssiPollCount rssi_poll_rssi_count = 35;
+
+ // Total number of times WiFi connected immediately after a Last Resort Watchdog trigger,
+ // without new networks becoming available.
+ optional int32 num_last_resort_watchdog_successes = 36;
+
+ // Total number of saved hidden networks
+ optional int32 num_hidden_networks = 37;
+
+ // Total number of saved passpoint / hotspot 2.0 networks
+ optional int32 num_passpoint_networks = 38;
+
+ // Total number of scan results
+ optional int32 num_total_scan_results = 39;
+
+ // Total number of scan results for open networks
+ optional int32 num_open_network_scan_results = 40;
+
+ // Total number of scan results for personal networks
+ optional int32 num_personal_network_scan_results = 41;
+
+ // Total number of scan results for enterprise networks
+ optional int32 num_enterprise_network_scan_results = 42;
+
+ // Total number of scan results for hidden networks
+ optional int32 num_hidden_network_scan_results = 43;
+
+ // Total number of scan results for hotspot 2.0 r1 networks
+ optional int32 num_hotspot2_r1_network_scan_results = 44;
+
+ // Total number of scan results for hotspot 2.0 r2 networks
+ optional int32 num_hotspot2_r2_network_scan_results = 45;
+
+ // Total number of scans handled by framework (oneshot or otherwise)
+ optional int32 num_scans = 46;
+
+ // Counts the occurrences of each alert reason.
+ repeated AlertReasonCount alert_reason_count = 47;
+
+ // Counts the occurrences of each Wifi score
+ repeated WifiScoreCount wifi_score_count = 48;
+
+ // Histogram of Soft AP Durations
+ repeated SoftApDurationBucket soft_ap_duration = 49;
+
+ // Histogram of Soft AP ReturnCode
+ repeated SoftApReturnCodeCount soft_ap_return_code = 50;
+
+ // Histogram of the delta between scan result RSSI and RSSI polls
+ repeated RssiPollCount rssi_poll_delta_count = 51;
+}
+
+// Information that gets logged for every WiFi connection.
+message RouterFingerPrint {
+
+ enum RoamType {
+
+ // Type is unknown.
+ ROAM_TYPE_UNKNOWN = 0;
+
+ // No roaming - usually happens on a single band (2.4 GHz) router.
+ ROAM_TYPE_NONE = 1;
+
+ // Enterprise router.
+ ROAM_TYPE_ENTERPRISE = 2;
+
+ // DBDC => Dual Band Dual Concurrent essentially a router that
+ // supports both 2.4 GHz and 5 GHz bands.
+ ROAM_TYPE_DBDC = 3;
+ }
+
+ enum Auth {
+
+ // Auth is unknown.
+ AUTH_UNKNOWN = 0;
+
+ // No authentication.
+ AUTH_OPEN = 1;
+
+ // If the router uses a personal authentication.
+ AUTH_PERSONAL = 2;
+
+ // If the router is setup for enterprise authentication.
+ AUTH_ENTERPRISE = 3;
+ }
+
+ enum RouterTechnology {
+
+ // Router is unknown.
+ ROUTER_TECH_UNKNOWN = 0;
+
+ // Router Channel A.
+ ROUTER_TECH_A = 1;
+
+ // Router Channel B.
+ ROUTER_TECH_B = 2;
+
+ // Router Channel G.
+ ROUTER_TECH_G = 3;
+
+ // Router Channel N.
+ ROUTER_TECH_N = 4;
+
+ // Router Channel AC.
+ ROUTER_TECH_AC = 5;
+
+ // When the channel is not one of the above.
+ ROUTER_TECH_OTHER = 6;
+ }
+
+ optional RoamType roam_type = 1;
+
+ // Channel on which the connection takes place.
+ optional int32 channel_info = 2;
+
+ // DTIM setting of the router.
+ optional int32 dtim = 3;
+
+ // Authentication scheme of the router.
+ optional Auth authentication = 4;
+
+ // If the router is hidden.
+ optional bool hidden = 5;
+
+ // Channel information.
+ optional RouterTechnology router_technology = 6;
+
+ // whether ipv6 is supported.
+ optional bool supports_ipv6 = 7;
+
+ // If the router is a passpoint / hotspot 2.0 network
+ optional bool passpoint = 8;
+}
+
+message ConnectionEvent {
+
+ // Roam Type.
+ enum RoamType {
+
+ // Type is unknown.
+ ROAM_UNKNOWN = 0;
+
+ // No roaming.
+ ROAM_NONE = 1;
+
+ // DBDC roaming.
+ ROAM_DBDC = 2;
+
+ // Enterprise roaming.
+ ROAM_ENTERPRISE = 3;
+
+ // User selected roaming.
+ ROAM_USER_SELECTED = 4;
+
+ // Unrelated.
+ ROAM_UNRELATED = 5;
+ }
+
+ // Connectivity Level Failure.
+ enum ConnectivityLevelFailure {
+
+ // Failure is unknown.
+ HLF_UNKNOWN = 0;
+
+ // No failure.
+ HLF_NONE = 1;
+
+ // DHCP failure.
+ HLF_DHCP = 2;
+
+ // No internet connection.
+ HLF_NO_INTERNET = 3;
+
+ // No internet connection.
+ HLF_UNWANTED = 4;
+ }
+
+ // Start time of the connection.
+ optional int64 start_time_millis = 1;// [(datapol.semantic_type) = ST_TIMESTAMP];
+
+ // Duration to connect.
+ optional int32 duration_taken_to_connect_millis = 2;
+
+ // Router information.
+ optional RouterFingerPrint router_fingerprint = 3;
+
+ // RSSI at the start of the connection.
+ optional int32 signal_strength = 4;
+
+ // Roam Type.
+ optional RoamType roam_type = 5;
+
+ // Result of the connection.
+ optional int32 connection_result = 6;
+
+ // Reasons for level 2 failure (needs to be coordinated with wpa-supplicant).
+ optional int32 level_2_failure_code = 7;
+
+ // Failures that happen at the connectivity layer.
+ optional ConnectivityLevelFailure connectivity_level_failure_code = 8;
+
+ // Has bug report been taken.
+ optional bool automatic_bug_report_taken = 9;
+}
+
+// Number of occurrences of a specific RSSI poll rssi value
+message RssiPollCount {
+ // RSSI
+ optional int32 rssi = 1;
+
+ // Number of RSSI polls with 'rssi'
+ optional int32 count = 2;
+}
+
+// Number of occurrences of a specific alert reason value
+message AlertReasonCount {
+ // Alert reason
+ optional int32 reason = 1;
+
+ // Number of alerts with |reason|.
+ optional int32 count = 2;
+}
+
+// Counts the number of instances of a specific Wifi Score calculated by WifiScoreReport
+message WifiScoreCount {
+ // Wifi Score
+ optional int32 score = 1;
+
+ // Number of Wifi score reports with this score
+ optional int32 count = 2;
+}
+
+// Number of occurrences of Soft AP session durations
+message SoftApDurationBucket {
+ // Bucket covers duration : [duration_sec, duration_sec + bucket_size_sec)
+ // The (inclusive) lower bound of Soft AP session duration represented by this bucket
+ optional int32 duration_sec = 1;
+
+ // The size of this bucket
+ optional int32 bucket_size_sec = 2;
+
+ // Number of soft AP session durations that fit into this bucket
+ optional int32 count = 3;
+}
+
+// Number of occurrences of a soft AP session return code
+message SoftApReturnCodeCount {
+ // Return code of the soft AP session
+ optional int32 return_code = 1;
+
+ // Occurrences of this soft AP return code
+ optional int32 count = 2;
+}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 8424b39..9d3035a 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -21,8 +21,8 @@
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.AppGlobals;
+import android.app.ApplicationThreadConstants;
import android.app.IActivityManager;
-import android.app.IApplicationThread;
import android.app.IBackupAgent;
import android.app.PackageInstallObserver;
import android.app.PendingIntent;
@@ -2897,7 +2897,7 @@
try {
mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid));
agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo,
- IApplicationThread.BACKUP_MODE_INCREMENTAL);
+ ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
addBackupTrace("agent bound; a? = " + (agent != null));
if (agent != null) {
mAgentBinder = agent;
@@ -3808,7 +3808,7 @@
Slog.d(TAG, "Binding to full backup agent : " + mPkg.packageName);
}
mAgent = bindToAgentSynchronous(mPkg.applicationInfo,
- IApplicationThread.BACKUP_MODE_FULL);
+ ApplicationThreadConstants.BACKUP_MODE_FULL);
}
return mAgent != null;
}
@@ -5533,7 +5533,7 @@
// All set; now set up the IPC and launch the agent
setUpPipes();
mAgent = bindToAgentSynchronous(mTargetApp,
- IApplicationThread.BACKUP_MODE_RESTORE_FULL);
+ ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
mAgentPackage = pkg;
} catch (IOException e) {
// fall through to error handling
@@ -6968,7 +6968,7 @@
// All set; now set up the IPC and launch the agent
setUpPipes();
mAgent = bindToAgentSynchronous(mTargetApp,
- IApplicationThread.BACKUP_MODE_RESTORE_FULL);
+ ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
mAgentPackage = pkg;
} catch (IOException e) {
// fall through to error handling
@@ -8390,7 +8390,7 @@
// Good to go! Set up and bind the agent...
mAgent = bindToAgentSynchronous(
mCurrentPackage.applicationInfo,
- IApplicationThread.BACKUP_MODE_INCREMENTAL);
+ ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
if (mAgent == null) {
Slog.w(TAG, "Can't find backup agent for " + packageName);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 9efafb7..11586ee 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -8,7 +8,6 @@
LOCAL_SRC_FILES += \
$(call all-java-files-under,java) \
- $(call all-proto-files-under, proto) \
java/com/android/server/EventLogTags.logtags \
java/com/android/server/am/EventLogTags.logtags \
../../../../system/netd/server/binder/android/net/INetd.aidl \
@@ -24,7 +23,6 @@
android.hardware.light@2.0-java
LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
ifneq ($(INCREMENTAL_BUILDS),)
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 3ad71ea..6456048 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -208,7 +208,7 @@
} finally {
mBluetoothLock.readLock().unlock();
}
- Slog.d(TAG, "state" + st);
+ Slog.d(TAG, "Airplane Mode change - current state: " + st);
if (isAirplaneModeOn()) {
// Clear registered LE apps to force shut-off
@@ -222,6 +222,7 @@
mBluetoothLock.readLock().lock();
if (mBluetooth != null) {
mBluetooth.onBrEdrDown();
+ mEnable = false;
mEnableExternal = false;
}
} catch (RemoteException e) {
@@ -275,6 +276,7 @@
mContext.registerReceiver(mReceiver, filter);
loadStoredNameAndAddress();
if (isBluetoothPersistedStateOn()) {
+ if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
mEnableExternal = true;
}
@@ -301,8 +303,10 @@
* Returns true if the Bluetooth saved state is "on"
*/
private final boolean isBluetoothPersistedStateOn() {
- return Settings.Global.getInt(mContentResolver,
- Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) != BLUETOOTH_OFF;
+ int state = Settings.Global.getInt(mContentResolver,
+ Settings.Global.BLUETOOTH_ON, -1);
+ if (DBG) Slog.d(TAG, "Bluetooth persisted state: " + state);
+ return state != BLUETOOTH_OFF;
}
/**
@@ -318,6 +322,7 @@
*
*/
private void persistBluetoothSetting(int value) {
+ if (DBG) Slog.d(TAG, "Persisting Bluetooth Setting: " + value);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.BLUETOOTH_ON,
value);
@@ -454,14 +459,16 @@
class ClientDeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
- if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App");
+ if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App");
if (mBleAppCount > 0) --mBleAppCount;
if (mBleAppCount == 0) {
if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash");
try {
mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
+ if (mBluetooth != null &&
+ mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+ mEnable = false;
mBluetooth.onBrEdrDown();
}
} catch (RemoteException e) {
@@ -478,6 +485,9 @@
@Override
public boolean isBleScanAlwaysAvailable() {
+ if (isAirplaneModeOn() && !mEnable) {
+ return false;
+ }
try {
return (Settings.Global.getInt(mContentResolver,
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE)) != 0;
@@ -1430,7 +1440,7 @@
{
int prevState = msg.arg1;
int newState = msg.arg2;
- if (DBG) Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState);
+ if (DBG) Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState =" + newState);
mState = newState;
bluetoothStateChangeHandler(prevState, newState);
// handle error state transition case from TURNING_ON to OFF
@@ -1742,6 +1752,7 @@
private void bluetoothStateChangeHandler(int prevState, int newState) {
boolean isStandardBroadcast = true;
+ if (DBG) Slog.d(TAG, "bluetoothStateChangeHandler: " + prevState + " -> " + newState);
if (prevState != newState) {
//Notify all proxy objects first of adapter state change
if (newState == BluetoothAdapter.STATE_BLE_ON ||
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ae60d1e..de70026 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -132,6 +132,7 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.KeepaliveTracker;
+import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.LingerMonitor;
import com.android.server.connectivity.NetworkAgentInfo;
@@ -815,7 +816,8 @@
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
&& SystemProperties.get("ro.build.type").equals("eng");
- mTethering = new Tethering(mContext, mNetd, statsService, mPolicyManager);
+ mTethering = new Tethering(mContext, mNetd, statsService, mPolicyManager,
+ IoThread.get().getLooper(), new MockableSystemProperties());
mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -2255,11 +2257,19 @@
synchronized (mNetworkForNetId) {
nai = mNetworkForNetId.get(netId);
}
- // If captive portal status has changed, update capabilities.
+ // If captive portal status has changed, update capabilities or disconnect.
if (nai != null && (visible != nai.lastCaptivePortalDetected)) {
final int oldScore = nai.getCurrentScore();
nai.lastCaptivePortalDetected = visible;
nai.everCaptivePortalDetected |= visible;
+ if (nai.lastCaptivePortalDetected &&
+ Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
+ if (DBG) log("Avoiding captive portal network: " + nai.name());
+ nai.asyncChannel.sendMessage(
+ NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
+ teardownUnneededNetwork(nai);
+ break;
+ }
updateCapabilities(oldScore, nai, nai.networkCapabilities);
}
if (!visible) {
@@ -2280,6 +2290,12 @@
return true;
}
+ private int getCaptivePortalMode() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.CAPTIVE_PORTAL_MODE,
+ Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
+ }
+
private boolean maybeHandleNetworkAgentInfoMessage(Message msg) {
switch (msg.what) {
default:
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index 07aa5656..122074b 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -167,10 +167,17 @@
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(Intent.EXTRA_DOCK_STATE, mReportedDockState);
+ boolean dockSoundsEnabled = Settings.Global.getInt(cr,
+ Settings.Global.DOCK_SOUNDS_ENABLED, 1) == 1;
+ boolean dockSoundsEnabledWhenAccessibility = Settings.Global.getInt(cr,
+ Settings.Global.DOCK_SOUNDS_ENABLED_WHEN_ACCESSIBILITY, 1) == 1;
+ boolean accessibilityEnabled = Settings.Secure.getInt(cr,
+ Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
+
// Play a sound to provide feedback to confirm dock connection.
// Particularly useful for flaky contact pins...
- if (Settings.Global.getInt(cr,
- Settings.Global.DOCK_SOUNDS_ENABLED, 1) == 1) {
+ if ((dockSoundsEnabled) ||
+ (accessibilityEnabled && dockSoundsEnabledWhenAccessibility)) {
String whichSound = null;
if (mReportedDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
if ((previousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index bb5f62b..2825cf9 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -466,9 +466,8 @@
if (mCarModeEnabled) {
if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) {
adjustStatusBarCarModeLocked();
-
if (oldAction != null) {
- getContext().sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL);
+ sendForegroundBroadcastToAllUsers(oldAction);
}
mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
action = UiModeManager.ACTION_ENTER_CAR_MODE;
@@ -476,7 +475,7 @@
} else if (isDeskDockState(mDockState)) {
if (!isDeskDockState(mLastBroadcastState)) {
if (oldAction != null) {
- getContext().sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL);
+ sendForegroundBroadcastToAllUsers(oldAction);
}
mLastBroadcastState = mDockState;
action = UiModeManager.ACTION_ENTER_DESK_MODE;
@@ -502,6 +501,7 @@
Intent intent = new Intent(action);
intent.putExtra("enableFlags", enableFlags);
intent.putExtra("disableFlags", disableFlags);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
getContext().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
mResultReceiver, null, Activity.RESULT_OK, null, null);
@@ -550,6 +550,11 @@
}
}
+ private void sendForegroundBroadcastToAllUsers(String action) {
+ getContext().sendBroadcastAsUser(new Intent(action)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), UserHandle.ALL);
+ }
+
private void updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags) {
// Launch a dock activity
String category = null;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index fb4b010..dc4a52d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3070,7 +3070,7 @@
try {
TransferPipe tp = new TransferPipe();
try {
- thread.dumpService(tp.getWriteFd().getFileDescriptor(), r, args);
+ thread.dumpService(tp.getWriteFd(), r, args);
tp.setBufferPrefix(" ");
// Short timeout, since blocking here can
// deadlock with the application.
@@ -3338,7 +3338,7 @@
try {
TransferPipe tp = new TransferPipe();
try {
- r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(), r, args);
+ r.app.thread.dumpService(tp.getWriteFd(), r, args);
tp.setBufferPrefix(prefix + " ");
tp.go(fd);
} finally {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 097d2a5..4568da4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,7 +16,11 @@
package com.android.server.am;
+import android.app.ApplicationThreadConstants;
import android.os.IDeviceIdentifiersPolicyService;
+import android.util.Size;
+import android.util.TypedValue;
+import android.view.DisplayInfo;
import com.android.internal.telephony.TelephonyIntents;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -82,7 +86,6 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.ApplicationErrorReport;
-import android.app.ApplicationThreadNative;
import android.app.BroadcastOptions;
import android.app.Dialog;
import android.app.IActivityContainer;
@@ -216,7 +219,6 @@
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.Xml;
-import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -253,6 +255,7 @@
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
+import static android.Manifest.permission.CHANGE_CONFIGURATION;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
@@ -276,6 +279,7 @@
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
+import static android.os.Build.VERSION_CODES.N;
import static android.os.Process.PROC_CHAR;
import static android.os.Process.PROC_OUT_LONG;
import static android.os.Process.PROC_PARENS;
@@ -288,6 +292,9 @@
import static android.provider.Settings.Global.LENIENT_BACKGROUND_CHECK;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.provider.Settings.System.FONT_SCALE;
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -1125,10 +1132,11 @@
private int mConfigurationSeq;
/**
- * Temp object used when global configuration is updated. It is also sent to outer world
- * instead of {@link #getGlobalConfiguration} because we don't trust anyone...
+ * Temp object used when global and/or display override configuration is updated. It is also
+ * sent to outer world instead of {@link #getGlobalConfiguration} because we don't trust
+ * anyone...
*/
- private Configuration mTempGlobalConfig = new Configuration();
+ private Configuration mTempConfig = new Configuration();
private final UpdateConfigurationResult mTmpUpdateConfigurationResult =
new UpdateConfigurationResult();
@@ -1369,7 +1377,6 @@
boolean mSupportsFreeformWindowManagement;
boolean mSupportsPictureInPicture;
boolean mSupportsLeanbackOnly;
- Rect mDefaultPinnedStackBounds;
IActivityController mController = null;
boolean mControllerIsAMonkey = false;
String mProfileApp = null;
@@ -1389,6 +1396,12 @@
final long[] mTmpLong = new long[2];
+ // The size and position information that describes where the pinned stack will go by default.
+ // In particular, the size is defined in DPs.
+ Size mDefaultPinnedStackSizeDp;
+ Size mDefaultPinnedStackScreenEdgeInsetsDp;
+ int mDefaultPinnedStackGravity;
+
static final class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
static final int CHANGE_PROCESS_STATE = 1<<1;
@@ -2707,14 +2720,14 @@
mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
- mTempGlobalConfig.setToDefaults();
- mTempGlobalConfig.setLocales(LocaleList.getDefault());
- mConfigurationSeq = mTempGlobalConfig.seq = 1;
+ mTempConfig.setToDefaults();
+ mTempConfig.setLocales(LocaleList.getDefault());
+ mConfigurationSeq = mTempConfig.seq = 1;
mProcessCpuTracker.init();
mStackSupervisor = new ActivityStackSupervisor(this);
- mStackSupervisor.onConfigurationChanged(mTempGlobalConfig);
+ mStackSupervisor.onConfigurationChanged(mTempConfig);
mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mActivityStarter = new ActivityStarter(this, mStackSupervisor);
@@ -4059,7 +4072,7 @@
@Override
public int getPackageProcessState(String packageName, String callingPackage) {
if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.GET_PACKAGE_IMPORTANCE,
+ enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
"getPackageProcessState");
}
@@ -4069,20 +4082,12 @@
final ProcessRecord proc = mLruProcesses.get(i);
if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT
|| procState > proc.setProcState) {
- boolean found = false;
- for (int j=proc.pkgList.size()-1; j>=0 && !found; j--) {
- if (proc.pkgList.keyAt(j).equals(packageName)) {
- procState = proc.setProcState;
- found = true;
- }
+ if (proc.pkgList.containsKey(packageName)) {
+ procState = proc.setProcState;
+ break;
}
- if (proc.pkgDeps != null && !found) {
- for (int j=proc.pkgDeps.size()-1; j>=0; j--) {
- if (proc.pkgDeps.valueAt(j).equals(packageName)) {
- procState = proc.setProcState;
- break;
- }
- }
+ if (proc.pkgDeps != null && proc.pkgDeps.contains(packageName)) {
+ procState = proc.setProcState;
}
}
}
@@ -4752,23 +4757,12 @@
if (r == null) {
return;
}
- TaskRecord task = r.task;
- if (task != null && (!task.mFullscreen || !task.getStack().mFullscreen)) {
- // Fixed screen orientation isn't supported when activities aren't in full screen
- // mode.
- return;
- }
final long origId = Binder.clearCallingIdentity();
- mWindowManager.setAppOrientation(r.appToken, requestedOrientation);
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- getGlobalConfiguration(), r.mayFreezeScreenLocked(r.app) ? r.appToken : null);
- if (config != null) {
- r.frozenBeforeDestroy = true;
- if (!updateConfigurationLocked(config, r, false)) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
- }
+ try {
+ r.setRequestedOrientation(requestedOrientation);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- Binder.restoreCallingIdentity(origId);
}
}
@@ -6507,11 +6501,11 @@
TAG, "New app record " + app
+ " thread=" + thread.asBinder() + " pid=" + pid);
try {
- int testMode = IApplicationThread.DEBUG_OFF;
+ int testMode = ApplicationThreadConstants.DEBUG_OFF;
if (mDebugApp != null && mDebugApp.equals(processName)) {
testMode = mWaitForDebugger
- ? IApplicationThread.DEBUG_WAIT
- : IApplicationThread.DEBUG_ON;
+ ? ApplicationThreadConstants.DEBUG_WAIT
+ : ApplicationThreadConstants.DEBUG_ON;
app.debugging = true;
if (mDebugTransient) {
mDebugApp = mOrigDebugApp;
@@ -7595,7 +7589,7 @@
// current bounds.
final ActivityStack pinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID);
final Rect bounds = (pinnedStack != null)
- ? pinnedStack.mBounds : mDefaultPinnedStackBounds;
+ ? pinnedStack.mBounds : getDefaultPictureInPictureBounds(DEFAULT_DISPLAY);
mStackSupervisor.moveActivityToPinnedStackLocked(
r, "enterPictureInPictureMode", bounds);
@@ -7605,6 +7599,85 @@
}
}
+ @Override
+ public Rect getDefaultPictureInPictureBounds(int displayId) {
+ final long origId = Binder.clearCallingIdentity();
+ final Rect defaultBounds = new Rect();
+ try {
+ synchronized(this) {
+ if (!mSupportsPictureInPicture) {
+ return new Rect();
+ }
+
+ // Convert the sizes to for the current display state
+ final DisplayMetrics dm = mStackSupervisor.getDisplayRealMetrics(displayId);
+ final int stackWidth = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
+ mDefaultPinnedStackSizeDp.getWidth(), dm);
+ final int stackHeight = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
+ mDefaultPinnedStackSizeDp.getHeight(), dm);
+ final Rect maxBounds = new Rect();
+ getPictureInPictureBounds(displayId, maxBounds);
+ Gravity.apply(mDefaultPinnedStackGravity, stackWidth, stackHeight,
+ maxBounds, 0, 0, defaultBounds);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ return defaultBounds;
+ }
+
+ @Override
+ public Rect getPictureInPictureMovementBounds(int displayId) {
+ final long origId = Binder.clearCallingIdentity();
+ final Rect maxBounds = new Rect();
+ try {
+ synchronized(this) {
+ if (!mSupportsPictureInPicture) {
+ return new Rect();
+ }
+
+ getPictureInPictureBounds(displayId, maxBounds);
+
+ // Adjust the max bounds by the current stack dimensions
+ final StackInfo pinnedStackInfo = mStackSupervisor.getStackInfoLocked(
+ PINNED_STACK_ID);
+ if (pinnedStackInfo != null) {
+ maxBounds.right = Math.max(maxBounds.left, maxBounds.right -
+ pinnedStackInfo.bounds.width());
+ maxBounds.bottom = Math.max(maxBounds.top, maxBounds.bottom -
+ pinnedStackInfo.bounds.height());
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ return maxBounds;
+ }
+
+ /**
+ * Calculate the bounds where the pinned stack can move in the current display state.
+ */
+ private void getPictureInPictureBounds(int displayId, Rect outRect) {
+ // Convert the insets to for the current display state
+ final DisplayMetrics dm = mStackSupervisor.getDisplayRealMetrics(displayId);
+ final int insetsLR = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
+ mDefaultPinnedStackScreenEdgeInsetsDp.getWidth(), dm);
+ final int insetsTB = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
+ mDefaultPinnedStackScreenEdgeInsetsDp.getHeight(), dm);
+ try {
+ final Point displaySize = mStackSupervisor.getDisplayRealSize(displayId);
+ final Rect insets = new Rect();
+ mWindowManager.getStableInsets(displayId, insets);
+
+ // Calculate the insets from the system decorations and apply the gravity
+ outRect.set(insets.left + insetsLR, insets.top + insetsTB,
+ displaySize.x - insets.right - insetsLR,
+ displaySize.y - insets.bottom - insetsTB);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to calculate PIP movement bounds", e);
+ }
+ }
+
// =========================================================
// PROCESS INFO
// =========================================================
@@ -9783,14 +9856,6 @@
}
@Override
- public void deleteActivityContainer(IActivityContainer container) throws RemoteException {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "deleteActivityContainer()");
- synchronized (this) {
- mStackSupervisor.deleteActivityContainer(container);
- }
- }
-
- @Override
public IActivityContainer createStackOnDisplay(int displayId) throws RemoteException {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
synchronized (this) {
@@ -9811,7 +9876,7 @@
if (stack != null && stack.mActivityContainer.isAttachedLocked()) {
return stack.mActivityContainer.getDisplayId();
}
- return Display.DEFAULT_DISPLAY;
+ return DEFAULT_DISPLAY;
}
}
@@ -13174,8 +13239,12 @@
com.android.internal.R.dimen.thumbnail_width);
mThumbnailHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.thumbnail_height);
- mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString(
- com.android.internal.R.string.config_defaultPictureInPictureBounds));
+ mDefaultPinnedStackSizeDp = Size.parseSize(res.getString(
+ com.android.internal.R.string.config_defaultPictureInPictureSize));
+ mDefaultPinnedStackScreenEdgeInsetsDp = Size.parseSize(res.getString(
+ com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets));
+ mDefaultPinnedStackGravity = res.getInteger(
+ com.android.internal.R.integer.config_defaultPictureInPictureGravity);
mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
com.android.internal.R.string.config_appsNotReportingCrashes));
mUserController.mUserSwitchUiEnabled = !res.getBoolean(
@@ -14257,6 +14326,13 @@
}
} else if ("locks".equals(cmd)) {
LockGuard.dump(fd, pw, args);
+ } else if ("pip".equals(cmd)) {
+ Rect bounds = getDefaultPictureInPictureBounds(DEFAULT_DISPLAY);
+ pw.print("defaultBounds="); bounds.printShortString(pw);
+ pw.println();
+ bounds = getPictureInPictureMovementBounds(DEFAULT_DISPLAY);
+ pw.print("movementBounds="); bounds.printShortString(pw);
+ pw.println();
} else {
// Dumping a single activity?
if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll, dumpVisibleStacks)) {
@@ -14779,6 +14855,7 @@
}
if (dumpPackage == null) {
pw.println(" mGlobalConfiguration: " + getGlobalConfiguration());
+ mStackSupervisor.dumpDisplayConfigs(pw, " ");
}
if (dumpAll) {
pw.println(" mConfigWillChange: " + getFocusedStack().mConfigWillChange);
@@ -15155,7 +15232,8 @@
if (lastTask != r.task) {
lastTask = r.task;
pw.print("TASK "); pw.print(lastTask.affinity);
- pw.print(" id="); pw.println(lastTask.taskId);
+ pw.print(" id="); pw.print(lastTask.taskId);
+ pw.print(" userId="); pw.println(lastTask.userId);
if (dumpAll) {
lastTask.dump(pw, " ");
}
@@ -15190,7 +15268,7 @@
try {
TransferPipe tp = new TransferPipe();
try {
- r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
+ r.app.thread.dumpActivity(tp.getWriteFd(),
r.appToken, innerPrefix, args);
tp.go(fd);
} finally {
@@ -15723,7 +15801,7 @@
try {
TransferPipe tp = new TransferPipe();
try {
- r.thread.dumpGfxInfo(tp.getWriteFd().getFileDescriptor(), args);
+ r.thread.dumpGfxInfo(tp.getWriteFd(), args);
tp.go(fd);
} finally {
tp.kill();
@@ -15756,7 +15834,7 @@
try {
TransferPipe tp = new TransferPipe();
try {
- r.thread.dumpDbInfo(tp.getWriteFd().getFileDescriptor(), args);
+ r.thread.dumpDbInfo(tp.getWriteFd(), args);
tp.go(fd);
} finally {
tp.kill();
@@ -16160,10 +16238,22 @@
pw.println();
}
} else {
+ pw.flush();
try {
- pw.flush();
- thread.dumpMemInfo(fd, mi, isCheckinRequest, dumpFullDetails,
- dumpDalvik, dumpSummaryOnly, dumpUnreachable, innerArgs);
+ TransferPipe tp = new TransferPipe();
+ try {
+ thread.dumpMemInfo(tp.getWriteFd(),
+ mi, isCheckinRequest, dumpFullDetails,
+ dumpDalvik, dumpSummaryOnly, dumpUnreachable, innerArgs);
+ tp.go(fd);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ if (!isCheckinRequest) {
+ pw.println("Got IoException!");
+ pw.flush();
+ }
} catch (RemoteException e) {
if (!isCheckinRequest) {
pw.println("Got RemoteException!");
@@ -17333,9 +17423,10 @@
}
BackupRecord r = new BackupRecord(ss, app, backupMode);
- ComponentName hostingName = (backupMode == IApplicationThread.BACKUP_MODE_INCREMENTAL)
- ? new ComponentName(app.packageName, app.backupAgentName)
- : new ComponentName("android", "FullBackupAgent");
+ ComponentName hostingName =
+ (backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL)
+ ? new ComponentName(app.packageName, app.backupAgentName)
+ : new ComponentName("android", "FullBackupAgent");
// startProcessLocked() returns existing proc's record if it's already running
ProcessRecord proc = startProcessLocked(app.processName, app,
false, 0, "backup", hostingName, false, false, false);
@@ -17348,7 +17439,8 @@
// process, etc, then mark it as being in full backup so that certain calls to the
// process can be blocked. This is not reset to false anywhere because we kill the
// process after the full backup is done and the ProcessRecord will vaporize anyway.
- if (UserHandle.isApp(app.uid) && backupMode == IApplicationThread.BACKUP_MODE_FULL) {
+ if (UserHandle.isApp(app.uid) &&
+ backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL) {
proc.inFullBackup = true;
}
r.app = proc;
@@ -18019,8 +18111,8 @@
}
mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
sendPackageBroadcastLocked(
- IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list,
- userId);
+ ApplicationThreadConstants.EXTERNAL_STORAGE_UNAVAILABLE,
+ list, userId);
}
break;
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
@@ -18045,8 +18137,8 @@
removed ? "pkg removed" : "pkg changed");
}
final int cmd = killProcess
- ? IApplicationThread.PACKAGE_REMOVED
- : IApplicationThread.PACKAGE_REMOVED_DONT_KILL;
+ ? ApplicationThreadConstants.PACKAGE_REMOVED
+ : ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL;
sendPackageBroadcastLocked(cmd,
new String[] {ssp}, userId);
if (fullUninstall) {
@@ -18111,7 +18203,7 @@
return ActivityManager.BROADCAST_SUCCESS;
}
mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
- sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REPLACED,
+ sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,
new String[] {ssp}, userId);
}
break;
@@ -18826,8 +18918,7 @@
@Override
public void updatePersistentConfiguration(Configuration values) {
- enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
- "updatePersistentConfiguration()");
+ enforceCallingPermission(CHANGE_CONFIGURATION, "updatePersistentConfiguration()");
enforceWriteSettingsPermission("updatePersistentConfiguration()");
if (values == null) {
throw new NullPointerException("Configuration must not be null");
@@ -18852,12 +18943,16 @@
private void updateFontScaleIfNeeded(@UserIdInt int userId) {
final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
FONT_SCALE, 1.0f, userId);
- if (getGlobalConfiguration().fontScale != scaleFactor) {
- final Configuration configuration = mWindowManager.computeNewConfiguration();
- configuration.fontScale = scaleFactor;
- synchronized (this) {
- updatePersistentConfigurationLocked(configuration, userId);
+
+ synchronized (this) {
+ if (getGlobalConfiguration().fontScale == scaleFactor) {
+ return;
}
+
+ final Configuration configuration
+ = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+ configuration.fontScale = scaleFactor;
+ updatePersistentConfigurationLocked(configuration, userId);
}
}
@@ -18882,21 +18977,20 @@
@Override
public boolean updateConfiguration(Configuration values) {
- enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
- "updateConfiguration()");
+ enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
synchronized(this) {
if (values == null && mWindowManager != null) {
// sentinel: fetch the current configuration from the window manager
- values = mWindowManager.computeNewConfiguration();
+ values = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
}
if (mWindowManager != null) {
+ // Update OOM levels based on display size.
mProcessList.applyDisplaySize(mWindowManager);
}
final long origId = Binder.clearCallingIdentity();
-
try {
if (values != null) {
Settings.System.clearConfiguration(values);
@@ -18983,8 +19077,8 @@
/** Update default (global) configuration and notify listeners about changes. */
private int updateGlobalConfiguration(@NonNull Configuration values, boolean initLocale,
boolean persistent, int userId, boolean deferResume) {
- mTempGlobalConfig.setTo(getGlobalConfiguration());
- final int changes = mTempGlobalConfig.updateFrom(values);
+ mTempConfig.setTo(getGlobalConfiguration());
+ final int changes = mTempConfig.updateFrom(values);
if (changes == 0) {
return 0;
}
@@ -19011,33 +19105,33 @@
}
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
- mTempGlobalConfig.seq = mConfigurationSeq;
+ mTempConfig.seq = mConfigurationSeq;
// Update stored global config and notify everyone about the change.
- mStackSupervisor.onConfigurationChanged(mTempGlobalConfig);
+ mStackSupervisor.onConfigurationChanged(mTempConfig);
- Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempGlobalConfig);
+ Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
// TODO(multi-display): Update UsageEvents#Event to include displayId.
- mUsageStatsService.reportConfigurationChange(mTempGlobalConfig,
+ mUsageStatsService.reportConfigurationChange(mTempConfig,
mUserController.getCurrentUserIdLocked());
// TODO: If our config changes, should we auto dismiss any currently showing dialogs?
- mShowDialogs = shouldShowDialogs(mTempGlobalConfig, mInVrMode);
+ mShowDialogs = shouldShowDialogs(mTempConfig, mInVrMode);
AttributeCache ac = AttributeCache.instance();
if (ac != null) {
- ac.updateConfiguration(mTempGlobalConfig);
+ ac.updateConfiguration(mTempConfig);
}
// Make sure all resources in our process are updated right now, so that anyone who is going
// to retrieve resource values after we return will be sure to get the new ones. This is
// especially important during boot, where the first config change needs to guarantee all
// resources have that config before following boot code is executed.
- mSystemThread.applyConfigurationToResources(mTempGlobalConfig);
+ mSystemThread.applyConfigurationToResources(mTempConfig);
// We need another copy of global config because we're scheduling some calls instead of
// running them in place. We need to be sure that object we send will be handled unchanged.
- final Configuration configCopy = new Configuration(mTempGlobalConfig);
+ final Configuration configCopy = new Configuration(mTempConfig);
if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
msg.obj = configCopy;
@@ -19045,16 +19139,6 @@
mHandler.sendMessage(msg);
}
- // TODO(multi-display): Clear also on secondary display density change?
- final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
- if (isDensityChange) {
- // Reset the unsupported display size dialog.
- mUiHandler.sendEmptyMessage(SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG);
-
- killAllBackgroundProcessesExcept(Build.VERSION_CODES.N,
- ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
- }
-
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord app = mLruProcesses.get(i);
try {
@@ -19084,13 +19168,116 @@
UserHandle.USER_ALL);
}
+ // Override configuration of the default display duplicates global config, so we need to
+ // update it also. This will also notify WindowManager about changes.
+ performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
+ DEFAULT_DISPLAY);
+
+ return changes;
+ }
+
+ @Override
+ public boolean updateDisplayOverrideConfiguration(Configuration values, int displayId) {
+ enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()");
+
+ synchronized (this) {
+ if (values == null && mWindowManager != null) {
+ // sentinel: fetch the current configuration from the window manager
+ values = mWindowManager.computeNewConfiguration(displayId);
+ }
+
+ if (mWindowManager != null) {
+ // Update OOM levels based on display size.
+ mProcessList.applyDisplaySize(mWindowManager);
+ }
+
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ if (values != null) {
+ Settings.System.clearConfiguration(values);
+ }
+ updateDisplayOverrideConfigurationLocked(values, null /* starting */,
+ false /* deferResume */, displayId, mTmpUpdateConfigurationResult);
+ return mTmpUpdateConfigurationResult.changes != 0;
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ boolean updateDisplayOverrideConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean deferResume, int displayId) {
+ return updateDisplayOverrideConfigurationLocked(values, starting, deferResume /* deferResume */,
+ displayId, null /* result */);
+ }
+
+ /**
+ * Updates override configuration specific for the selected display. If no config is provided,
+ * new one will be computed in WM based on current display info.
+ */
+ private boolean updateDisplayOverrideConfigurationLocked(Configuration values,
+ ActivityRecord starting, boolean deferResume, int displayId,
+ UpdateConfigurationResult result) {
+ int changes = 0;
+ boolean kept = true;
+
+ if (mWindowManager != null) {
+ mWindowManager.deferSurfaceLayout();
+ }
+ try {
+ if (values != null) {
+ if (displayId == DEFAULT_DISPLAY) {
+ // Override configuration of the default display duplicates global config, so
+ // we're calling global config update instead for default display. It will also
+ // apply the correct override config.
+ changes = updateGlobalConfiguration(values, false /* initLocale */,
+ false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);
+ } else {
+ changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);
+ }
+ }
+
+ kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ } finally {
+ if (mWindowManager != null) {
+ mWindowManager.continueSurfaceLayout();
+ }
+ }
+
+ if (result != null) {
+ result.changes = changes;
+ result.activityRelaunched = !kept;
+ }
+ return kept;
+ }
+
+ private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
+ int displayId) {
+ mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+ final int changes = mTempConfig.updateFrom(values);
+ if (changes == 0) {
+ return 0;
+ }
+
+ Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " " + mTempConfig
+ + " for displayId=" + displayId);
+ mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
+
+ final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
+ if (isDensityChange) {
+ // Reset the unsupported display size dialog.
+ mUiHandler.sendEmptyMessage(SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG);
+
+ killAllBackgroundProcessesExcept(N, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ }
+
// Update the configuration with WM first and check if any of the stacks need to be resized
// due to the configuration change. If so, resize the stacks now and do any relaunches if
// necessary. This way we don't need to relaunch again afterwards in
// ensureActivityConfigurationLocked().
if (mWindowManager != null) {
final int[] resizedStacks =
- mWindowManager.setNewConfiguration(mTempGlobalConfig);
+ mWindowManager.setNewDisplayOverrideConfiguration(mTempConfig, displayId);
if (resizedStacks != null) {
for (int stackId : resizedStacks) {
resizeStackWithBoundsFromWindowManager(stackId, deferResume);
@@ -21881,8 +22068,7 @@
try {
TransferPipe tp = new TransferPipe();
try {
- process.thread.stopBinderTrackingAndDump(
- tp.getWriteFd().getFileDescriptor());
+ process.thread.stopBinderTrackingAndDump(tp.getWriteFd());
tp.go(fd.getFileDescriptor());
} finally {
tp.kill();
@@ -22206,7 +22392,7 @@
if (tr == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
- appThread = ApplicationThreadNative.asInterface(whoThread);
+ appThread = IApplicationThread.Stub.asInterface(whoThread);
if (appThread == null) {
throw new IllegalArgumentException("Bad app thread " + appThread);
}
@@ -22291,4 +22477,29 @@
// before the profile user is unlocked.
return rInfo != null && rInfo.activityInfo != null;
}
+
+ /**
+ * Attach an agent to the specified process (proces name or PID)
+ */
+ public void attachAgent(String process, String path) {
+ try {
+ synchronized (this) {
+ ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM, "attachAgent");
+ if (proc == null || proc.thread == null) {
+ throw new IllegalArgumentException("Unknown process: " + process);
+ }
+
+ boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+ if (!isDebuggable) {
+ if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+ throw new SecurityException("Process not debuggable: " + proc);
+ }
+ }
+
+ proc.thread.attachAgent(path);
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Process disappeared");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 7d9a706..7a692b6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -220,6 +220,8 @@
return runTask(pw);
case "write":
return runWrite(pw);
+ case "attach-agent":
+ return runAttachAgent(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -2462,6 +2464,21 @@
return 0;
}
+ int runAttachAgent(PrintWriter pw) {
+ // TODO: revisit the permissions required for attaching agents
+ mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "attach-agent");
+ String process = getNextArgRequired();
+ String agent = getNextArgRequired();
+ String opt;
+ if ((opt = getNextArg()) != null) {
+ pw.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ mInternal.attachAgent(process, agent);
+ return 0;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -2481,6 +2498,7 @@
pw.println(" p[rocesses] [PACKAGE_NAME]: process state");
pw.println(" o[om]: out of memory management");
pw.println(" perm[issions]: URI permission grant state");
+ pw.println(" pip: PIP state");
pw.println(" prov[iders] [COMP_SPEC ...]: content provider state");
pw.println(" provider [COMP_SPEC]: provider client-side state");
pw.println(" s[ervices] [COMP_SPEC ...]: service state");
@@ -2618,6 +2636,8 @@
pw.println(" Optionally controls lenient background check mode, returns current mode.");
pw.println(" get-uid-state <UID>");
pw.println(" Gets the process state of an app given its <UID>.");
+ pw.println(" attach-agent <PROCESS> <FILE>");
+ pw.println(" Attach an agent to the specified <PROCESS>, which may be either a process name or a PID.");
pw.println(" get-config");
pw.println(" Rtrieve the configuration and any recent configurations of the device.");
pw.println(" suppress-resize-config-changes <true|false>");
@@ -2682,6 +2702,8 @@
pw.println(" Test command for sizing <TASK_ID> by <STEP_SIZE>");
pw.println(" increments within the screen applying the optional [DELAY_MS] between");
pw.println(" each step.");
+ pw.println(" pip");
+ pw.println(" Gets the current PIP state.");
pw.println(" write");
pw.println(" Write all pending state to storage.");
pw.println();
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index d89f02d..abe4f30 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1279,7 +1279,7 @@
stopFreezingScreenLocked(false);
try {
if (returningOptions != null) {
- app.thread.scheduleOnNewActivityOptions(appToken, returningOptions);
+ app.thread.scheduleOnNewActivityOptions(appToken, returningOptions.toBundle());
}
} catch(RemoteException e) {
}
@@ -1646,6 +1646,17 @@
return null;
}
+ /**
+ * @return display id to which this record is attached, -1 if not attached.
+ */
+ int getDisplayId() {
+ final ActivityStack stack = getStack();
+ if (stack == null) {
+ return -1;
+ }
+ return stack.mDisplayId;
+ }
+
final boolean isDestroyable() {
if (finishing || app == null || state == ActivityState.DESTROYING
|| state == ActivityState.DESTROYED) {
@@ -1704,6 +1715,26 @@
}
}
+ void setRequestedOrientation(int requestedOrientation) {
+ if (task != null && (!task.mFullscreen || !task.getStack().mFullscreen)) {
+ // Fixed screen orientation isn't supported when activities aren't in full screen mode.
+ return;
+ }
+
+ service.mWindowManager.setAppOrientation(appToken, requestedOrientation);
+ final int displayId = getDisplayId();
+ final Configuration config = service.mWindowManager.updateOrientationFromAppTokens(
+ mStackSupervisor.getDisplayOverrideConfiguration(displayId),
+ mayFreezeScreenLocked(app) ? appToken : null, displayId);
+ if (config != null) {
+ frozenBeforeDestroy = true;
+ if (!service.updateDisplayOverrideConfigurationLocked(config, this,
+ false /* deferResume */, displayId)) {
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ }
+ }
+ }
+
// TODO: now used only in one place to address race-condition. Remove when that will be fixed.
void setLastReportedConfiguration(@NonNull Configuration config) {
mLastReportedConfiguration.setTo(config);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 55066fd..3dbbb13 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -467,20 +467,21 @@
}
}
- void detachDisplay() {
+ void remove() {
mDisplayId = Display.INVALID_DISPLAY;
mStacks = null;
if (mTaskPositioner != null) {
mTaskPositioner.reset();
}
- mWindowManager.detachStack(mStackId);
- onParentChanged();
if (mStackId == DOCKED_STACK_ID) {
// If we removed a docked stack we want to resize it so it resizes all other stacks
// in the system to fullscreen.
mStackSupervisor.resizeDockedStackLocked(
null, null, null, null, null, PRESERVE_WINDOWS);
}
+ mStackSupervisor.deleteActivityContainerRecord(mStackId);
+ mWindowManager.removeStack(mStackId);
+ onParentChanged();
}
void getDisplaySize(Point out) {
@@ -2285,13 +2286,14 @@
// the screen based on the new activity order.
boolean notUpdated = true;
if (mStackSupervisor.isFocusedStack(this)) {
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mService.getGlobalConfiguration(),
- next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
+ final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+ mStackSupervisor.getDisplayOverrideConfiguration(mDisplayId),
+ next.mayFreezeScreenLocked(next.app) ? next.appToken : null, mDisplayId);
if (config != null) {
next.frozenBeforeDestroy = true;
}
- notUpdated = !mService.updateConfigurationLocked(config, next, false);
+ notUpdated = !mService.updateDisplayOverrideConfigurationLocked(config, next,
+ false /* deferResume */, mDisplayId);
}
if (notUpdated) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index cd7481c..99c5b06 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -45,6 +45,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
@@ -76,6 +77,7 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -419,7 +421,7 @@
}
@Override
- protected ConfigurationContainer getChildAt(int index) {
+ protected ActivityDisplay getChildAt(int index) {
return mActivityDisplays.valueAt(index);
}
@@ -428,6 +430,24 @@
return null;
}
+ Configuration getDisplayOverrideConfiguration(int displayId) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
+ if (activityDisplay == null) {
+ throw new IllegalArgumentException("No display found with id: " + displayId);
+ }
+
+ return activityDisplay.getOverrideConfiguration();
+ }
+
+ void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
+ if (activityDisplay == null) {
+ throw new IllegalArgumentException("No display found with id: " + displayId);
+ }
+
+ activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
+ }
+
static class FindTaskResult {
ActivityRecord r;
boolean matchedByRootAffinity;
@@ -1190,20 +1210,20 @@
r.startLaunchTickingLocked();
}
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order. Note that
- // as a result of this, it can call back into the activity
- // manager with a new orientation. We don't care about that,
- // because the activity is not currently running so we are
- // just restarting it anyway.
+ // Have the window manager re-evaluate the orientation of the screen based on the new
+ // activity order. Note that as a result of this, it can call back into the activity
+ // manager with a new orientation. We don't care about that, because the activity is not
+ // currently running so we are just restarting it anyway.
if (checkConfig) {
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mService.getGlobalConfiguration(),
- r.mayFreezeScreenLocked(app) ? r.appToken : null);
+ final int displayId = r.getDisplayId();
+ final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+ getDisplayOverrideConfiguration(displayId),
+ r.mayFreezeScreenLocked(app) ? r.appToken : null, displayId);
// Deferring resume here because we're going to launch new activity shortly.
// We don't want to perform a redundant launch of the same record while ensuring
// configurations and trying to resume top activity of focused stack.
- mService.updateConfigurationLocked(config, r, false, true /* deferResume */);
+ mService.updateDisplayOverrideConfigurationLocked(config, r, true /* deferResume */,
+ displayId);
}
r.app = app;
@@ -1989,15 +2009,10 @@
}
}
- void deleteActivityContainer(IActivityContainer container) {
- ActivityContainer activityContainer = (ActivityContainer)container;
- if (activityContainer != null) {
- if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
- "deleteActivityContainer: callers=" + Debug.getCallers(4));
- final int stackId = activityContainer.mStackId;
- mActivityContainers.remove(stackId);
- mWindowManager.removeStack(stackId);
- }
+ void deleteActivityContainerRecord(int stackId) {
+ if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
+ "deleteActivityContainerRecord: callers=" + Debug.getCallers(4));
+ mActivityContainers.remove(stackId);
}
void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
@@ -3156,6 +3171,20 @@
}
/**
+ * Dump all connected displays' configurations.
+ * @param prefix Prefix to apply to each line of the dump.
+ */
+ void dumpDisplayConfigs(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.println("Display override configurations:");
+ final int displayCount = mActivityDisplays.size();
+ for (int i = 0; i < displayCount; i++) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(i);
+ pw.print(prefix); pw.print(" "); pw.print(activityDisplay.mDisplayId); pw.print(": ");
+ pw.println(activityDisplay.getOverrideConfiguration());
+ }
+ }
+
+ /**
* Dumps the activities matching the given {@param name} in the either the focused stack
* or all visible stacks if {@param dumpVisibleStacks} is true.
*/
@@ -3324,8 +3353,7 @@
try {
TransferPipe tp = new TransferPipe();
try {
- r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
- r.appToken, innerPrefix, args);
+ r.app.thread.dumpActivity(tp.getWriteFd(), r.appToken, innerPrefix, args);
// Short timeout, since blocking here can
// deadlock with the application.
tp.go(fd, 2000);
@@ -3394,6 +3422,18 @@
mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0));
}
+ DisplayMetrics getDisplayRealMetrics(int displayId) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
+ activityDisplay.mDisplay.getRealMetrics(activityDisplay.mRealMetrics);
+ return activityDisplay.mRealMetrics;
+ }
+
+ Point getDisplayRealSize(int displayId) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
+ activityDisplay.mDisplay.getRealSize(activityDisplay.mRealSize);
+ return activityDisplay.mRealSize;
+ }
+
private void handleDisplayAdded(int displayId) {
boolean newDisplay;
synchronized (mService) {
@@ -3425,7 +3465,7 @@
if (activityDisplay != null) {
ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
- stacks.get(stackNdx).mActivityContainer.detachLocked();
+ stacks.get(stackNdx).mActivityContainer.removeLocked();
}
mActivityDisplays.remove(displayId);
}
@@ -4065,14 +4105,14 @@
}
}
- protected void detachLocked() {
- if (DEBUG_STACK) Slog.d(TAG_STACK, "detachLocked: " + this + " from display="
+ void removeLocked() {
+ if (DEBUG_STACK) Slog.d(TAG_STACK, "removeLocked: " + this + " from display="
+ mActivityDisplay + " Callers=" + Debug.getCallers(2));
if (mActivityDisplay != null) {
mActivityDisplay.detachActivitiesLocked(mStack);
mActivityDisplay = null;
- mStack.detachDisplay();
}
+ mStack.remove();
}
@Override
@@ -4147,8 +4187,7 @@
}
void onTaskListEmptyLocked() {
- detachLocked();
- deleteActivityContainer(this);
+ removeLocked();
mHandler.obtainMessage(CONTAINER_CALLBACK_TASK_LIST_EMPTY, this).sendToTarget();
}
@@ -4254,7 +4293,8 @@
/** Actual Display this object tracks. */
int mDisplayId;
Display mDisplay;
- DisplayInfo mDisplayInfo = new DisplayInfo();
+ private final DisplayMetrics mRealMetrics = new DisplayMetrics();
+ private final Point mRealSize = new Point();
/** All of the stacks on this display. Order matters, topmost stack is in front of all other
* stacks, bottommost behind. Accessed directly by ActivityManager package classes */
@@ -4278,7 +4318,6 @@
void init(Display display) {
mDisplay = display;
mDisplayId = display.getDisplayId();
- mDisplay.getDisplayInfo(mDisplayInfo);
}
void attachActivities(ActivityStack stack, boolean onTop) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index a834af7..20a14d3 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1592,9 +1592,9 @@
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
// The caller has requested to completely replace any existing task with its new
// activity. Well that should not be too hard...
+ intentActivity.task.performClearTaskLocked();
+ intentActivity.task.setIntent(mStartActivity);
mReuseTask = intentActivity.task;
- mReuseTask.performClearTaskLocked();
- mReuseTask.setIntent(mStartActivity);
// When we clear the task - focus will be adjusted, which will bring another task
// to top before we launch the activity we need. This will temporary swap their
// mTaskToReturnTo values and we don't want to overwrite them accidentally.
diff --git a/services/core/java/com/android/server/am/ConfigurationContainer.java b/services/core/java/com/android/server/am/ConfigurationContainer.java
index 30f5309..a3e95b8 100644
--- a/services/core/java/com/android/server/am/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/am/ConfigurationContainer.java
@@ -114,10 +114,14 @@
*/
void onParentChanged() {
final ConfigurationContainer parent = getParent();
- // Update full configuration of this container and all its children.
- onConfigurationChanged(parent != null ? parent.mFullConfiguration : Configuration.EMPTY);
- // Update merged override configuration of this container and all its children.
- onMergedOverrideConfigurationChanged();
+ // Removing parent usually means that we've detached this entity to destroy it or to attach
+ // to another parent. In both cases we don't need to update the configuration now.
+ if (parent != null) {
+ // Update full configuration of this container and all its children.
+ onConfigurationChanged(parent.mFullConfiguration);
+ // Update merged override configuration of this container and all its children.
+ onMergedOverrideConfigurationChanged();
+ }
}
abstract protected int getChildCount();
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index 878c0e7..a6997f9 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -405,7 +405,7 @@
TransferPipe tp = new TransferPipe();
try {
r.proc.thread.dumpProvider(
- tp.getWriteFd().getFileDescriptor(), r.provider.asBinder(), args);
+ tp.getWriteFd(), r.provider.asBinder(), args);
tp.setBufferPrefix(" ");
// Short timeout, since blocking here can
// deadlock with the application.
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 0817f0d..1ecb2e9 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -54,6 +54,8 @@
import java.util.Comparator;
import java.util.List;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+
public class TaskPersister {
static final String TAG = "TaskPersister";
static final boolean DEBUG = false;
@@ -450,7 +452,7 @@
final int taskId = task.taskId;
if (mStackSupervisor.anyTaskForIdLocked(taskId,
- /* restoreFromRecents= */ false, 0) != null) {
+ /* restoreFromRecents= */ false, HOME_STACK_ID) != null) {
// Should not happen.
Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
} else if (userId != task.userId) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 77d0db3..9d97dd3 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -75,6 +75,8 @@
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -132,8 +134,11 @@
private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
private static final String ATTR_MIN_WIDTH = "min_width";
private static final String ATTR_MIN_HEIGHT = "min_height";
+ private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version";
-
+ // Current version of the task record we persist. Used to check if we need to run any upgrade
+ // code.
+ private static final int PERSIST_TASK_VERSION = 1;
private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
static final int INVALID_TASK_ID = -1;
@@ -1231,6 +1236,7 @@
}
out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth));
out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight));
+ out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION));
if (affinityIntent != null) {
out.startTag(null, TAG_AFFINITYINTENT);
@@ -1297,6 +1303,7 @@
Rect bounds = null;
int minWidth = INVALID_MIN_SIZE;
int minHeight = INVALID_MIN_SIZE;
+ int persistTaskVersion = 0;
for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
final String attrName = in.getAttributeName(attrNdx);
@@ -1366,6 +1373,8 @@
minWidth = Integer.parseInt(attrValue);
} else if (ATTR_MIN_HEIGHT.equals(attrName)) {
minHeight = Integer.parseInt(attrValue);
+ } else if (ATTR_PERSIST_TASK_VERSION.equals(attrName)) {
+ persistTaskVersion = Integer.parseInt(attrValue);
} else {
Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
}
@@ -1420,6 +1429,16 @@
+ ": effectiveUid=" + effectiveUid);
}
+ if (persistTaskVersion < 1) {
+ // We need to convert the resize mode of home activities saved before version one if
+ // they are marked as RESIZE_MODE_RESIZEABLE to RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION
+ // since we didn't have that differentiation before version 1 and the system didn't
+ // resize home activities before then.
+ if (taskType == HOME_ACTIVITY_TYPE && resizeMode == RESIZE_MODE_RESIZEABLE) {
+ resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+ }
+ }
+
final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index f1ef947..f1d01e0 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -48,6 +48,10 @@
final IpConnectivityLog log = new IpConnectivityLog();
log.events = toProto(events);
log.droppedEvents = dropped;
+ if ((log.events.length > 0) || (dropped > 0)) {
+ // Only write version number if log has some information at all.
+ log.version = IpConnectivityMetrics.VERSION;
+ }
return IpConnectivityLog.toByteArray(log);
}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 28e724c..be68173 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -19,19 +19,25 @@
import android.content.Context;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
+import android.net.metrics.ApfProgramEvent;
import android.net.metrics.IpConnectivityLog;
import android.os.IBinder;
import android.os.Parcelable;
+import android.provider.Settings;
import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.Base64;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.TokenBucket;
import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.function.ToIntFunction;
import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent;
@@ -40,10 +46,21 @@
private static final String TAG = IpConnectivityMetrics.class.getSimpleName();
private static final boolean DBG = false;
+ // The logical version numbers of ipconnectivity.proto, corresponding to the
+ // "version" field of IpConnectivityLog.
+ private static final int NYC = 0;
+ private static final int NYC_MR1 = 1;
+ private static final int NYC_MR2 = 2;
+ public static final int VERSION = NYC_MR2;
+
private static final String SERVICE_NAME = IpConnectivityLog.SERVICE_NAME;
// Default size of the event buffer. Once the buffer is full, incoming events are dropped.
private static final int DEFAULT_BUFFER_SIZE = 2000;
+ // Maximum size of the event buffer.
+ private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10;
+
+ private static final int ERROR_RATE_LIMITED = -1;
// Lock ensuring that concurrent manipulations of the event buffer are correct.
// There are three concurrent operations to synchronize:
@@ -62,10 +79,19 @@
private int mDropped;
@GuardedBy("mLock")
private int mCapacity;
+ @GuardedBy("mLock")
+ private final ArrayMap<Class<?>, TokenBucket> mBuckets = makeRateLimitingBuckets();
+
+ private final ToIntFunction<Context> mCapacityGetter;
+
+ public IpConnectivityMetrics(Context ctx, ToIntFunction<Context> capacityGetter) {
+ super(ctx);
+ mCapacityGetter = capacityGetter;
+ initBuffer();
+ }
public IpConnectivityMetrics(Context ctx) {
- super(ctx);
- initBuffer();
+ this(ctx, READ_BUFFER_SIZE);
}
@Override
@@ -86,7 +112,7 @@
@VisibleForTesting
public int bufferCapacity() {
- return DEFAULT_BUFFER_SIZE; // TODO: read from config
+ return mCapacityGetter.applyAsInt(getContext());
}
private void initBuffer() {
@@ -104,6 +130,10 @@
if (event == null) {
return left;
}
+ if (isRateLimited(event)) {
+ // Do not count as a dropped event. TODO: consider adding separate counter
+ return ERROR_RATE_LIMITED;
+ }
if (left == 0) {
mDropped++;
return 0;
@@ -113,6 +143,11 @@
}
}
+ private boolean isRateLimited(ConnectivityMetricsEvent event) {
+ TokenBucket tb = mBuckets.get(event.data.getClass());
+ return (tb != null) && !tb.get();
+ }
+
private String flushEncodedOutput() {
final ArrayList<ConnectivityMetricsEvent> events;
final int dropped;
@@ -186,6 +221,7 @@
static final String CMD_FLUSH = "flush";
static final String CMD_LIST = "list";
static final String CMD_STATS = "stats";
+ static final String CMD_DUMPSYS = "-a"; // dumpsys.cpp dumps services with "-a" as arguments
static final String CMD_DEFAULT = CMD_STATS;
@Override
@@ -203,6 +239,8 @@
case CMD_FLUSH:
cmdFlush(fd, pw, args);
return;
+ case CMD_DUMPSYS:
+ // Fallthrough to CMD_LIST when dumpsys.cpp dumps services states (bug reports)
case CMD_LIST:
cmdList(fd, pw, args);
return;
@@ -226,4 +264,20 @@
getContext().enforceCallingOrSelfPermission(what, "IpConnectivityMetrics");
}
};
+
+ private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> {
+ int size = Settings.Global.getInt(ctx.getContentResolver(),
+ Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
+ if (size <= 0) {
+ return DEFAULT_BUFFER_SIZE;
+ }
+ return Math.min(size, MAXIMUM_BUFFER_SIZE);
+ };
+
+ private static ArrayMap<Class<?>, TokenBucket> makeRateLimitingBuckets() {
+ ArrayMap<Class<?>, TokenBucket> map = new ArrayMap<>();
+ // one token every minute, 50 tokens max: burst of ~50 events every hour.
+ map.put(ApfProgramEvent.class, new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50));
+ return map;
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
new file mode 100644
index 0000000..4f68652
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.os.SystemProperties;
+
+public class MockableSystemProperties {
+ public boolean getBoolean(String key, boolean def) {
+ return SystemProperties.getBoolean(key, def);
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 6eb89fa..c73d1dd 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -211,7 +211,9 @@
private final NetworkRequest mDefaultRequest;
private final IpConnectivityLog mMetricsLog;
- private boolean mIsCaptivePortalCheckEnabled;
+ @VisibleForTesting
+ protected boolean mIsCaptivePortalCheckEnabled;
+
private boolean mUseHttps;
// Set if the user explicitly selected "Do not use this network" in captive portal sign-in app.
@@ -265,7 +267,8 @@
setInitialState(mDefaultState);
mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
+ Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT)
+ != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE;
mUseHttps = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
@@ -632,7 +635,10 @@
@VisibleForTesting
protected CaptivePortalProbeResult isCaptivePortal() {
- if (!mIsCaptivePortalCheckEnabled) return new CaptivePortalProbeResult(204);
+ if (!mIsCaptivePortalCheckEnabled) {
+ validationLog("Validation disabled.");
+ return new CaptivePortalProbeResult(204);
+ }
URL pacUrl = null, httpsUrl = null, httpUrl = null, fallbackUrl = null;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 921fd23..0beb227 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -52,7 +52,6 @@
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
@@ -62,6 +61,7 @@
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.IndentingPrintWriter;
@@ -69,7 +69,6 @@
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
-import com.android.server.IoThread;
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
@@ -100,6 +99,8 @@
private final static boolean DBG = false;
private final static boolean VDBG = false;
+ protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
+
private static final Class[] messageClasses = {
Tethering.class, TetherMasterSM.class, TetherInterfaceStateMachine.class
};
@@ -127,6 +128,7 @@
private final INetworkStatsService mStatsService;
private final INetworkPolicyManager mPolicyManager;
private final Looper mLooper;
+ private final MockableSystemProperties mSystemProperties;
private static class TetherState {
public final TetherInterfaceStateMachine mStateMachine;
@@ -180,18 +182,19 @@
private boolean mWifiTetherRequested;
public Tethering(Context context, INetworkManagementService nmService,
- INetworkStatsService statsService, INetworkPolicyManager policyManager) {
+ INetworkStatsService statsService, INetworkPolicyManager policyManager,
+ Looper looper, MockableSystemProperties systemProperties) {
mContext = context;
mNMService = nmService;
mStatsService = statsService;
mPolicyManager = policyManager;
+ mLooper = looper;
+ mSystemProperties = systemProperties;
mPublicSync = new Object();
mTetherStates = new ArrayMap<>();
- // make our own thread so we don't anr the system
- mLooper = IoThread.get().getLooper();
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
mTetherMasterSM.start();
@@ -394,10 +397,11 @@
*
* @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
*/
- private boolean isTetherProvisioningRequired() {
+ @VisibleForTesting
+ protected boolean isTetherProvisioningRequired() {
String[] provisionApp = mContext.getResources().getStringArray(
com.android.internal.R.array.config_mobile_hotspot_provision_app);
- if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)
+ if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
|| provisionApp == null) {
return false;
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 4e236d1..0727629 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -838,7 +838,7 @@
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
return syncManager.computeSyncable(
- account, userId, providerName);
+ account, userId, providerName, false);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -854,6 +854,8 @@
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
+ syncable = normalizeSyncable(syncable);
+
int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -1156,6 +1158,15 @@
}
}
+ private static int normalizeSyncable(int syncable) {
+ if (syncable > 0) {
+ return SyncStorageEngine.AuthorityInfo.SYNCABLE;
+ } else if (syncable == 0) {
+ return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE;
+ }
+ return SyncStorageEngine.AuthorityInfo.UNDEFINED;
+ }
+
/**
* Hide this class since it is not part of api,
* but current unittest framework requires it to be public
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 72feab7..03d95b2 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1001,7 +1001,12 @@
}
}
- public int computeSyncable(Account account, int userId, String authority) {
+ private int computeSyncable(Account account, int userId, String authority) {
+ return computeSyncable(account, userId, authority, true);
+ }
+
+ public int computeSyncable(Account account, int userId, String authority,
+ boolean checkAccountAccess) {
final int status = getIsSyncable(account, userId, authority);
if (status == AuthorityInfo.NOT_SYNCABLE) {
return AuthorityInfo.NOT_SYNCABLE;
@@ -1025,7 +1030,7 @@
} catch (RemoteException e) {
/* ignore - local call */
}
- if (!canAccessAccount(account, owningPackage, owningUid)) {
+ if (checkAccountAccess && !canAccessAccount(account, owningPackage, owningUid)) {
Log.w(TAG, "Access to " + account + " denied for package "
+ owningPackage + " in UID " + syncAdapterInfo.uid);
return AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index df5def9..3b2dc34 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -284,7 +284,7 @@
+ mScreenBrightnessDarkConfig + ") to be less than or equal to "
+ "config_screenBrightnessDim (" + mScreenBrightnessDimConfig + ").");
}
- if (mScreenBrightnessDarkConfig > mScreenBrightnessDimConfig) {
+ if (mScreenBrightnessDarkConfig > screenBrightnessSettingMinimum) {
Slog.w(TAG, "Expected config_screenBrightnessDark ("
+ mScreenBrightnessDarkConfig + ") to be less than or equal to "
+ "config_screenBrightnessSettingMinimum ("
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 20bccf1..1991c00 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.BIND_DREAM_SERVICE;
+import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -88,6 +89,8 @@
private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+ private AmbientDisplayConfiguration mDozeConfig;
+
public DreamManagerService(Context context) {
super(context);
mContext = context;
@@ -97,6 +100,7 @@
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG);
+ mDozeConfig = new AmbientDisplayConfiguration(mContext);
}
@Override
@@ -121,7 +125,7 @@
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.DOZE_ENABLED), false,
+ Settings.Secure.getUriFor(Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP), false,
mDozeEnabledObserver, UserHandle.USER_ALL);
writePulseGestureEnabled();
}
@@ -326,19 +330,12 @@
}
private ComponentName getDozeComponent(int userId) {
- // Read the component from a system property to facilitate debugging.
- // Note that for production devices, the dream should actually be declared in
- // a config.xml resource.
- String name = Build.IS_DEBUGGABLE ? SystemProperties.get("debug.doze.component") : null;
- if (TextUtils.isEmpty(name)) {
- // Read the component from a config.xml resource.
- // The value should be specified in a resource overlay for the product.
- name = mContext.getResources().getString(
- com.android.internal.R.string.config_dozeComponent);
+ if (mDozeConfig.enabled(userId)) {
+ return ComponentName.unflattenFromString(mDozeConfig.ambientDisplayComponent());
+ } else {
+ return null;
}
- boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.DOZE_ENABLED, 1, userId) != 0;
- return TextUtils.isEmpty(name) || !enabled ? null : ComponentName.unflattenFromString(name);
+
}
private ServiceInfo getServiceInfo(ComponentName name) {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index ae98077..9ae496f 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -58,6 +58,7 @@
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -75,6 +76,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import android.util.Log;
@@ -396,10 +398,6 @@
// Persist property for LPP_PROFILE
private final static String LPP_PROFILE = "persist.sys.gps.lpp";
- // VZW PLMN info
- private static final String[] VzwMccMncList = {"311480", "310004", "20404"};
- // corresponding GID1 value, empty string means ignore gid1 match.
- private static final String[] VzwGid1List = {"", "", "BAE0000000000000"};
private final PowerManager mPowerManager;
@@ -520,42 +518,30 @@
}
};
- private final boolean isVerizon(String mccMnc, String imsi, String groupId) {
- if (DEBUG) Log.d(TAG, "simOperator: " + mccMnc);
- if (!TextUtils.isEmpty(mccMnc) || !TextUtils.isEmpty(imsi)) {
- for (int i = 0; i < VzwMccMncList.length; i++) {
- if ((!TextUtils.isEmpty(mccMnc) && mccMnc.equals(VzwMccMncList[i])) ||
- (!TextUtils.isEmpty(imsi) && imsi.startsWith(VzwMccMncList[i]))) {
- // check gid too if needed
- if (TextUtils.isEmpty(VzwGid1List[i]) || VzwGid1List[i].equals(groupId)) {
- if (DEBUG) Log.d(TAG, "Verizon UICC");
- return true;
- }
- }
- }
- }
- return false;
- }
-
private void subscriptionOrSimChanged(Context context) {
if (DEBUG) Log.d(TAG, "received SIM related action: ");
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
String mccMnc = phone.getSimOperator();
- String imsi = phone.getSubscriberId();
- String groupId = phone.getGroupIdLevel1();
+ boolean isKeepLppProfile = false;
if (!TextUtils.isEmpty(mccMnc)) {
if (DEBUG) Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
synchronized (mLock) {
- if (isVerizon(mccMnc, imsi, groupId)) {
- // load current properties for carrier VZW
- loadPropertiesFromResource(context, mProperties);
- String lpp_profile = mProperties.getProperty("LPP_PROFILE");
- // set the persist property LPP_PROFILE for VZW
- SystemProperties.set(LPP_PROFILE, lpp_profile);
+ if (configManager != null) {
+ PersistableBundle b = configManager.getConfig();
+ isKeepLppProfile = b.getBoolean(CarrierConfigManager.KEY_PERSIST_LPP_MODE_BOOL);
+ }
+ if (isKeepLppProfile) {
+ // load current properties for the carrier
+ loadPropertiesFromResource(context, mProperties);
+ String lpp_profile = mProperties.getProperty("LPP_PROFILE");
+ // set the persist property LPP_PROFILE for the value
+ SystemProperties.set(LPP_PROFILE, lpp_profile);
} else {
- // reset the persist property for Non VZW
- SystemProperties.set(LPP_PROFILE, "");
+ // reset the persist property
+ SystemProperties.set(LPP_PROFILE, "");
}
reloadGpsProperties(context, mProperties);
mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
new file mode 100644
index 0000000..8ea4909
--- /dev/null
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * NotificationManagerService helper for auto-grouping notifications.
+ */
+public class GroupHelper {
+ private static final String TAG = "GroupHelper";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ protected static final int AUTOGROUP_AT_COUNT = 4;
+ protected static final String AUTOGROUP_KEY = "ranker_group";
+
+ private final Callback mCallback;
+
+ // Map of user : <Map of package : notification keys>. Only contains notifications that are not
+ // groupd by the app (aka no group or sort key).
+ Map<Integer, Map<String, LinkedHashSet<String>>> mUngroupedNotifications = new HashMap<>();
+
+ public GroupHelper(Callback callback) {;
+ mCallback = callback;
+ }
+
+ public void onNotificationPosted(StatusBarNotification sbn) {
+ if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
+ try {
+ List<String> notificationsToGroup = new ArrayList<>();
+ if (!sbn.isAppGroup()) {
+ // Not grouped by the app, add to the list of notifications for the app;
+ // send grouping update if app exceeds the autogrouping limit.
+ synchronized (mUngroupedNotifications) {
+ Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser
+ = mUngroupedNotifications.get(sbn.getUserId());
+ if (ungroupedNotificationsByUser == null) {
+ ungroupedNotificationsByUser = new HashMap<>();
+ }
+ mUngroupedNotifications.put(sbn.getUserId(), ungroupedNotificationsByUser);
+ LinkedHashSet<String> notificationsForPackage
+ = ungroupedNotificationsByUser.get(sbn.getPackageName());
+ if (notificationsForPackage == null) {
+ notificationsForPackage = new LinkedHashSet<>();
+ }
+
+ notificationsForPackage.add(sbn.getKey());
+ ungroupedNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage);
+
+ if (notificationsForPackage.size() >= AUTOGROUP_AT_COUNT) {
+ notificationsToGroup.addAll(notificationsForPackage);
+ }
+ }
+ if (notificationsToGroup.size() > 0) {
+ adjustAutogroupingSummary(sbn.getUserId(), sbn.getPackageName(),
+ notificationsToGroup.get(0), true);
+ adjustNotificationBundling(notificationsToGroup, true);
+ }
+ } else {
+ // Grouped, but not by us. Send updates to un-autogroup, if we grouped it.
+ maybeUngroup(sbn, false, sbn.getUserId());
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Failure processing new notification", e);
+ }
+ }
+
+ public void onNotificationRemoved(StatusBarNotification sbn) {
+ try {
+ maybeUngroup(sbn, true, sbn.getUserId());
+ } catch (Exception e) {
+ Slog.e(TAG, "Error processing canceled notification", e);
+ }
+ }
+
+ /**
+ * Un-autogroups notifications that are now grouped by the app. Additionally cancels
+ * autogrouping if the status change of this notification resulted in the loose notification
+ * count being under the limit.
+ */
+ private void maybeUngroup(StatusBarNotification sbn, boolean notificationGone, int userId) {
+ List<String> notificationsToUnAutogroup = new ArrayList<>();
+ boolean removeSummary = false;
+ synchronized (mUngroupedNotifications) {
+ Map<String, LinkedHashSet<String>> ungroupdNotificationsByUser
+ = mUngroupedNotifications.get(sbn.getUserId());
+ if (ungroupdNotificationsByUser == null || ungroupdNotificationsByUser.size() == 0) {
+ return;
+ }
+ LinkedHashSet<String> notificationsForPackage
+ = ungroupdNotificationsByUser.get(sbn.getPackageName());
+ if (notificationsForPackage == null || notificationsForPackage.size() == 0) {
+ return;
+ }
+ if (notificationsForPackage.remove(sbn.getKey())) {
+ if (!notificationGone) {
+ // Add the current notification to the ungrouping list if it still exists.
+ notificationsToUnAutogroup.add(sbn.getKey());
+ }
+ // If the status change of this notification has brought the number of loose
+ // notifications back below the limit, remove the summary and un-autogroup.
+ if (notificationsForPackage.size() == AUTOGROUP_AT_COUNT - 1) {
+ removeSummary = true;
+ for (String key : notificationsForPackage) {
+ notificationsToUnAutogroup.add(key);
+ }
+ }
+ }
+ }
+ if (notificationsToUnAutogroup.size() > 0) {
+ if (removeSummary) {
+ adjustAutogroupingSummary(userId, sbn.getPackageName(), null, false);
+ }
+ adjustNotificationBundling(notificationsToUnAutogroup, false);
+ }
+ }
+
+ private void adjustAutogroupingSummary(int userId, String packageName, String triggeringKey,
+ boolean summaryNeeded) {
+ if (summaryNeeded) {
+ mCallback.addAutoGroupSummary(userId, packageName, triggeringKey);
+ } else {
+ mCallback.removeAutoGroupSummary(userId, packageName);
+ }
+ }
+
+ private void adjustNotificationBundling(List<String> keys, boolean group) {
+ for (String key : keys) {
+ if (DEBUG) Log.i(TAG, "Sending grouping adjustment for: " + key + " group? " + group);
+ if (group) {
+ mCallback.addAutoGroup(key);
+ } else {
+ mCallback.removeAutoGroup(key);
+ }
+ }
+ }
+
+ protected interface Callback {
+ void addAutoGroup(String key);
+ void removeAutoGroup(String key);
+ void addAutoGroupSummary(int userId, String pkg, String triggeringKey);
+ void removeAutoGroupSummary(int user, String pkg);
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 61bf3bd..4e50567 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -311,6 +311,7 @@
private String mSystemNotificationSound;
private SnoozeHelper mSnoozeHelper;
+ private GroupHelper mGroupHelper;
private static class Archive {
final int mBufferSize;
@@ -1017,6 +1018,35 @@
}
}
}, mUserProfiles);
+ mGroupHelper = new GroupHelper(new GroupHelper.Callback() {
+ @Override
+ public void addAutoGroup(String key) {
+ synchronized (mNotificationList) {
+ addAutogroupKeyLocked(key);
+ }
+ mRankingHandler.requestSort();
+ }
+
+ @Override
+ public void removeAutoGroup(String key) {
+ synchronized (mNotificationList) {
+ removeAutogroupKeyLocked(key);
+ }
+ mRankingHandler.requestSort();
+ }
+
+ @Override
+ public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
+ createAutoGroupSummary(userId, pkg, triggeringKey);
+ }
+
+ @Override
+ public void removeAutoGroupSummary(int userId, String pkg) {
+ synchronized (mNotificationList) {
+ clearAutogroupSummaryLocked(userId, pkg);
+ }
+ }
+ });
final File systemDir = new File(Environment.getDataDirectory(), "system");
mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
@@ -2350,7 +2380,6 @@
mRankerServices.checkServiceTokenLocked(token);
applyAdjustmentLocked(adjustment);
}
- maybeAddAutobundleSummary(adjustment);
mRankingHandler.requestSort();
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2369,9 +2398,6 @@
applyAdjustmentLocked(adjustment);
}
}
- for (Adjustment adjustment : adjustments) {
- maybeAddAutobundleSummary(adjustment);
- }
mRankingHandler.requestSort();
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2380,7 +2406,6 @@
};
private void applyAdjustmentLocked(Adjustment adjustment) {
- maybeClearAutobundleSummaryLocked(adjustment);
NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
if (n == null) {
return;
@@ -2390,107 +2415,97 @@
}
if (adjustment.getSignals() != null) {
Bundle.setDefusable(adjustment.getSignals(), true);
- final String autoGroupKey = adjustment.getSignals().getString(
- Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
- if (autoGroupKey == null) {
- EventLogTags.writeNotificationUnautogrouped(adjustment.getKey());
- } else {
- EventLogTags.writeNotificationAutogrouped(adjustment.getKey());
- }
- n.sbn.setOverrideGroupKey(autoGroupKey);
+ // TODO: apply signals
}
}
- // Clears the 'fake' auto-bunding summary.
- private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
- if (adjustment.getSignals() != null) {
- Bundle.setDefusable(adjustment.getSignals(), true);
- if (adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
- && !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
- ArrayMap<String, String> summaries =
- mAutobundledSummaries.get(adjustment.getUser());
- if (summaries != null && summaries.containsKey(adjustment.getPackage())) {
- // Clear summary.
- final NotificationRecord removed = mNotificationsByKey.get(
- summaries.remove(adjustment.getPackage()));
- if (removed != null) {
- mNotificationList.remove(removed);
- cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
- }
- }
+ private void addAutogroupKeyLocked(String key) {
+ NotificationRecord n = mNotificationsByKey.get(key);
+ if (n == null) {
+ return;
+ }
+ n.sbn.setOverrideGroupKey(GroupHelper.AUTOGROUP_KEY);
+ EventLogTags.writeNotificationAutogrouped(key);
+ }
+
+ private void removeAutogroupKeyLocked(String key) {
+ NotificationRecord n = mNotificationsByKey.get(key);
+ if (n == null) {
+ return;
+ }
+ n.sbn.setOverrideGroupKey(null);
+ EventLogTags.writeNotificationUnautogrouped(key);
+ }
+
+ // Clears the 'fake' auto-group summary.
+ private void clearAutogroupSummaryLocked(int userId, String pkg) {
+ ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
+ if (summaries != null && summaries.containsKey(pkg)) {
+ // Clear summary.
+ final NotificationRecord removed = mNotificationsByKey.get(summaries.remove(pkg));
+ if (removed != null) {
+ mNotificationList.remove(removed);
+ cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
}
}
}
// Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
- private void maybeAddAutobundleSummary(Adjustment adjustment) {
- if (adjustment.getSignals() != null) {
- Bundle.setDefusable(adjustment.getSignals(), true);
- if (adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
- final String newAutoBundleKey =
- adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
- int userId = -1;
- NotificationRecord summaryRecord = null;
- synchronized (mNotificationList) {
- NotificationRecord notificationRecord =
- mNotificationsByKey.get(adjustment.getKey());
- if (notificationRecord == null) {
- // The notification could have been cancelled again already. A successive
- // adjustment will post a summary if needed.
- return;
- }
- final StatusBarNotification adjustedSbn = notificationRecord.sbn;
- userId = adjustedSbn.getUser().getIdentifier();
- ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
- if (summaries == null) {
- summaries = new ArrayMap<>();
- }
- mAutobundledSummaries.put(userId, summaries);
- if (!summaries.containsKey(adjustment.getPackage())
- && newAutoBundleKey != null) {
- // Add summary
- final ApplicationInfo appInfo =
- adjustedSbn.getNotification().extras.getParcelable(
- Notification.EXTRA_BUILDER_APPLICATION_INFO);
- final Bundle extras = new Bundle();
- extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
- final Notification summaryNotification =
- new Notification.Builder(getContext()).setSmallIcon(
- adjustedSbn.getNotification().getSmallIcon())
- .setGroupSummary(true)
- .setGroup(newAutoBundleKey)
- .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
- .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
- .setColor(adjustedSbn.getNotification().color)
- .setLocalOnly(true)
- .build();
- summaryNotification.extras.putAll(extras);
- Intent appIntent = getContext().getPackageManager()
- .getLaunchIntentForPackage(adjustment.getPackage());
- if (appIntent != null) {
- summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
- getContext(), 0, appIntent, 0, null,
- UserHandle.of(userId));
- }
- final StatusBarNotification summarySbn =
- new StatusBarNotification(adjustedSbn.getPackageName(),
- adjustedSbn.getOpPkg(),
- Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
- adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
- summaryNotification, adjustedSbn.getUser(),
- newAutoBundleKey,
- System.currentTimeMillis());
- summaryRecord = new NotificationRecord(getContext(), summarySbn,
- mRankingHelper.getNotificationChannel(adjustedSbn.getPackageName(),
- adjustedSbn.getUid(),
- adjustedSbn.getNotification().getNotificationChannel()));
- summaries.put(adjustment.getPackage(), summarySbn.getKey());
- }
- }
- if (summaryRecord != null) {
- mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
- }
+ private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
+ NotificationRecord summaryRecord = null;
+ synchronized (mNotificationList) {
+ NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
+ if (notificationRecord == null) {
+ // The notification could have been cancelled again already. A successive
+ // adjustment will post a summary if needed.
+ return;
}
+ final StatusBarNotification adjustedSbn = notificationRecord.sbn;
+ userId = adjustedSbn.getUser().getIdentifier();
+ ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
+ if (summaries == null) {
+ summaries = new ArrayMap<>();
+ }
+ mAutobundledSummaries.put(userId, summaries);
+ if (!summaries.containsKey(pkg)) {
+ // Add summary
+ final ApplicationInfo appInfo =
+ adjustedSbn.getNotification().extras.getParcelable(
+ Notification.EXTRA_BUILDER_APPLICATION_INFO);
+ final Bundle extras = new Bundle();
+ extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
+ final Notification summaryNotification =
+ new Notification.Builder(getContext()).setSmallIcon(
+ adjustedSbn.getNotification().getSmallIcon())
+ .setGroupSummary(true)
+ .setGroup(GroupHelper.AUTOGROUP_KEY)
+ .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
+ .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
+ .setColor(adjustedSbn.getNotification().color)
+ .setLocalOnly(true)
+ .build();
+ summaryNotification.extras.putAll(extras);
+ Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
+ if (appIntent != null) {
+ summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
+ getContext(), 0, appIntent, 0, null, UserHandle.of(userId));
+ }
+ final StatusBarNotification summarySbn =
+ new StatusBarNotification(adjustedSbn.getPackageName(),
+ adjustedSbn.getOpPkg(), Integer.MAX_VALUE,
+ GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
+ adjustedSbn.getInitialPid(), summaryNotification,
+ adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
+ System.currentTimeMillis());
+ summaryRecord = new NotificationRecord(getContext(), summarySbn,
+ mRankingHelper.getNotificationChannel(adjustedSbn.getPackageName(),
+ adjustedSbn.getUid(),
+ adjustedSbn.getNotification().getNotificationChannel()));
+ summaries.put(pkg, summarySbn.getKey());
+ }
+ }
+ if (summaryRecord != null) {
+ mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
}
}
@@ -2690,6 +2705,12 @@
(r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
mRankingHelper.sort(mNotificationList);
mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mGroupHelper.onNotificationPosted(sbn);
+ }
+ });
}
}
};
@@ -2914,10 +2935,22 @@
if (notification.getSmallIcon() != null) {
StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
mListeners.notifyPostedLocked(n, oldSbn);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mGroupHelper.onNotificationPosted(n);
+ }
+ });
} else {
Slog.e(TAG, "Not posting notification without small icon: " + notification);
if (old != null && !old.isCanceled) {
mListeners.notifyRemovedLocked(n);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mGroupHelper.onNotificationRemoved(n);
+ }
+ });
}
// ATTENTION: in a future release we will bail out here
// so that we do not play sounds, show lights, etc. for invalid
@@ -3525,6 +3558,12 @@
if (r.getNotification().getSmallIcon() != null) {
r.isCanceled = true;
mListeners.notifyRemovedLocked(r.sbn);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mGroupHelper.onNotificationRemoved(r.sbn);
+ }
+ });
}
final String canceledKey = r.getKey();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e48d6fb..974d95d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -466,11 +466,11 @@
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
/**
- * If VENDOR_OVERLAY_SKU_PROPERTY is set, search for runtime resource overlay APKs also in
- * VENDOR_OVERLAY_DIR/<value of VENDOR_OVERLAY_SKU_PROPERTY> in addition to
+ * If VENDOR_OVERLAY_THEME_PROPERTY is set, search for runtime resource overlay APKs also in
+ * VENDOR_OVERLAY_DIR/<value of VENDOR_OVERLAY_THEME_PROPERTY> in addition to
* VENDOR_OVERLAY_DIR.
*/
- private static final String VENDOR_OVERLAY_SKU_PROPERTY = "ro.boot.vendor.overlay.sku";
+ private static final String VENDOR_OVERLAY_THEME_PROPERTY = "ro.boot.vendor.overlay.theme";
private static int DEFAULT_EPHEMERAL_HASH_PREFIX_MASK = 0xFFFFF000;
private static int DEFAULT_EPHEMERAL_HASH_PREFIX_COUNT = 5;
@@ -2288,9 +2288,9 @@
// Collect vendor overlay packages. (Do this before scanning any apps.)
// For security and version matching reason, only consider
// overlay packages if they reside in the right directory.
- String overlaySkuDir = SystemProperties.get(VENDOR_OVERLAY_SKU_PROPERTY);
- if (!overlaySkuDir.isEmpty()) {
- scanDirTracedLI(new File(VENDOR_OVERLAY_DIR, overlaySkuDir), mDefParseFlags
+ String overlayThemeDir = SystemProperties.get(VENDOR_OVERLAY_THEME_PROPERTY);
+ if (!overlayThemeDir.isEmpty()) {
+ scanDirTracedLI(new File(VENDOR_OVERLAY_DIR, overlayThemeDir), mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b8b16d5..6e5c025 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5408,18 +5408,15 @@
}
} else if (mDismissKeyguard != DISMISS_KEYGUARD_NONE) {
mKeyguardHidden = false;
- boolean willDismiss = false;
+ boolean dismissKeyguard = false;
+ final boolean trusted = mKeyguardDelegate.isTrusted();
if (mDismissKeyguard == DISMISS_KEYGUARD_START) {
- final boolean trusted = mKeyguardDelegate.isTrusted();
- willDismiss = trusted && mKeyguardOccluded && mKeyguardDelegate != null
- && mKeyguardDelegate.isShowing();
+ final boolean willDismiss = trusted && mKeyguardOccluded
+ && mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
if (willDismiss) {
mCurrentlyDismissingKeyguard = true;
}
-
- // Only launch the next keyguard unlock window once per window.
- mHandler.post(() -> mKeyguardDelegate.dismiss(
- trusted /* allowWhileOccluded */));
+ dismissKeyguard = true;
}
// If we are currently dismissing Keyguard, there is no need to unocclude it.
@@ -5430,6 +5427,12 @@
| FINISH_LAYOUT_REDO_WALLPAPER;
}
}
+
+ if (dismissKeyguard) {
+ // Only launch the next keyguard unlock window once per window.
+ mHandler.post(() -> mKeyguardDelegate.dismiss(
+ trusted /* allowWhileOccluded */));
+ }
} else {
mWinDismissingKeyguard = null;
mSecureDismissingKeyguard = false;
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index bb76449..9f0f11a 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -36,6 +36,7 @@
import android.util.Log;
import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewZygote;
import com.android.internal.util.XmlUtils;
@@ -268,6 +269,11 @@
return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
}
+ @Override
+ public void setMultiprocessEnabled(boolean enabled) {
+ WebViewZygote.setMultiprocessEnabled(enabled);
+ }
+
// flags declaring we want extra info from the package manager for webview providers
private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
| PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 7bde37a..7c934fc 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -48,4 +48,6 @@
public boolean systemIsDebuggable();
public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
throws NameNotFoundException;
+
+ public void setMultiprocessEnabled(boolean enabled);
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index b69a8c1..57cd768 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -20,7 +20,12 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Base64;
import android.util.Slog;
import android.webkit.WebViewFactory;
@@ -73,6 +78,7 @@
private SystemInterface mSystemInterface;
private WebViewUpdater mWebViewUpdater;
+ private SettingsObserver mSettingsObserver;
private Context mContext;
public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
@@ -92,6 +98,10 @@
void prepareWebViewInSystemServer() {
updateFallbackStateOnBoot();
mWebViewUpdater.prepareWebViewInSystemServer();
+
+ // Register for changes in the multiprocess developer option. This has to be done
+ // here, since the update service gets created before the ContentResolver service.
+ mSettingsObserver = new SettingsObserver();
}
private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
@@ -709,4 +719,41 @@
& ApplicationInfo.PRIVATE_FLAG_HIDDEN) == 0));
}
+ /**
+ * Watches for changes in the WEBVIEW_MULTIPROCESS setting and lets
+ * the WebViewZygote know, so it can start or stop the zygote process
+ * appropriately.
+ */
+ private class SettingsObserver extends ContentObserver {
+ private final ContentResolver mResolver;
+
+ SettingsObserver() {
+ super(new Handler());
+
+ mResolver = mContext.getContentResolver();
+ mResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.WEBVIEW_MULTIPROCESS),
+ false, this);
+
+ // Push the current value of the setting immediately.
+ notifyZygote();
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ notifyZygote();
+ }
+
+ private void notifyZygote() {
+ boolean enableMultiprocess = false;
+
+ try {
+ enableMultiprocess = Settings.Global.getInt(mResolver,
+ Settings.Global.WEBVIEW_MULTIPROCESS) == 1;
+ } catch (Settings.SettingNotFoundException ex) {
+ }
+
+ mSystemInterface.setMultiprocessEnabled(enableMultiprocess);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 429cb2d..e774259 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -31,7 +31,6 @@
import android.view.Choreographer;
import android.view.Display;
import android.view.SurfaceControl;
-import android.view.WindowManagerPolicy;
import android.view.animation.Animation;
import android.view.animation.Transformation;
@@ -394,7 +393,7 @@
}
if (mService.mInputMethodTarget != null
&& mService.mInputMethodTarget.mAppToken == mAppToken) {
- mService.moveInputMethodWindowsIfNeededLocked(true);
+ mAppToken.getDisplayContent().moveInputMethodWindowsIfNeeded(true);
}
if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ee7c6d2..d46b535 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -425,8 +425,7 @@
}
if (destroyedSomething) {
final DisplayContent dc = getDisplayContent();
- mService.mLayersController.assignLayersLocked(dc.getWindowList());
- dc.setLayoutNeeded();
+ dc.assignWindowLayers(true /*setLayoutNeeded*/);
}
}
@@ -950,7 +949,7 @@
mService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
- mService.getDefaultDisplayContentLocked().setLayoutNeeded();
+ getDisplayContent().setLayoutNeeded();
mService.mWindowPlacerLocked.performSurfacePlacement();
Binder.restoreCallingIdentity(origId);
return true;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3681123..7cd9971 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -32,27 +32,61 @@
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_TOP;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE;
+import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
+import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
+import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+import static com.android.server.wm.WindowAnimator.KEYGUARD_ANIMATING_OUT;
+import static com.android.server.wm.WindowAnimator.KEYGUARD_ANIM_TIMEOUT_MS;
+import static com.android.server.wm.WindowAnimator.KEYGUARD_NOT_SHOWN;
+import static com.android.server.wm.WindowAnimator.KEYGUARD_SHOWN;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
import static com.android.server.wm.WindowManagerService.dipToPixel;
import static com.android.server.wm.WindowManagerService.localLOGV;
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
+import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
+import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
+import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED;
+import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
import android.annotation.NonNull;
import android.app.ActivityManager.StackId;
@@ -66,13 +100,20 @@
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.IWindow;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
import com.android.internal.util.FastPrintWriter;
+import com.android.internal.view.IInputMethodClient;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -92,6 +133,7 @@
* WindowManagerService.mWindowMap.
*/
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer> {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
/** Unique identifier of this stack. */
private final int mDisplayId;
@@ -171,13 +213,24 @@
// the display's direct children should be allowed.
private boolean mRemovingDisplay = false;
+ private final WindowLayersController mLayersController;
+ final WallpaperController mWallpaperController;
+ int mInputMethodAnimLayerAdjustment;
+
/**
* @param display May not be null.
* @param service You know.
+ * @param layersController window layer controller used to assign layer to the windows on this
+ * display.
+ * @param wallpaperController wallpaper windows controller used to adjust the positioning of the
+ * wallpaper windows in the window list.
*/
- DisplayContent(Display display, WindowManagerService service) {
+ DisplayContent(Display display, WindowManagerService service,
+ WindowLayersController layersController, WallpaperController wallpaperController) {
mDisplay = display;
mDisplayId = display.getDisplayId();
+ mLayersController = layersController;
+ mWallpaperController = wallpaperController;
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -325,33 +378,79 @@
@Override
int getOrientation() {
- if (mService.isStackVisibleLocked(DOCKED_STACK_ID)
- || mService.isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
- // Apps and their containers are not allowed to specify an orientation while the docked
- // or freeform stack is visible...except for the home stack/task if the docked stack is
- // minimized and it actually set something.
- if (mHomeStack != null && mHomeStack.isVisible()
- && mDividerControllerLocked.isMinimizedDock()) {
- final int orientation = mHomeStack.getOrientation();
- if (orientation != SCREEN_ORIENTATION_UNSET) {
- return orientation;
- }
+ final WindowManagerPolicy policy = mService.mPolicy;
+
+ // TODO: All the logic before the last return statement in this method should really go in
+ // #NonAppWindowContainer.getOrientation() since it is trying to decide orientation based
+ // on non-app windows. But, we can not do that until the window list is always correct in
+ // terms of z-ordering based on layers.
+ if (mService.mDisplayFrozen) {
+ if (mService.mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+ "Display is frozen, return " + mService.mLastWindowForcedOrientation);
+ // If the display is frozen, some activities may be in the middle of restarting, and
+ // thus have removed their old window. If the window has the flag to hide the lock
+ // screen, then the lock screen can re-appear and inflict its own orientation on us.
+ // Keep the orientation stable until this all settles down.
+ return mService.mLastWindowForcedOrientation;
+ } else if (policy.isKeyguardLocked()) {
+ // Use the last orientation the while the display is frozen with the keyguard
+ // locked. This could be the keyguard forced orientation or from a SHOW_WHEN_LOCKED
+ // window. We don't want to check the show when locked window directly though as
+ // things aren't stable while the display is frozen, for example the window could be
+ // momentarily unavailable due to activity relaunch.
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display is frozen while keyguard locked, "
+ + "return " + mService.mLastOrientation);
+ return mService.mLastOrientation;
}
- return SCREEN_ORIENTATION_UNSPECIFIED;
+ } else {
+ for (int pos = mWindows.size() - 1; pos >= 0; --pos) {
+ final WindowState win = mWindows.get(pos);
+ if (win.mAppToken != null) {
+ // We hit an application window. so the orientation will be determined by the
+ // app window. No point in continuing further.
+ break;
+ }
+ if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) {
+ continue;
+ }
+ int req = win.mAttrs.screenOrientation;
+ if(req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND) {
+ continue;
+ }
+
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req);
+ if (policy.isKeyguardHostWindow(win.mAttrs)) {
+ mService.mLastKeyguardForcedOrientation = req;
+ }
+ return (mService.mLastWindowForcedOrientation = req);
+ }
+ mService.mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+ if (policy.isKeyguardLocked()) {
+ // The screen is locked and no top system window is requesting an orientation.
+ // Return either the orientation of the show-when-locked app (if there is any) or
+ // the orientation of the keyguard. No point in searching from the rest of apps.
+ WindowState winShowWhenLocked = (WindowState) policy.getWinShowWhenLockedLw();
+ AppWindowToken appShowWhenLocked = winShowWhenLocked == null
+ ? null : winShowWhenLocked.mAppToken;
+ if (appShowWhenLocked != null) {
+ int req = appShowWhenLocked.getOrientation();
+ if (req == SCREEN_ORIENTATION_BEHIND) {
+ req = mService.mLastKeyguardForcedOrientation;
+ }
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Done at " + appShowWhenLocked
+ + " -- show when locked, return " + req);
+ return req;
+ }
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+ "No one is requesting an orientation when the screen is locked");
+ return mService.mLastKeyguardForcedOrientation;
+ }
}
- final int orientation = super.getOrientation();
- if (orientation != SCREEN_ORIENTATION_UNSET && orientation != SCREEN_ORIENTATION_BEHIND) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "App is requesting an orientation, return " + orientation);
- return orientation;
- }
-
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "No app is requesting an orientation, return " + mService.mLastOrientation);
- // The next app has not been requested to be visible, so we keep the current orientation
- // to prevent freezing/unfreezing the display too early.
- return mService.mLastOrientation;
+ // Top system windows are not requesting an orientation. Start searching from apps.
+ return mTaskStackContainers.getOrientation();
}
void updateDisplayInfo() {
@@ -691,6 +790,39 @@
}
}
+ void setInputMethodAnimLayerAdjustment(int adj) {
+ if (DEBUG_LAYERS) Slog.v(TAG_WM, "Setting im layer adj to " + adj);
+ mInputMethodAnimLayerAdjustment = adj;
+ final WindowState imw = mService.mInputMethodWindow;
+ if (imw != null) {
+ imw.adjustAnimLayer(adj);
+ }
+ for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) {
+ final WindowState dialog = mService.mInputMethodDialogs.get(i);
+ // TODO: This and other places setting mAnimLayer can probably use WS.adjustAnimLayer,
+ // but need to make sure we are not setting things twice for child windows that are
+ // already in the list.
+ dialog.mWinAnimator.mAnimLayer = dialog.mLayer + adj;
+ if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
+ + " anim layer: " + dialog.mWinAnimator.mAnimLayer);
+ }
+ }
+
+ /**
+ * If a window that has an animation specifying a colored background and the current wallpaper
+ * is visible, then the color goes *below* the wallpaper so we don't cause the wallpaper to
+ * suddenly disappear.
+ */
+ int getLayerForAnimationBackground(WindowStateAnimator winAnimator) {
+ for (int i = mWindows.size() - 1; i >= 0; --i) {
+ final WindowState win = mWindows.get(i);
+ if (win.mIsWallpaper && win.isVisibleNow()) {
+ return win.mWinAnimator.mAnimLayer;
+ }
+ }
+ return winAnimator.mAnimLayer;
+ }
+
void prepareFreezingTaskBounds() {
for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.get(stackNdx);
@@ -798,6 +930,11 @@
mDimLayerController.dump(prefix + " ", pw);
pw.println();
mDividerControllerLocked.dump(prefix + " ", pw);
+
+ if (mInputMethodAnimLayerAdjustment != 0) {
+ pw.println(subPrefix
+ + "mInputMethodAnimLayerAdjustment=" + mInputMethodAnimLayerAdjustment);
+ }
}
@Override
@@ -1071,6 +1208,36 @@
mWindows.add(index, win);
}
+ boolean removeFromWindowList(WindowState win) {
+ return mWindows.remove(win);
+ }
+
+ private int removeWindowAndChildrenFromWindowList(WindowState win, int interestingPos) {
+ final WindowList windows = getWindowList();
+ int wpos = windows.indexOf(win);
+ if (wpos < 0) {
+ return interestingPos;
+ }
+
+ if (wpos < interestingPos) interestingPos--;
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing at " + wpos + ": " + this);
+ windows.remove(wpos);
+ mService.mWindowsChanged = true;
+ int childWinCount = win.mChildren.size();
+ while (childWinCount > 0) {
+ childWinCount--;
+ final WindowState cw = win.mChildren.get(childWinCount);
+ int cpos = windows.indexOf(cw);
+ if (cpos >= 0) {
+ if (cpos < interestingPos) interestingPos--;
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM,
+ "Temp removing child at " + cpos + ": " + cw);
+ windows.remove(cpos);
+ }
+ }
+ return interestingPos;
+ }
+
void addChildWindowToWindowList(WindowState win) {
final WindowState parentWindow = win.getParentWindow();
@@ -1113,6 +1280,20 @@
}
}
+ /** Updates the layer assignment of windows on this display. */
+ void assignWindowLayers(boolean setLayoutNeeded) {
+ mLayersController.assignWindowLayers(mWindows);
+ if (setLayoutNeeded) {
+ setLayoutNeeded();
+ }
+ }
+
+ void adjustWallpaperWindows() {
+ if (mWallpaperController.adjustWallpaperWindows(mWindows)) {
+ assignWindowLayers(true /*setLayoutNeeded*/);
+ }
+ }
+
/**
* Z-orders the display window list so that:
* <ul>
@@ -1218,6 +1399,371 @@
return windowList;
}
+ private void reAddToWindowList(WindowState win) {
+ win.mToken.addWindow(win);
+ // This is a hack to get all of the child windows added as well at the right position. Child
+ // windows should be rare and this case should be rare, so it shouldn't be that big a deal.
+ int wpos = mWindows.indexOf(win);
+ if (wpos >= 0) {
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "ReAdd removing from " + wpos + ": " + win);
+ mWindows.remove(wpos);
+ mService.mWindowsChanged = true;
+ win.reAddWindow(wpos);
+ }
+ }
+
+ void moveInputMethodDialogs(int pos) {
+ ArrayList<WindowState> dialogs = mService.mInputMethodDialogs;
+
+ final int N = dialogs.size();
+ if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Removing " + N + " dialogs w/pos=" + pos);
+ for (int i = 0; i < N; i++) {
+ pos = removeWindowAndChildrenFromWindowList(dialogs.get(i), pos);
+ }
+ if (DEBUG_INPUT_METHOD) {
+ Slog.v(TAG_WM, "Window list w/pos=" + pos);
+ logWindowList(mWindows, " ");
+ }
+
+ WindowState ime = mService.mInputMethodWindow;
+ if (pos >= 0) {
+ // Skip windows owned by the input method.
+ if (ime != null) {
+ while (pos < mWindows.size()) {
+ WindowState wp = mWindows.get(pos);
+ if (wp == ime || wp.getParentWindow() == ime) {
+ pos++;
+ continue;
+ }
+ break;
+ }
+ }
+ if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Adding " + N + " dialogs at pos=" + pos);
+ for (int i=0; i<N; i++) {
+ WindowState win = dialogs.get(i);
+ pos = win.reAddWindow(pos);
+ }
+ if (DEBUG_INPUT_METHOD) {
+ Slog.v(TAG_WM, "Final window list:");
+ logWindowList(mWindows, " ");
+ }
+ return;
+ }
+ for (int i=0; i<N; i++) {
+ WindowState win = dialogs.get(i);
+ reAddToWindowList(win);
+ if (DEBUG_INPUT_METHOD) {
+ Slog.v(TAG_WM, "No IM target, final list:");
+ logWindowList(mWindows, " ");
+ }
+ }
+ }
+
+ boolean moveInputMethodWindowsIfNeeded(boolean needAssignLayers) {
+ final WindowState imWin = mService.mInputMethodWindow;
+ final int DN = mService.mInputMethodDialogs.size();
+ if (imWin == null && DN == 0) {
+ return false;
+ }
+
+ // TODO(multidisplay): IMEs are only supported on the default display.
+ WindowList windows = mWindows;
+
+ int imPos = findDesiredInputMethodWindowIndex(true);
+ if (imPos >= 0) {
+ // In this case, the input method windows are to be placed
+ // immediately above the window they are targeting.
+
+ // First check to see if the input method windows are already
+ // located here, and contiguous.
+ final int N = windows.size();
+ final WindowState firstImWin = imPos < N ? windows.get(imPos) : null;
+
+ // Figure out the actual input method window that should be
+ // at the bottom of their stack.
+ WindowState baseImWin = imWin != null ? imWin : mService.mInputMethodDialogs.get(0);
+ final WindowState cw = baseImWin.getBottomChild();
+ if (cw != null && cw.mSubLayer < 0) {
+ baseImWin = cw;
+ }
+
+ if (firstImWin == baseImWin) {
+ // The windows haven't moved... but are they still contiguous?
+ // First find the top IM window.
+ int pos = imPos+1;
+ while (pos < N) {
+ if (!(windows.get(pos)).mIsImWindow) {
+ break;
+ }
+ pos++;
+ }
+ pos++;
+ // Now there should be no more input method windows above.
+ while (pos < N) {
+ if ((windows.get(pos)).mIsImWindow) {
+ break;
+ }
+ pos++;
+ }
+ if (pos >= N) {
+ return false;
+ }
+ }
+
+ if (imWin != null) {
+ if (DEBUG_INPUT_METHOD) {
+ Slog.v(TAG_WM, "Moving IM from " + imPos);
+ logWindowList(windows, " ");
+ }
+ imPos = removeWindowAndChildrenFromWindowList(imWin, imPos);
+ if (DEBUG_INPUT_METHOD) {
+ Slog.v(TAG_WM, "List after removing with new pos " + imPos + ":");
+ logWindowList(windows, " ");
+ }
+ imWin.reAddWindow(imPos);
+ if (DEBUG_INPUT_METHOD) {
+ Slog.v(TAG_WM, "List after moving IM to " + imPos + ":");
+ logWindowList(windows, " ");
+ }
+ if (DN > 0) moveInputMethodDialogs(imPos+1);
+ } else {
+ moveInputMethodDialogs(imPos);
+ }
+
+ } else {
+ // In this case, the input method windows go in a fixed layer,
+ // because they aren't currently associated with a focus window.
+
+ if (imWin != null) {
+ if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Moving IM from " + imPos);
+ removeWindowAndChildrenFromWindowList(imWin, 0);
+ reAddToWindowList(imWin);
+ if (DEBUG_INPUT_METHOD) {
+ Slog.v(TAG_WM, "List with no IM target:");
+ logWindowList(windows, " ");
+ }
+ if (DN > 0) moveInputMethodDialogs(-1);
+ } else {
+ moveInputMethodDialogs(-1);
+ }
+
+ }
+
+ if (needAssignLayers) {
+ assignWindowLayers(false /* setLayoutNeeded */);
+ }
+
+ return true;
+ }
+
+ /**
+ * Dig through the WindowStates and find the one that the Input Method will target.
+ * @param willMove
+ * @return The index+1 in mWindows of the discovered target.
+ */
+ int findDesiredInputMethodWindowIndex(boolean willMove) {
+ // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the
+ // same display. Or even when the current IME/target are not on the same screen as the next
+ // IME/target. For now only look for input windows on the main screen.
+ final WindowList windows = getWindowList();
+ WindowState w = null;
+ int i;
+ for (i = windows.size() - 1; i >= 0; --i) {
+ WindowState win = windows.get(i);
+
+ if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG_WM, "Checking window @" + i
+ + " " + win + " fl=0x" + Integer.toHexString(win.mAttrs.flags));
+ if (canBeImeTarget(win)) {
+ w = win;
+ //Slog.i(TAG_WM, "Putting input method here!");
+
+ // Yet more tricksyness! If this window is a "starting" window, we do actually want
+ // to be on top of it, but it is not -really- where input will go. So if the caller
+ // is not actually looking to move the IME, look down below for a real window to
+ // target...
+ if (!willMove && w.mAttrs.type == TYPE_APPLICATION_STARTING && i > 0) {
+ WindowState wb = windows.get(i-1);
+ if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) {
+ i--;
+ w = wb;
+ }
+ }
+ break;
+ }
+ }
+
+ // Now w is either mWindows[0] or an IME (or null if mWindows is empty).
+
+ if (DEBUG_INPUT_METHOD && willMove) Slog.v(TAG_WM, "Proposed new IME target: " + w);
+
+ // Now, a special case -- if the last target's window is in the process of exiting, and is
+ // above the new target, keep on the last target to avoid flicker. Consider for example a
+ // Dialog with the IME shown: when the Dialog is dismissed, we want to keep the IME above it
+ // until it is completely gone so it doesn't drop behind the dialog or its full-screen
+ // scrim.
+ final WindowState curTarget = mService.mInputMethodTarget;
+ if (curTarget != null
+ && curTarget.isDisplayedLw()
+ && curTarget.isClosing()
+ && (w == null || curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer)) {
+ if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Current target higher, not changing");
+ return windows.indexOf(curTarget) + 1;
+ }
+
+ if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Desired input method target="
+ + w + " willMove=" + willMove);
+
+ if (willMove && w != null) {
+ AppWindowToken token = curTarget == null ? null : curTarget.mAppToken;
+ if (token != null) {
+
+ // Now some fun for dealing with window animations that modify the Z order. We need
+ // to look at all windows below the current target that are in this app, finding the
+ // highest visible one in layering.
+ WindowState highestTarget = null;
+ int highestPos = 0;
+ if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) {
+ WindowList curWindows = token.getDisplayContent().getWindowList();
+ int pos = curWindows.indexOf(curTarget);
+ while (pos >= 0) {
+ WindowState win = curWindows.get(pos);
+ if (win.mAppToken != token) {
+ break;
+ }
+ if (!win.mRemoved) {
+ if (highestTarget == null || win.mWinAnimator.mAnimLayer >
+ highestTarget.mWinAnimator.mAnimLayer) {
+ highestTarget = win;
+ highestPos = pos;
+ }
+ }
+ pos--;
+ }
+ }
+
+ if (highestTarget != null) {
+ final AppTransition appTransition = mService.mAppTransition;
+ if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, appTransition + " " + highestTarget
+ + " animating=" + highestTarget.mWinAnimator.isAnimationSet()
+ + " layer=" + highestTarget.mWinAnimator.mAnimLayer
+ + " new layer=" + w.mWinAnimator.mAnimLayer);
+
+ if (appTransition.isTransitionSet()) {
+ // If we are currently setting up for an animation, hold everything until we
+ // can find out what will happen.
+ mService.mInputMethodTargetWaitingAnim = true;
+ mService.mInputMethodTarget = highestTarget;
+ return highestPos + 1;
+ } else if (highestTarget.mWinAnimator.isAnimationSet() &&
+ highestTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer) {
+ // If the window we are currently targeting is involved with an animation,
+ // and it is on top of the next target we will be over, then hold off on
+ // moving until that is done.
+ mService.mInputMethodTargetWaitingAnim = true;
+ mService.mInputMethodTarget = highestTarget;
+ return highestPos + 1;
+ }
+ }
+ }
+ }
+
+ //Slog.i(TAG_WM, "Placing input method @" + (i+1));
+ if (w != null) {
+ if (willMove) {
+ if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget + " to "
+ + w + (SHOW_STACK_CRAWLS ? " Callers=" + Debug.getCallers(4) : ""));
+ mService.mInputMethodTarget = w;
+ mService.mInputMethodTargetWaitingAnim = false;
+ if (w.mAppToken != null) {
+ setInputMethodAnimLayerAdjustment(
+ w.mAppToken.mAppAnimator.animLayerAdjustment);
+ } else {
+ setInputMethodAnimLayerAdjustment(0);
+ }
+ }
+
+ // If the docked divider is visible, we still need to go through this whole excercise to
+ // find the appropriate input method target (used for animations and dialog
+ // adjustments), but for purposes of Z ordering we simply wish to place it above the
+ // docked divider. Unless it is already above the divider.
+ final WindowState dockedDivider = mDividerControllerLocked.getWindow();
+ if (dockedDivider != null && dockedDivider.isVisibleLw()) {
+ int dividerIndex = windows.indexOf(dockedDivider);
+ if (dividerIndex > 0 && dividerIndex > i) {
+ return dividerIndex + 1;
+ }
+ }
+ return i+1;
+ }
+ if (willMove) {
+ if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget
+ + " to null." + (SHOW_STACK_CRAWLS ? " Callers=" + Debug.getCallers(4) : ""));
+ mService.mInputMethodTarget = null;
+ setInputMethodAnimLayerAdjustment(0);
+ }
+ return -1;
+ }
+
+ private static boolean canBeImeTarget(WindowState w) {
+ final int fl = w.mAttrs.flags & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM);
+ final int type = w.mAttrs.type;
+
+ if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM)
+ && type != TYPE_APPLICATION_STARTING) {
+ return false;
+ }
+
+ if (DEBUG_INPUT_METHOD) {
+ Slog.i(TAG_WM, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding());
+ if (!w.isVisibleOrAdding()) {
+ Slog.i(TAG_WM, " mSurfaceController=" + w.mWinAnimator.mSurfaceController
+ + " relayoutCalled=" + w.mRelayoutCalled
+ + " viewVis=" + w.mViewVisibility
+ + " policyVis=" + w.mPolicyVisibility
+ + " policyVisAfterAnim=" + w.mPolicyVisibilityAfterAnim
+ + " parentHidden=" + w.isParentWindowHidden()
+ + " exiting=" + w.mAnimatingExit + " destroying=" + w.mDestroying);
+ if (w.mAppToken != null) {
+ Slog.i(TAG_WM, " mAppToken.hiddenRequested=" + w.mAppToken.hiddenRequested);
+ }
+ }
+ }
+ return w.isVisibleOrAdding();
+ }
+
+ private void logWindowList(final WindowList windows, String prefix) {
+ int N = windows.size();
+ while (N > 0) {
+ N--;
+ Slog.v(TAG_WM, prefix + "#" + N + ": " + windows.get(N));
+ }
+ }
+
+ boolean getNeedsMenu(WindowState win, WindowManagerPolicy.WindowState bottom) {
+ int index = -1;
+ WindowList windows = getWindowList();
+ while (true) {
+ if (win.mAttrs.needsMenuKey != NEEDS_MENU_UNSET) {
+ return win.mAttrs.needsMenuKey == NEEDS_MENU_SET_TRUE;
+ }
+ // If we reached the bottom of the range of windows we are considering,
+ // assume no menu is needed.
+ if (win == bottom) {
+ return false;
+ }
+ // The current window hasn't specified whether menu key is needed; look behind it.
+ // First, we may need to determine the starting position.
+ if (index < 0) {
+ index = windows.indexOf(win);
+ }
+ index--;
+ if (index < 0) {
+ return false;
+ }
+ win = windows.get(index);
+ }
+ }
+
void setLayoutNeeded() {
if (DEBUG_LAYOUT) Slog.w(TAG_WM, "setLayoutNeeded: callers=" + Debug.getCallers(3));
mLayoutNeeded = true;
@@ -1341,6 +1887,14 @@
}
}
+ void dumpWindowAnimators(PrintWriter pw, String subPrefix) {
+ final int count = mWindows.size();
+ for (int j = 0; j < count; j++) {
+ final WindowStateAnimator wAnim = mWindows.get(j).mWinAnimator;
+ pw.println(subPrefix + "Window #" + j + ": " + wAnim);
+ }
+ }
+
void enableSurfaceTrace(FileDescriptor fd) {
for (int i = mWindows.size() - 1; i >= 0; i--) {
final WindowState win = mWindows.get(i);
@@ -1355,6 +1909,531 @@
}
}
+ boolean checkWaitingForWindows() {
+
+ boolean haveBootMsg = false;
+ boolean haveApp = false;
+ // if the wallpaper service is disabled on the device, we're never going to have
+ // wallpaper, don't bother waiting for it
+ boolean haveWallpaper = false;
+ boolean wallpaperEnabled = mService.mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableWallpaperService)
+ && !mService.mOnlyCore;
+ boolean haveKeyguard = true;
+ final int count = mWindows.size();
+ for (int i = 0; i < count; i++) {
+ final WindowState w = mWindows.get(i);
+ if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+ return true;
+ }
+ if (w.isDrawnLw()) {
+ if (w.mAttrs.type == TYPE_BOOT_PROGRESS) {
+ haveBootMsg = true;
+ } else if (w.mAttrs.type == TYPE_APPLICATION
+ || w.mAttrs.type == TYPE_DRAWN_APPLICATION) {
+ haveApp = true;
+ } else if (w.mAttrs.type == TYPE_WALLPAPER) {
+ haveWallpaper = true;
+ } else if (w.mAttrs.type == TYPE_STATUS_BAR) {
+ haveKeyguard = mService.mPolicy.isKeyguardDrawnLw();
+ }
+ }
+ }
+
+ if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM,
+ "******** booted=" + mService.mSystemBooted
+ + " msg=" + mService.mShowingBootMessages
+ + " haveBoot=" + haveBootMsg + " haveApp=" + haveApp
+ + " haveWall=" + haveWallpaper + " wallEnabled=" + wallpaperEnabled
+ + " haveKeyguard=" + haveKeyguard);
+
+ // If we are turning on the screen to show the boot message, don't do it until the boot
+ // message is actually displayed.
+ if (!mService.mSystemBooted && !haveBootMsg) {
+ return true;
+ }
+
+ // If we are turning on the screen after the boot is completed normally, don't do so until
+ // we have the application and wallpaper.
+ if (mService.mSystemBooted && ((!haveApp && !haveKeyguard) ||
+ (wallpaperEnabled && !haveWallpaper))) {
+ return true;
+ }
+
+ return false;
+ }
+
+ void updateWindowsForAnimator(WindowAnimator animator) {
+ final WindowManagerPolicy policy = animator.mPolicy;
+ final int keyguardGoingAwayFlags = animator.mKeyguardGoingAwayFlags;
+ final boolean keyguardGoingAwayToShade =
+ (keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0;
+ final boolean keyguardGoingAwayNoAnimation =
+ (keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0;
+ final boolean keyguardGoingAwayWithWallpaper =
+ (keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0;
+
+ if (animator.mKeyguardGoingAway) {
+ for (int i = mWindows.size() - 1; i >= 0; i--) {
+ WindowState win = mWindows.get(i);
+ if (!policy.isKeyguardHostWindow(win.mAttrs)) {
+ continue;
+ }
+ final WindowStateAnimator winAnimator = win.mWinAnimator;
+ if (policy.isKeyguardShowingAndNotOccluded()) {
+ if (!winAnimator.mAnimating) {
+ if (DEBUG_KEYGUARD) Slog.d(TAG,
+ "updateWindowsForAnimator: creating delay animation");
+
+ // Create a new animation to delay until keyguard is gone on its own.
+ winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f);
+ winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS);
+ winAnimator.mAnimationIsEntrance = false;
+ winAnimator.mAnimationStartTime = -1;
+ winAnimator.mKeyguardGoingAwayAnimation = true;
+ winAnimator.mKeyguardGoingAwayWithWallpaper
+ = keyguardGoingAwayWithWallpaper;
+ }
+ } else {
+ if (DEBUG_KEYGUARD) Slog.d(TAG,
+ "updateWindowsForAnimator: StatusBar is no longer keyguard");
+ animator.mKeyguardGoingAway = false;
+ winAnimator.clearAnimation();
+ }
+ break;
+ }
+ }
+
+ animator.mForceHiding = KEYGUARD_NOT_SHOWN;
+
+ boolean wallpaperInUnForceHiding = false;
+ boolean startingInUnForceHiding = false;
+ ArrayList<WindowStateAnimator> unForceHiding = null;
+ WindowState wallpaper = null;
+ final WallpaperController wallpaperController = mWallpaperController;
+ for (int i = mWindows.size() - 1; i >= 0; i--) {
+ WindowState win = mWindows.get(i);
+ WindowStateAnimator winAnimator = win.mWinAnimator;
+ final int flags = win.mAttrs.flags;
+ boolean canBeForceHidden = policy.canBeForceHidden(win, win.mAttrs);
+ boolean shouldBeForceHidden = animator.shouldForceHide(win);
+ if (winAnimator.hasSurface()) {
+ final boolean wasAnimating = winAnimator.mWasAnimating;
+ final boolean nowAnimating = winAnimator.stepAnimationLocked(animator.mCurrentTime);
+ winAnimator.mWasAnimating = nowAnimating;
+ animator.orAnimating(nowAnimating);
+
+ if (DEBUG_WALLPAPER) Slog.v(TAG,
+ win + ": wasAnimating=" + wasAnimating + ", nowAnimating=" + nowAnimating);
+
+ if (wasAnimating && !winAnimator.mAnimating
+ && wallpaperController.isWallpaperTarget(win)) {
+ animator.mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
+ pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ if (DEBUG_LAYOUT_REPEATS) {
+ mService.mWindowPlacerLocked.debugLayoutRepeats(
+ "updateWindowsAndWallpaperLocked 2", pendingLayoutChanges);
+ }
+ }
+
+ if (policy.isForceHiding(win.mAttrs)) {
+ if (!wasAnimating && nowAnimating) {
+ if (DEBUG_KEYGUARD || DEBUG_ANIM || DEBUG_VISIBILITY) Slog.v(TAG,
+ "Animation started that could impact force hide: " + win);
+ animator.mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
+ pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ if (DEBUG_LAYOUT_REPEATS) {
+ mService.mWindowPlacerLocked.debugLayoutRepeats(
+ "updateWindowsAndWallpaperLocked 3", pendingLayoutChanges);
+ }
+ mService.mFocusMayChange = true;
+ } else if (animator.mKeyguardGoingAway && !nowAnimating) {
+ // Timeout!!
+ Slog.e(TAG, "Timeout waiting for animation to startup");
+ policy.startKeyguardExitAnimation(0, 0);
+ animator.mKeyguardGoingAway = false;
+ }
+ if (win.isReadyForDisplay()) {
+ if (nowAnimating && win.mWinAnimator.mKeyguardGoingAwayAnimation) {
+ animator.mForceHiding = KEYGUARD_ANIMATING_OUT;
+ } else {
+ animator.mForceHiding = win.isDrawnLw()
+ ? KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN;
+ }
+ }
+ if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
+ "Force hide " + animator.forceHidingToString()
+ + " hasSurface=" + win.mHasSurface
+ + " policyVis=" + win.mPolicyVisibility
+ + " destroying=" + win.mDestroying
+ + " parentHidden=" + win.isParentWindowHidden()
+ + " vis=" + win.mViewVisibility
+ + " hidden=" + win.mToken.hidden
+ + " anim=" + win.mWinAnimator.mAnimation);
+ } else if (canBeForceHidden) {
+ if (shouldBeForceHidden) {
+ if (!win.hideLw(false, false)) {
+ // Was already hidden
+ continue;
+ }
+ if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
+ "Now policy hidden: " + win);
+ } else {
+ final Animation postKeyguardExitAnimation =
+ animator.mPostKeyguardExitAnimation;
+ boolean applyExistingExitAnimation = postKeyguardExitAnimation != null
+ && !postKeyguardExitAnimation.hasEnded()
+ && !winAnimator.mKeyguardGoingAwayAnimation
+ && win.hasDrawnLw()
+ && !win.isChildWindow()
+ && !win.mIsImWindow
+ && isDefaultDisplay;
+
+ // If the window is already showing and we don't need to apply an existing
+ // Keyguard exit animation, skip.
+ if (!win.showLw(false, false) && !applyExistingExitAnimation) {
+ continue;
+ }
+ final boolean visibleNow = win.isVisibleNow();
+ if (!visibleNow) {
+ // Couldn't really show, must showLw() again when win becomes visible.
+ win.hideLw(false, false);
+ continue;
+ }
+ if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
+ "Now policy shown: " + win);
+ if ((animator.mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0
+ && !win.isChildWindow()) {
+ if (unForceHiding == null) {
+ unForceHiding = new ArrayList<>();
+ }
+ unForceHiding.add(winAnimator);
+ if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
+ wallpaperInUnForceHiding = true;
+ }
+ if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
+ startingInUnForceHiding = true;
+ }
+ } else if (applyExistingExitAnimation) {
+ // We're already in the middle of an animation. Use the existing
+ // animation to bring in this window.
+ if (DEBUG_KEYGUARD) Slog.v(TAG,
+ "Applying existing Keyguard exit animation to new window: win="
+ + win);
+
+ final Animation a = policy.createForceHideEnterAnimation(false,
+ keyguardGoingAwayToShade);
+ winAnimator.setAnimation(a, postKeyguardExitAnimation.getStartTime(),
+ STACK_CLIP_BEFORE_ANIM);
+ winAnimator.mKeyguardGoingAwayAnimation = true;
+ winAnimator.mKeyguardGoingAwayWithWallpaper
+ = keyguardGoingAwayWithWallpaper;
+ }
+ final WindowState currentFocus = mService.mCurrentFocus;
+ if (currentFocus == null || currentFocus.mLayer < win.mLayer) {
+ // We are showing on top of the current
+ // focus, so re-evaluate focus to make
+ // sure it is correct.
+ if (DEBUG_FOCUS_LIGHT) Slog.v(TAG,
+ "updateWindowsForAnimator: setting mFocusMayChange true");
+ mService.mFocusMayChange = true;
+ }
+ }
+ if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
+ animator.mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
+ pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ if (DEBUG_LAYOUT_REPEATS) {
+ mService.mWindowPlacerLocked.debugLayoutRepeats(
+ "updateWindowsAndWallpaperLocked 4", pendingLayoutChanges);
+ }
+ }
+ }
+ }
+
+ // If the window doesn't have a surface, the only thing we care about is the correct
+ // policy visibility.
+ else if (canBeForceHidden) {
+ if (shouldBeForceHidden) {
+ win.hideLw(false, false);
+ } else {
+ win.showLw(false, false);
+ }
+ }
+
+ final AppWindowToken atoken = win.mAppToken;
+ if (winAnimator.mDrawState == READY_TO_SHOW) {
+ if (atoken == null || atoken.allDrawn) {
+ if (win.performShowLocked()) {
+ pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
+ if (DEBUG_LAYOUT_REPEATS) {
+ mService.mWindowPlacerLocked.debugLayoutRepeats(
+ "updateWindowsAndWallpaperLocked 5", pendingLayoutChanges);
+ }
+ }
+ }
+ }
+ final AppWindowAnimator appAnimator = winAnimator.mAppAnimator;
+ if (appAnimator != null && appAnimator.thumbnail != null) {
+ if (appAnimator.thumbnailTransactionSeq != animator.mAnimTransactionSequence) {
+ appAnimator.thumbnailTransactionSeq = animator.mAnimTransactionSequence;
+ appAnimator.thumbnailLayer = 0;
+ }
+ if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {
+ appAnimator.thumbnailLayer = winAnimator.mAnimLayer;
+ }
+ }
+ if (win.mIsWallpaper) {
+ wallpaper = win;
+ }
+ } // end forall windows
+
+ // If we have windows that are being shown due to them no longer being force-hidden, apply
+ // the appropriate animation to them if animations are not disabled.
+ if (unForceHiding != null) {
+ if (!keyguardGoingAwayNoAnimation) {
+ boolean first = true;
+ for (int i=unForceHiding.size()-1; i>=0; i--) {
+ final WindowStateAnimator winAnimator = unForceHiding.get(i);
+ final Animation a = policy.createForceHideEnterAnimation(
+ wallpaperInUnForceHiding && !startingInUnForceHiding,
+ keyguardGoingAwayToShade);
+ if (a != null) {
+ if (DEBUG_KEYGUARD) Slog.v(TAG,
+ "Starting keyguard exit animation on window " + winAnimator.mWin);
+ winAnimator.setAnimation(a, STACK_CLIP_BEFORE_ANIM);
+ winAnimator.mKeyguardGoingAwayAnimation = true;
+ winAnimator.mKeyguardGoingAwayWithWallpaper
+ = keyguardGoingAwayWithWallpaper;
+ if (first) {
+ animator.mPostKeyguardExitAnimation = a;
+ animator.mPostKeyguardExitAnimation.setStartTime(animator.mCurrentTime);
+ first = false;
+ }
+ }
+ }
+ } else if (animator.mKeyguardGoingAway) {
+ policy.startKeyguardExitAnimation(animator.mCurrentTime, 0 /* duration */);
+ animator.mKeyguardGoingAway = false;
+ }
+
+
+ // Wallpaper is going away in un-force-hide motion, animate it as well.
+ if (!wallpaperInUnForceHiding && wallpaper != null && !keyguardGoingAwayNoAnimation) {
+ if (DEBUG_KEYGUARD) Slog.d(TAG,
+ "updateWindowsForAnimator: wallpaper animating away");
+ final Animation a = policy.createForceHideWallpaperExitAnimation(
+ keyguardGoingAwayToShade);
+ if (a != null) {
+ wallpaper.mWinAnimator.setAnimation(a);
+ }
+ }
+ }
+
+ if (animator.mPostKeyguardExitAnimation != null) {
+ // We're in the midst of a keyguard exit animation.
+ if (animator.mKeyguardGoingAway) {
+ policy.startKeyguardExitAnimation(animator.mCurrentTime +
+ animator.mPostKeyguardExitAnimation.getStartOffset(),
+ animator.mPostKeyguardExitAnimation.getDuration());
+ animator.mKeyguardGoingAway = false;
+ }
+ // mPostKeyguardExitAnimation might either be ended normally, cancelled, or "orphaned",
+ // meaning that the window it was running on was removed. We check for hasEnded() for
+ // ended normally and cancelled case, and check the time for the "orphaned" case.
+ else if (animator.mPostKeyguardExitAnimation.hasEnded()
+ || animator.mCurrentTime - animator.mPostKeyguardExitAnimation.getStartTime()
+ > animator.mPostKeyguardExitAnimation.getDuration()) {
+ // Done with the animation, reset.
+ if (DEBUG_KEYGUARD) Slog.v(TAG, "Done with Keyguard exit animations.");
+ animator.mPostKeyguardExitAnimation = null;
+ }
+ }
+
+ final WindowState winShowWhenLocked = (WindowState) policy.getWinShowWhenLockedLw();
+ if (winShowWhenLocked != null) {
+ animator.mLastShowWinWhenLocked = winShowWhenLocked;
+ }
+ }
+
+ void updateWallpaperForAnimator(WindowAnimator animator) {
+ resetAnimationBackgroundAnimator();
+
+ final WindowList windows = mWindows;
+ WindowState detachedWallpaper = null;
+
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ final WindowState win = windows.get(i);
+ final WindowStateAnimator winAnimator = win.mWinAnimator;
+ if (winAnimator.mSurfaceController == null || !winAnimator.hasSurface()) {
+ continue;
+ }
+
+ final int flags = win.mAttrs.flags;
+
+ // If this window is animating, make a note that we have an animating window and take
+ // care of a request to run a detached wallpaper animation.
+ if (winAnimator.mAnimating) {
+ if (winAnimator.mAnimation != null) {
+ if ((flags & FLAG_SHOW_WALLPAPER) != 0
+ && winAnimator.mAnimation.getDetachWallpaper()) {
+ detachedWallpaper = win;
+ }
+ final int color = winAnimator.mAnimation.getBackgroundColor();
+ if (color != 0) {
+ final TaskStack stack = win.getStack();
+ if (stack != null) {
+ stack.setAnimationBackground(winAnimator, color);
+ }
+ }
+ }
+ animator.setAnimating(true);
+ }
+
+ // If this window's app token is running a detached wallpaper animation, make a note so
+ // we can ensure the wallpaper is displayed behind it.
+ final AppWindowAnimator appAnimator = winAnimator.mAppAnimator;
+ if (appAnimator != null && appAnimator.animation != null
+ && appAnimator.animating) {
+ if ((flags & FLAG_SHOW_WALLPAPER) != 0
+ && appAnimator.animation.getDetachWallpaper()) {
+ detachedWallpaper = win;
+ }
+
+ final int color = appAnimator.animation.getBackgroundColor();
+ if (color != 0) {
+ final TaskStack stack = win.getStack();
+ if (stack != null) {
+ stack.setAnimationBackground(winAnimator, color);
+ }
+ }
+ }
+ } // end forall windows
+
+ if (animator.mWindowDetachedWallpaper != detachedWallpaper) {
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Detached wallpaper changed from "
+ + animator.mWindowDetachedWallpaper + " to " + detachedWallpaper);
+ animator.mWindowDetachedWallpaper = detachedWallpaper;
+ animator.mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
+ }
+ }
+
+ void prepareWindowSurfaces() {
+ final int count = mWindows.size();
+ for (int j = 0; j < count; j++) {
+ mWindows.get(j).mWinAnimator.prepareSurfaceLocked(true);
+ }
+ }
+
+ boolean inputMethodClientHasFocus(IInputMethodClient client) {
+ // The focus for the client is the window immediately below where we would place the input
+ // method window.
+ int idx = findDesiredInputMethodWindowIndex(false);
+ if (idx <= 0) {
+ return false;
+ }
+
+ WindowState imFocus = mWindows.get(idx - 1);
+ if (DEBUG_INPUT_METHOD) {
+ Slog.i(TAG_WM, "Desired input method target: " + imFocus);
+ Slog.i(TAG_WM, "Current focus: " + mService.mCurrentFocus);
+ Slog.i(TAG_WM, "Last focus: " + mService.mLastFocus);
+ }
+
+ if (imFocus == null) {
+ return false;
+ }
+
+ // This may be a starting window, in which case we still want to count it as okay.
+ if (imFocus.mAttrs.type == TYPE_APPLICATION_STARTING && imFocus.mAppToken != null) {
+ // The client has definitely started, so it really should have a window in this app
+ // token. Let's look for it.
+ final WindowState w = imFocus.mAppToken.getFirstNonStartingWindow();
+ if (w != null) {
+ if (DEBUG_INPUT_METHOD) Slog.i(TAG_WM, "Switching to real app window: " + w);
+ imFocus = w;
+ }
+ }
+
+ final IInputMethodClient imeClient = imFocus.mSession.mClient;
+
+ if (DEBUG_INPUT_METHOD) {
+ Slog.i(TAG_WM, "IM target client: " + imeClient);
+ if (imeClient != null) {
+ Slog.i(TAG_WM, "IM target client binder: " + imeClient.asBinder());
+ Slog.i(TAG_WM, "Requesting client binder: " + client.asBinder());
+ }
+ }
+
+ return imeClient != null && imeClient.asBinder() == client.asBinder();
+ }
+
+ boolean hasSecureWindowOnScreen() {
+ for (int i = mWindows.size() - 1; i >= 0; --i) {
+ final WindowState ws = mWindows.get(i);
+ if (ws.isOnScreen() && (ws.mAttrs.flags & FLAG_SECURE) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void updateSystemUiVisibility(int visibility, int globalDiff) {
+ for (int i = mWindows.size() - 1; i >= 0; --i) {
+ final WindowState ws = mWindows.get(i);
+ try {
+ int curValue = ws.mSystemUiVisibility;
+ int diff = (curValue ^ visibility) & globalDiff;
+ int newValue = (curValue & ~diff) | (visibility & diff);
+ if (newValue != curValue) {
+ ws.mSeq++;
+ ws.mSystemUiVisibility = newValue;
+ }
+ if (newValue != curValue || ws.mAttrs.hasSystemUiListeners) {
+ ws.mClient.dispatchSystemUiVisibilityChanged(ws.mSeq,
+ visibility, newValue, diff);
+ }
+ } catch (RemoteException e) {
+ // so sorry
+ }
+ }
+ }
+
+ void onWindowFreezeTimeout() {
+ Slog.w(TAG_WM, "Window freeze timeout expired.");
+ mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
+ for (int i = mWindows.size() - 1; i >= 0; --i) {
+ final WindowState w = mWindows.get(i);
+ if (!w.mOrientationChanging) {
+ continue;
+ }
+ w.mOrientationChanging = false;
+ w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mService.mDisplayFreezeTime);
+ Slog.w(TAG_WM, "Force clearing orientation change: " + w);
+ }
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+
+ void waitForAllWindowsDrawn() {
+ final WindowManagerPolicy policy = mService.mPolicy;
+ for (int winNdx = mWindows.size() - 1; winNdx >= 0; --winNdx) {
+ final WindowState win = mWindows.get(winNdx);
+ final boolean isForceHiding = policy.isForceHiding(win.mAttrs);
+ final boolean keyguard = policy.isKeyguardHostWindow(win.mAttrs);
+ if (win.isVisibleLw() && (win.mAppToken != null || isForceHiding || keyguard)) {
+ win.mWinAnimator.mDrawState = DRAW_PENDING;
+ // Force add to mResizingWindows.
+ win.mLastContentInsets.set(-1, -1, -1, -1);
+ mService.mWaitingForDrawn.add(win);
+
+ // No need to wait for the windows below Keyguard.
+ if (isForceHiding) {
+ return;
+ }
+ }
+ }
+ }
+
static final class GetWindowOnDisplaySearchResult {
boolean reachedToken;
WindowState foundWindow;
@@ -1451,6 +2530,37 @@
setLayoutNeeded();
}
+ @Override
+ int getOrientation() {
+ if (mService.isStackVisibleLocked(DOCKED_STACK_ID)
+ || mService.isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
+ // Apps and their containers are not allowed to specify an orientation while the
+ // docked or freeform stack is visible...except for the home stack/task if the
+ // docked stack is minimized and it actually set something.
+ if (mHomeStack != null && mHomeStack.isVisible()
+ && mDividerControllerLocked.isMinimizedDock()) {
+ final int orientation = mHomeStack.getOrientation();
+ if (orientation != SCREEN_ORIENTATION_UNSET) {
+ return orientation;
+ }
+ }
+ return SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
+ final int orientation = super.getOrientation();
+ if (orientation != SCREEN_ORIENTATION_UNSET
+ && orientation != SCREEN_ORIENTATION_BEHIND) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+ "App is requesting an orientation, return " + orientation);
+ return orientation;
+ }
+
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+ "No app is requesting an orientation, return " + mService.mLastOrientation);
+ // The next app has not been requested to be visible, so we keep the current orientation
+ // to prevent freezing/unfreezing the display too early.
+ return mService.mLastOrientation;
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 5854197..f75f224 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -33,6 +33,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
+import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
import android.content.Context;
import android.content.res.Configuration;
@@ -296,7 +297,7 @@
}
}
- boolean wasVisible() {
+ private boolean wasVisible() {
return mLastVisibility;
}
@@ -356,7 +357,7 @@
mLastRect.set(frame);
}
- void notifyDockedDividerVisibilityChanged(boolean visible) {
+ private void notifyDockedDividerVisibilityChanged(boolean visible) {
final int size = mDockedStackListeners.beginBroadcast();
for (int i = 0; i < size; ++i) {
final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
@@ -370,6 +371,7 @@
}
void notifyDockedStackExistsChanged(boolean exists) {
+ // TODO(multi-display): Perform all actions only for current display.
final int size = mDockedStackListeners.beginBroadcast();
for (int i = 0; i < size; ++i) {
final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
@@ -413,7 +415,7 @@
return mImeHideRequested;
}
- void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
+ private void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
minimizedDock ? 1 : 0, 0).sendToTarget();
@@ -442,7 +444,7 @@
mDockedStackListeners.finishBroadcast();
}
- void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {
+ private void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {
final int size = mDockedStackListeners.beginBroadcast();
for (int i = 0; i < size; ++i) {
final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
@@ -474,8 +476,7 @@
stack.getDimBounds(mTmpRect);
if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
mDimLayer.setBounds(mTmpRect);
- mDimLayer.show(mService.mLayersController.getResizeDimLayer(),
- alpha, 0 /* duration */);
+ mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
} else {
visibleAndValid = false;
}
@@ -487,6 +488,14 @@
}
/**
+ * @return The layer used for dimming the apps when dismissing docked/fullscreen stack. Just
+ * above all application surfaces.
+ */
+ private int getResizeDimLayer() {
+ return (mWindow != null) ? mWindow.mLayer - 1 : LAYER_OFFSET_DIM;
+ }
+
+ /**
* Notifies the docked stack divider controller of a visibility change that happens without
* an animation.
*/
@@ -693,7 +702,7 @@
return animateForIme(now);
} else {
if (mDimLayer != null && mDimLayer.isDimming()) {
- mDimLayer.setLayer(mService.mLayersController.getResizeDimLayer());
+ mDimLayer.setLayer(getResizeDimLayer());
}
return false;
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 065a3dc..6a06ef3 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
@@ -384,7 +385,8 @@
/* Notifies that the input device configuration has changed. */
@Override
public void notifyConfigurationChanged() {
- mService.sendNewConfiguration();
+ // TODO(multi-display): Notify proper displays that are associated with this input device.
+ mService.sendNewConfiguration(DEFAULT_DISPLAY);
synchronized (mInputDevicesReadyMonitor) {
if (!mInputDevicesReady) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 05ffe2d..90e27ea 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -165,8 +165,13 @@
ParcelFileDescriptor mSurfaceTraceFd;
RemoteEventTrace mRemoteEventTrace;
+ private final WindowLayersController mLayersController;
+ final WallpaperController mWallpaperController;
+
RootWindowContainer(WindowManagerService service) {
mService = service;
+ mLayersController = new WindowLayersController(mService);
+ mWallpaperController = new WallpaperController(mService);
}
WindowState computeFocusedWindow() {
@@ -211,7 +216,8 @@
}
private DisplayContent createDisplayContent(final Display display) {
- final DisplayContent dc = new DisplayContent(display, mService);
+ final DisplayContent dc = new DisplayContent(display, mService, mLayersController,
+ mWallpaperController);
final int displayId = display.getDisplayId();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
@@ -230,7 +236,7 @@
mService.configureDisplayPolicyLocked(dc);
// TODO(multi-display): Create an input channel for each display with touch capability.
- if (displayId == Display.DEFAULT_DISPLAY) {
+ if (displayId == DEFAULT_DISPLAY) {
dc.mTapDetector = new TaskTapPointerEventListener(
mService, dc);
mService.registerPointerEventListener(dc.mTapDetector);
@@ -268,8 +274,7 @@
mService.mStackIdToStack.put(stackId, stack);
if (stackId == DOCKED_STACK_ID) {
- mService.getDefaultDisplayContentLocked().mDividerControllerLocked
- .notifyDockedStackExistsChanged(true);
+ dc.mDividerControllerLocked.notifyDockedStackExistsChanged(true);
}
}
@@ -439,10 +444,10 @@
// TODO(multi-display): By default we add this to the default display, but maybe we
// should provide an API for a token to be added to any display?
- final WindowToken token = new WindowToken(mService, binder, type, true,
- getDisplayContent(DEFAULT_DISPLAY));
+ final DisplayContent dc = getDisplayContent(DEFAULT_DISPLAY);
+ final WindowToken token = new WindowToken(mService, binder, type, true, dc);
if (type == TYPE_WALLPAPER) {
- mService.mWallpaperControllerLocked.addWallpaperToken(token);
+ dc.mWallpaperController.addWallpaperToken(token);
}
}
@@ -563,8 +568,33 @@
}
}
- /** Set new config and return array of ids of stacks that were changed during update. */
- int[] setGlobalConfigurationIfNeeded(Configuration newConfiguration) {
+ /**
+ * Set new display override config and return array of ids of stacks that were changed during
+ * update. If called for the default display, global configuration will also be updated.
+ */
+ int[] setDisplayOverrideConfigurationIfNeeded(Configuration newConfiguration, int displayId) {
+ final DisplayContent displayContent = getDisplayContent(displayId);
+ if (displayContent == null) {
+ throw new IllegalArgumentException("Display not found for id: " + displayId);
+ }
+
+ final Configuration currentConfig = displayContent.getOverrideConfiguration();
+ final boolean configChanged = currentConfig.diff(newConfiguration) != 0;
+ if (!configChanged) {
+ return null;
+ }
+ displayContent.onOverrideConfigurationChanged(currentConfig);
+
+ if (displayId == DEFAULT_DISPLAY) {
+ // Override configuration of the default display duplicates global config. In this case
+ // we also want to update the global config.
+ return setGlobalConfigurationIfNeeded(newConfiguration);
+ } else {
+ return updateStackBoundsAfterConfigChange(displayId);
+ }
+ }
+
+ private int[] setGlobalConfigurationIfNeeded(Configuration newConfiguration) {
final boolean configChanged = getConfiguration().diff(newConfiguration) != 0;
if (!configChanged) {
return null;
@@ -597,6 +627,16 @@
return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
}
+ /** Same as {@link #updateStackBoundsAfterConfigChange()} but only for a specific display. */
+ private int[] updateStackBoundsAfterConfigChange(int displayId) {
+ mChangedStackList.clear();
+
+ final DisplayContent dc = getDisplayContent(displayId);
+ dc.updateStackBoundsAfterConfigChange(mChangedStackList);
+
+ return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
+ }
+
private void prepareFreezingTaskBounds() {
for (int i = mChildren.size() - 1; i >= 0; i--) {
mChildren.get(i).prepareFreezingTaskBounds();
@@ -715,7 +755,6 @@
boolean addPipInputConsumerHandle = pipInputConsumer != null;
boolean addWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
final Rect pipTouchableBounds = addPipInputConsumerHandle ? new Rect() : null;
- final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
boolean disableWallpaperTouchEvents = false;
final int count = mChildren.size();
@@ -766,7 +805,7 @@
if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) {
disableWallpaperTouchEvents = true;
}
- final boolean hasWallpaper = wallpaperController.isWallpaperTarget(child)
+ final boolean hasWallpaper = dc.mWallpaperController.isWallpaperTarget(child)
&& (privateFlags & PRIVATE_FLAG_KEYGUARD) == 0
&& !disableWallpaperTouchEvents;
@@ -948,14 +987,13 @@
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
- final WindowList defaultWindows = defaultDisplay.getWindowList();
final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
// If we are ready to perform an app transition, check through all of the app tokens to be
// shown and see if they are ready to go.
if (mService.mAppTransition.isReady()) {
defaultDisplay.pendingLayoutChanges |=
- surfacePlacer.handleAppTransitionReadyLocked(defaultWindows);
+ surfacePlacer.handleAppTransitionReadyLocked();
if (DEBUG_LAYOUT_REPEATS)
surfacePlacer.debugLayoutRepeats("after handleAppTransitionReadyLocked",
defaultDisplay.pendingLayoutChanges);
@@ -1043,7 +1081,7 @@
if (mService.mInputMethodWindow == win) {
mService.mInputMethodWindow = null;
}
- if (mService.mWallpaperControllerLocked.isWallpaperTarget(win)) {
+ if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) {
wallpaperDestroyed = true;
}
win.destroyOrSaveSurface();
@@ -1060,7 +1098,7 @@
if (!token.hasVisible) {
exitingTokens.remove(i);
if (token.windowType == TYPE_WALLPAPER) {
- mService.mWallpaperControllerLocked.removeWallpaperToken(token);
+ displayContent.mWallpaperController.removeWallpaperToken(token);
}
}
}
@@ -1141,8 +1179,10 @@
if (mUpdateRotation) {
if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
- if (mService.updateRotationUncheckedLocked(false)) {
- mService.mH.sendEmptyMessage(SEND_NEW_CONFIGURATION);
+ // TODO(multi-display): Update rotation for different displays separately.
+ final int displayId = defaultDisplay.getDisplayId();
+ if (mService.updateRotationUncheckedLocked(false, displayId)) {
+ mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
} else {
mUpdateRotation = false;
}
@@ -1171,9 +1211,9 @@
}
}
- for (DisplayContent displayContent : displayList) {
- mService.mLayersController.assignLayersLocked(displayContent.getWindowList());
- displayContent.setLayoutNeeded();
+ for (int j = displayList.size() - 1; j >= 0; --j) {
+ final DisplayContent dc = displayList.get(j);
+ dc.assignWindowLayers(true /*setLayoutNeeded*/);
}
}
@@ -1227,7 +1267,7 @@
final int displayId = dc.getDisplayId();
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
- final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ final boolean isDefaultDisplay = (displayId == DEFAULT_DISPLAY);
final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
// Reset for each display.
@@ -1248,18 +1288,16 @@
if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
"On entry to LockedInner", dc.pendingLayoutChanges);
- if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0
- && mService.mWallpaperControllerLocked.adjustWallpaperWindows()) {
- mService.mLayersController.assignLayersLocked(windows);
- dc.setLayoutNeeded();
+ if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+ dc.adjustWallpaperWindows();
}
if (isDefaultDisplay
&& (dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
- if (mService.updateOrientationFromAppTokensLocked(true)) {
+ if (mService.updateOrientationFromAppTokensLocked(true, displayId)) {
dc.setLayoutNeeded();
- mService.mH.sendEmptyMessage(SEND_NEW_CONFIGURATION);
+ mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
}
@@ -1316,10 +1354,10 @@
w.applyDimLayerIfNeeded();
if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
- && mService.mWallpaperControllerLocked.isWallpaperTarget(w)) {
+ && dc.mWallpaperController.isWallpaperTarget(w)) {
// This is the wallpaper target and its obscured state changed... make sure the
// current wallpaper's visibility has been updated accordingly.
- mService.mWallpaperControllerLocked.updateWallpaperVisibility();
+ dc.mWallpaperController.updateWallpaperVisibility();
}
w.handleWindowMovedIfNeeded();
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index edd3a3b..ead70e1 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -58,26 +58,25 @@
* This class represents an active client session. There is generally one
* Session object per process that is interacting with the window manager.
*/
-final class Session extends IWindowSession.Stub
+// Needs to be public and not final so we can mock during tests...sucks I know :(
+public class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
final WindowManagerService mService;
final IWindowSessionCallback mCallback;
final IInputMethodClient mClient;
- final IInputContext mInputContext;
final int mUid;
final int mPid;
- final String mStringName;
+ private final String mStringName;
SurfaceSession mSurfaceSession;
- int mNumWindow = 0;
- boolean mClientDead = false;
- float mLastReportedAnimatorScale;
+ private int mNumWindow = 0;
+ private boolean mClientDead = false;
+ private float mLastReportedAnimatorScale;
public Session(WindowManagerService service, IWindowSessionCallback callback,
IInputMethodClient client, IInputContext inputContext) {
mService = service;
mCallback = callback;
mClient = client;
- mInputContext = inputContext;
mUid = Binder.getCallingUid();
mPid = Binder.getCallingPid();
mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
@@ -461,7 +460,7 @@
synchronized(mService.mWindowMap) {
long ident = Binder.clearCallingIdentity();
try {
- mService.mWallpaperControllerLocked.setWindowWallpaperPosition(
+ mService.mRoot.mWallpaperController.setWindowWallpaperPosition(
mService.windowForClientLocked(this, window, true),
x, y, xStep, yStep);
} finally {
@@ -472,7 +471,7 @@
public void wallpaperOffsetsComplete(IBinder window) {
synchronized (mService.mWindowMap) {
- mService.mWallpaperControllerLocked.wallpaperOffsetsComplete(window);
+ mService.mRoot.mWallpaperController.wallpaperOffsetsComplete(window);
}
}
@@ -480,7 +479,7 @@
synchronized(mService.mWindowMap) {
long ident = Binder.clearCallingIdentity();
try {
- mService.mWallpaperControllerLocked.setWindowWallpaperDisplayOffset(
+ mService.mRoot.mWallpaperController.setWindowWallpaperDisplayOffset(
mService.windowForClientLocked(this, window, true), x, y);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -493,7 +492,7 @@
synchronized(mService.mWindowMap) {
long ident = Binder.clearCallingIdentity();
try {
- return mService.mWallpaperControllerLocked.sendWindowWallpaperCommand(
+ return mService.mRoot.mWallpaperController.sendWindowWallpaperCommand(
mService.windowForClientLocked(this, window, true),
action, x, y, z, extras, sync);
} finally {
@@ -504,7 +503,7 @@
public void wallpaperCommandComplete(IBinder window, Bundle result) {
synchronized (mService.mWindowMap) {
- mService.mWallpaperControllerLocked.wallpaperCommandComplete(window);
+ mService.mRoot.mWallpaperController.wallpaperCommandComplete(window);
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 19c9b7d..7f543f9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -327,8 +327,9 @@
* the adjusted bounds's top.
*/
void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) {
- final Configuration overrideConfig = getOverrideConfiguration();
- if (!isResizeable() || Configuration.EMPTY.equals(overrideConfig)) {
+ // Task override config might be empty, while display or stack override config isn't, so
+ // we have to check merged override config here.
+ if (!isResizeable() || Configuration.EMPTY.equals(getMergedOverrideConfiguration())) {
return;
}
@@ -340,7 +341,7 @@
mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
}
setTempInsetBounds(tempInsetBounds);
- resizeLocked(mTmpRect2, overrideConfig, false /* forced */);
+ resizeLocked(mTmpRect2, getOverrideConfiguration(), false /* forced */);
}
/** Return true if the current bound can get outputted to the rest of the system as-is. */
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 7c39bd2..5637e51 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -24,6 +24,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
@@ -35,9 +36,13 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
+import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
import android.app.ActivityManager.StackId;
+import android.app.IActivityManager;
import android.content.res.Configuration;
+import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Debug;
@@ -52,6 +57,7 @@
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.internal.policy.DockedDividerUtils;
+import com.android.internal.policy.PipSnapAlgorithm;
import com.android.server.EventLogTags;
import java.io.PrintWriter;
@@ -379,19 +385,41 @@
mTmpRect2.set(mBounds);
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
- if (mStackId == DOCKED_STACK_ID) {
- repositionDockedStackAfterRotation(mTmpRect2);
- snapDockedStackAfterRotation(mTmpRect2);
- final int newDockSide = getDockSide(mTmpRect2);
+ switch (mStackId) {
+ case PINNED_STACK_ID:
+ // Keep the pinned stack in the same aspect ratio as in the old orientation, but
+ // move it into the position in the rotated space, and snap to the closest space
+ // in the new orientation.
- // Update the dock create mode and clear the dock create bounds, these
- // might change after a rotation and the original values will be invalid.
- mService.setDockedStackCreateStateLocked(
- (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
- ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
- : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
- null);
- mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
+ try {
+ final IActivityManager am = mService.mActivityManager;
+ final Rect movementBounds = am.getPictureInPictureMovementBounds(
+ mDisplayContent.getDisplayId());
+ final int width = mBounds.width();
+ final int height = mBounds.height();
+ final int left = mTmpRect2.centerX() - (width / 2);
+ final int top = mTmpRect2.centerY() - (height / 2);
+ mTmpRect2.set(left, top, left + width, top + height);
+
+ final PipSnapAlgorithm snapAlgorithm = new PipSnapAlgorithm(mService.mContext,
+ mDisplayContent.getDisplayId());
+ mTmpRect2.set(snapAlgorithm.findClosestSnapBounds(movementBounds, mTmpRect2));
+ } catch (RemoteException e) {}
+ break;
+ case DOCKED_STACK_ID:
+ repositionDockedStackAfterRotation(mTmpRect2);
+ snapDockedStackAfterRotation(mTmpRect2);
+ final int newDockSide = getDockSide(mTmpRect2);
+
+ // Update the dock create mode and clear the dock create bounds, these
+ // might change after a rotation and the original values will be invalid.
+ mService.setDockedStackCreateStateLocked(
+ (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
+ ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+ : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
+ null);
+ mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
+ break;
}
mBoundsAfterRotation.set(mTmpRect2);
@@ -447,8 +475,7 @@
// Calculate the current position.
final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
- final int dividerSize = mService.getDefaultDisplayContentLocked()
- .getDockedDividerController().getContentWidth();
+ final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth();
final int dockSide = getDockSide(outBounds);
final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
dockSide, dividerSize);
@@ -782,13 +809,14 @@
mAnimationBackgroundSurface.destroySurface();
mAnimationBackgroundSurface = null;
}
+ final DockedStackDividerController dividerController =
+ mDisplayContent.mDividerControllerLocked;
mDisplayContent = null;
mService.mWindowPlacerLocked.requestTraversal();
if (mStackId == DOCKED_STACK_ID) {
- mService.getDefaultDisplayContentLocked().mDividerControllerLocked
- .notifyDockedStackExistsChanged(false);
+ dividerController.notifyDockedStackExistsChanged(false);
}
}
@@ -802,8 +830,8 @@
if (mAnimationBackgroundAnimator == null
|| animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
mAnimationBackgroundAnimator = winAnimator;
- animLayer = mService.adjustAnimationBackground(winAnimator);
- mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
+ animLayer = mDisplayContent.getLayerForAnimationBackground(winAnimator);
+ mAnimationBackgroundSurface.show(animLayer - LAYER_OFFSET_DIM,
((color >> 24) & 0xff) / 255f, 0);
}
}
@@ -1009,7 +1037,7 @@
}
if (dockSide == DOCKED_TOP) {
- mService.getStableInsetsLocked(mTmpRect);
+ mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
int topInset = mTmpRect.top;
mTmpAdjustedBounds.set(mBounds);
mTmpAdjustedBounds.bottom =
@@ -1040,7 +1068,7 @@
}
if (dockSide == DOCKED_TOP) {
- mService.getStableInsetsLocked(mTmpRect);
+ mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
int topInset = mTmpRect.top;
return mBounds.bottom - topInset;
} else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 0841231..4ab8887 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -628,10 +628,9 @@
return changed;
}
- boolean adjustWallpaperWindows() {
+ boolean adjustWallpaperWindows(WindowList windows) {
mService.mRoot.mWallpaperMayChange = false;
- final WindowList windows = mService.getDefaultWindowListLocked();
// First find top-most window that has asked to be on top of the wallpaper;
// all wallpapers go behind it.
findWallpaperTarget(windows, mFindResults);
@@ -726,8 +725,8 @@
* Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
* the opening apps should be a wallpaper target.
*/
- void adjustWallpaperWindowsForAppTransitionIfNeeded(
- DisplayContent dc, ArraySet<AppWindowToken> openingApps, WindowList windows) {
+ void adjustWallpaperWindowsForAppTransitionIfNeeded(DisplayContent dc,
+ ArraySet<AppWindowToken> openingApps) {
boolean adjust = false;
if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
adjust = true;
@@ -741,9 +740,8 @@
}
}
- if (adjust && adjustWallpaperWindows()) {
- mService.mLayersController.assignLayersLocked(windows);
- dc.setLayoutNeeded();
+ if (adjust) {
+ dc.adjustWallpaperWindows();
}
}
@@ -769,6 +767,10 @@
pw.print("mLastWallpaperDisplayOffsetX="); pw.print(mLastWallpaperDisplayOffsetX);
pw.print(" mLastWallpaperDisplayOffsetY="); pw.println(mLastWallpaperDisplayOffsetY);
}
+
+ if (mWallpaperAnimLayerAdjustment != 0) {
+ pw.println(prefix + "mWallpaperAnimLayerAdjustment=" + mWallpaperAnimLayerAdjustment);
+ }
}
void dumpTokens(PrintWriter pw, String prefix, boolean dumpAll) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index fb6b09a..4c62245 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -16,28 +16,17 @@
package com.android.server.wm;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
-import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
-import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
-import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
@@ -48,14 +37,11 @@
import android.util.SparseArray;
import android.util.TimeUtils;
import android.view.Choreographer;
-import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManagerPolicy;
-import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import java.io.PrintWriter;
-import java.util.ArrayList;
/**
* Singleton class that carries out the animations and Surface operations in a separate task
@@ -65,7 +51,7 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowAnimator" : TAG_WM;
/** How long to give statusbar to clear the private keyguard flag when animating out */
- private static final long KEYGUARD_ANIM_TIMEOUT_MS = 1000;
+ static final long KEYGUARD_ANIM_TIMEOUT_MS = 1000;
final WindowManagerService mService;
final Context mContext;
@@ -85,7 +71,7 @@
/** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this
* is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
- private int mAnimTransactionSequence;
+ int mAnimTransactionSequence;
/** Window currently running an animation that has requested it be detached
* from the wallpaper. This means we need to ensure the wallpaper is
@@ -120,9 +106,9 @@
private final AppTokenList mTmpExitingAppTokens = new AppTokenList();
/** The window that was previously hiding the Keyguard. */
- private WindowState mLastShowWinWhenLocked;
+ WindowState mLastShowWinWhenLocked;
- private String forceHidingToString() {
+ String forceHidingToString() {
switch (mForceHiding) {
case KEYGUARD_NOT_SHOWN: return "KEYGUARD_NOT_SHOWN";
case KEYGUARD_SHOWN: return "KEYGUARD_SHOWN";
@@ -150,7 +136,7 @@
void addDisplayLocked(final int displayId) {
// Create the DisplayContentsAnimator object by retrieving it.
getDisplayContentsAnimatorLocked(displayId);
- if (displayId == Display.DEFAULT_DISPLAY) {
+ if (displayId == DEFAULT_DISPLAY) {
mInitialized = true;
}
}
@@ -184,7 +170,7 @@
return null;
}
- private boolean shouldForceHide(WindowState win) {
+ boolean shouldForceHide(WindowState win) {
final WindowState imeTarget = mService.mInputMethodTarget;
final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleNow() &&
((imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0
@@ -220,377 +206,10 @@
&& !mKeyguardAnimatingIn;
boolean hideDockDivider = win.mAttrs.type == TYPE_DOCK_DIVIDER
&& win.getDisplayContent().getDockedStackLocked() == null;
- return keyguardOn && !allowWhenLocked && (win.getDisplayId() == Display.DEFAULT_DISPLAY)
+ return keyguardOn && !allowWhenLocked && (win.getDisplayId() == DEFAULT_DISPLAY)
|| hideDockDivider;
}
- private void updateWindowsLocked(final int displayId) {
- ++mAnimTransactionSequence;
-
- final WindowList windows = mService.getWindowListLocked(displayId);
-
- final boolean keyguardGoingAwayToShade =
- (mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0;
- final boolean keyguardGoingAwayNoAnimation =
- (mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0;
- final boolean keyguardGoingAwayWithWallpaper =
- (mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0;
-
- if (mKeyguardGoingAway) {
- for (int i = windows.size() - 1; i >= 0; i--) {
- WindowState win = windows.get(i);
- if (!mPolicy.isKeyguardHostWindow(win.mAttrs)) {
- continue;
- }
- final WindowStateAnimator winAnimator = win.mWinAnimator;
- if (mPolicy.isKeyguardShowingAndNotOccluded()) {
- if (!winAnimator.mAnimating) {
- if (DEBUG_KEYGUARD) Slog.d(TAG,
- "updateWindowsLocked: creating delay animation");
-
- // Create a new animation to delay until keyguard is gone on its own.
- winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f);
- winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS);
- winAnimator.mAnimationIsEntrance = false;
- winAnimator.mAnimationStartTime = -1;
- winAnimator.mKeyguardGoingAwayAnimation = true;
- winAnimator.mKeyguardGoingAwayWithWallpaper
- = keyguardGoingAwayWithWallpaper;
- }
- } else {
- if (DEBUG_KEYGUARD) Slog.d(TAG,
- "updateWindowsLocked: StatusBar is no longer keyguard");
- mKeyguardGoingAway = false;
- winAnimator.clearAnimation();
- }
- break;
- }
- }
-
- mForceHiding = KEYGUARD_NOT_SHOWN;
-
- boolean wallpaperInUnForceHiding = false;
- boolean startingInUnForceHiding = false;
- ArrayList<WindowStateAnimator> unForceHiding = null;
- WindowState wallpaper = null;
- final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
- for (int i = windows.size() - 1; i >= 0; i--) {
- WindowState win = windows.get(i);
- WindowStateAnimator winAnimator = win.mWinAnimator;
- final int flags = win.mAttrs.flags;
- boolean canBeForceHidden = mPolicy.canBeForceHidden(win, win.mAttrs);
- boolean shouldBeForceHidden = shouldForceHide(win);
- if (winAnimator.hasSurface()) {
- final boolean wasAnimating = winAnimator.mWasAnimating;
- final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime);
- winAnimator.mWasAnimating = nowAnimating;
- orAnimating(nowAnimating);
-
- if (DEBUG_WALLPAPER) {
- Slog.v(TAG, win + ": wasAnimating=" + wasAnimating +
- ", nowAnimating=" + nowAnimating);
- }
-
- if (wasAnimating && !winAnimator.mAnimating
- && wallpaperController.isWallpaperTarget(win)) {
- mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
- setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- if (DEBUG_LAYOUT_REPEATS) {
- mWindowPlacerLocked.debugLayoutRepeats(
- "updateWindowsAndWallpaperLocked 2",
- getPendingLayoutChanges(Display.DEFAULT_DISPLAY));
- }
- }
-
- if (mPolicy.isForceHiding(win.mAttrs)) {
- if (!wasAnimating && nowAnimating) {
- if (DEBUG_KEYGUARD || DEBUG_ANIM ||
- DEBUG_VISIBILITY) Slog.v(TAG,
- "Animation started that could impact force hide: " + win);
- mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
- setPendingLayoutChanges(displayId,
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- if (DEBUG_LAYOUT_REPEATS) {
- mWindowPlacerLocked.debugLayoutRepeats(
- "updateWindowsAndWallpaperLocked 3",
- getPendingLayoutChanges(displayId));
- }
- mService.mFocusMayChange = true;
- } else if (mKeyguardGoingAway && !nowAnimating) {
- // Timeout!!
- Slog.e(TAG, "Timeout waiting for animation to startup");
- mPolicy.startKeyguardExitAnimation(0, 0);
- mKeyguardGoingAway = false;
- }
- if (win.isReadyForDisplay()) {
- if (nowAnimating && win.mWinAnimator.mKeyguardGoingAwayAnimation) {
- mForceHiding = KEYGUARD_ANIMATING_OUT;
- } else {
- mForceHiding = win.isDrawnLw() ? KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN;
- }
- }
- if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
- "Force hide " + forceHidingToString()
- + " hasSurface=" + win.mHasSurface
- + " policyVis=" + win.mPolicyVisibility
- + " destroying=" + win.mDestroying
- + " parentHidden=" + win.isParentWindowHidden()
- + " vis=" + win.mViewVisibility
- + " hidden=" + win.mToken.hidden
- + " anim=" + win.mWinAnimator.mAnimation);
- } else if (canBeForceHidden) {
- if (shouldBeForceHidden) {
- if (!win.hideLw(false, false)) {
- // Was already hidden
- continue;
- }
- if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
- "Now policy hidden: " + win);
- } else {
- boolean applyExistingExitAnimation = mPostKeyguardExitAnimation != null
- && !mPostKeyguardExitAnimation.hasEnded()
- && !winAnimator.mKeyguardGoingAwayAnimation
- && win.hasDrawnLw()
- && !win.isChildWindow()
- && !win.mIsImWindow
- && displayId == Display.DEFAULT_DISPLAY;
-
- // If the window is already showing and we don't need to apply an existing
- // Keyguard exit animation, skip.
- if (!win.showLw(false, false) && !applyExistingExitAnimation) {
- continue;
- }
- final boolean visibleNow = win.isVisibleNow();
- if (!visibleNow) {
- // Couldn't really show, must showLw() again when win becomes visible.
- win.hideLw(false, false);
- continue;
- }
- if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
- "Now policy shown: " + win);
- if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0
- && !win.isChildWindow()) {
- if (unForceHiding == null) {
- unForceHiding = new ArrayList<>();
- }
- unForceHiding.add(winAnimator);
- if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
- wallpaperInUnForceHiding = true;
- }
- if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
- startingInUnForceHiding = true;
- }
- } else if (applyExistingExitAnimation) {
- // We're already in the middle of an animation. Use the existing
- // animation to bring in this window.
- if (DEBUG_KEYGUARD) Slog.v(TAG,
- "Applying existing Keyguard exit animation to new window: win="
- + win);
-
- Animation a = mPolicy.createForceHideEnterAnimation(false,
- keyguardGoingAwayToShade);
- winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime(),
- STACK_CLIP_BEFORE_ANIM);
- winAnimator.mKeyguardGoingAwayAnimation = true;
- winAnimator.mKeyguardGoingAwayWithWallpaper
- = keyguardGoingAwayWithWallpaper;
- }
- final WindowState currentFocus = mService.mCurrentFocus;
- if (currentFocus == null || currentFocus.mLayer < win.mLayer) {
- // We are showing on top of the current
- // focus, so re-evaluate focus to make
- // sure it is correct.
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG,
- "updateWindowsLocked: setting mFocusMayChange true");
- mService.mFocusMayChange = true;
- }
- }
- if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
- mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
- setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- if (DEBUG_LAYOUT_REPEATS) {
- mWindowPlacerLocked.debugLayoutRepeats(
- "updateWindowsAndWallpaperLocked 4",
- getPendingLayoutChanges(Display.DEFAULT_DISPLAY));
- }
- }
- }
- }
-
- // If the window doesn't have a surface, the only thing we care about is the correct
- // policy visibility.
- else if (canBeForceHidden) {
- if (shouldBeForceHidden) {
- win.hideLw(false, false);
- } else {
- win.showLw(false, false);
- }
- }
-
- final AppWindowToken atoken = win.mAppToken;
- if (winAnimator.mDrawState == READY_TO_SHOW) {
- if (atoken == null || atoken.allDrawn) {
- if (win.performShowLocked()) {
- setPendingLayoutChanges(displayId,
- WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
- if (DEBUG_LAYOUT_REPEATS) {
- mWindowPlacerLocked.debugLayoutRepeats(
- "updateWindowsAndWallpaperLocked 5",
- getPendingLayoutChanges(displayId));
- }
- }
- }
- }
- final AppWindowAnimator appAnimator = winAnimator.mAppAnimator;
- if (appAnimator != null && appAnimator.thumbnail != null) {
- if (appAnimator.thumbnailTransactionSeq != mAnimTransactionSequence) {
- appAnimator.thumbnailTransactionSeq = mAnimTransactionSequence;
- appAnimator.thumbnailLayer = 0;
- }
- if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {
- appAnimator.thumbnailLayer = winAnimator.mAnimLayer;
- }
- }
- if (win.mIsWallpaper) {
- wallpaper = win;
- }
- } // end forall windows
-
- // If we have windows that are being show due to them no longer
- // being force-hidden, apply the appropriate animation to them if animations are not
- // disabled.
- if (unForceHiding != null) {
- if (!keyguardGoingAwayNoAnimation) {
- boolean first = true;
- for (int i=unForceHiding.size()-1; i>=0; i--) {
- final WindowStateAnimator winAnimator = unForceHiding.get(i);
- Animation a = mPolicy.createForceHideEnterAnimation(
- wallpaperInUnForceHiding && !startingInUnForceHiding,
- keyguardGoingAwayToShade);
- if (a != null) {
- if (DEBUG_KEYGUARD) Slog.v(TAG,
- "Starting keyguard exit animation on window " + winAnimator.mWin);
- winAnimator.setAnimation(a, STACK_CLIP_BEFORE_ANIM);
- winAnimator.mKeyguardGoingAwayAnimation = true;
- winAnimator.mKeyguardGoingAwayWithWallpaper
- = keyguardGoingAwayWithWallpaper;
- if (first) {
- mPostKeyguardExitAnimation = a;
- mPostKeyguardExitAnimation.setStartTime(mCurrentTime);
- first = false;
- }
- }
- }
- } else if (mKeyguardGoingAway) {
- mPolicy.startKeyguardExitAnimation(mCurrentTime, 0 /* duration */);
- mKeyguardGoingAway = false;
- }
-
-
- // Wallpaper is going away in un-force-hide motion, animate it as well.
- if (!wallpaperInUnForceHiding && wallpaper != null && !keyguardGoingAwayNoAnimation) {
- if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: wallpaper animating away");
- Animation a = mPolicy.createForceHideWallpaperExitAnimation(
- keyguardGoingAwayToShade);
- if (a != null) {
- wallpaper.mWinAnimator.setAnimation(a);
- }
- }
- }
-
- if (mPostKeyguardExitAnimation != null) {
- // We're in the midst of a keyguard exit animation.
- if (mKeyguardGoingAway) {
- mPolicy.startKeyguardExitAnimation(mCurrentTime +
- mPostKeyguardExitAnimation.getStartOffset(),
- mPostKeyguardExitAnimation.getDuration());
- mKeyguardGoingAway = false;
- }
- // mPostKeyguardExitAnimation might either be ended normally, cancelled, or "orphaned",
- // meaning that the window it was running on was removed. We check for hasEnded() for
- // ended normally and cancelled case, and check the time for the "orphaned" case.
- else if (mPostKeyguardExitAnimation.hasEnded()
- || mCurrentTime - mPostKeyguardExitAnimation.getStartTime()
- > mPostKeyguardExitAnimation.getDuration()) {
- // Done with the animation, reset.
- if (DEBUG_KEYGUARD) Slog.v(TAG, "Done with Keyguard exit animations.");
- mPostKeyguardExitAnimation = null;
- }
- }
-
- final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
- if (winShowWhenLocked != null) {
- mLastShowWinWhenLocked = winShowWhenLocked;
- }
- }
-
- private void updateWallpaperLocked(int displayId) {
- mService.mRoot.getDisplayContentOrCreate(displayId).resetAnimationBackgroundAnimator();
-
- final WindowList windows = mService.getWindowListLocked(displayId);
- WindowState detachedWallpaper = null;
-
- for (int i = windows.size() - 1; i >= 0; i--) {
- final WindowState win = windows.get(i);
- WindowStateAnimator winAnimator = win.mWinAnimator;
- if (winAnimator.mSurfaceController == null || !winAnimator.hasSurface()) {
- continue;
- }
-
- final int flags = win.mAttrs.flags;
-
- // If this window is animating, make a note that we have
- // an animating window and take care of a request to run
- // a detached wallpaper animation.
- if (winAnimator.mAnimating) {
- if (winAnimator.mAnimation != null) {
- if ((flags & FLAG_SHOW_WALLPAPER) != 0
- && winAnimator.mAnimation.getDetachWallpaper()) {
- detachedWallpaper = win;
- }
- final int color = winAnimator.mAnimation.getBackgroundColor();
- if (color != 0) {
- final TaskStack stack = win.getStack();
- if (stack != null) {
- stack.setAnimationBackground(winAnimator, color);
- }
- }
- }
- setAnimating(true);
- }
-
- // If this window's app token is running a detached wallpaper
- // animation, make a note so we can ensure the wallpaper is
- // displayed behind it.
- final AppWindowAnimator appAnimator = winAnimator.mAppAnimator;
- if (appAnimator != null && appAnimator.animation != null
- && appAnimator.animating) {
- if ((flags & FLAG_SHOW_WALLPAPER) != 0
- && appAnimator.animation.getDetachWallpaper()) {
- detachedWallpaper = win;
- }
-
- final int color = appAnimator.animation.getBackgroundColor();
- if (color != 0) {
- final TaskStack stack = win.getStack();
- if (stack != null) {
- stack.setAnimationBackground(winAnimator, color);
- }
- }
- }
- } // end forall windows
-
- if (mWindowDetachedWallpaper != detachedWallpaper) {
- if (DEBUG_WALLPAPER) Slog.v(TAG,
- "Detached wallpaper changed from " + mWindowDetachedWallpaper
- + " to " + detachedWallpaper);
- mWindowDetachedWallpaper = detachedWallpaper;
- mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
- }
- }
-
/** Locked on mService.mWindowMap. */
private void animateLocked(long frameTimeNs) {
if (!mInitialized) {
@@ -606,17 +225,17 @@
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
- if (SHOW_TRANSACTIONS) Slog.i(
- TAG, ">>> OPEN TRANSACTION animateLocked");
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animateLocked");
mService.openSurfaceTransaction();
SurfaceControl.setAnimationTransaction();
try {
+ final AccessibilityController accessibilityController =
+ mService.mAccessibilityController;
final int numDisplays = mDisplayContentsAnimators.size();
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent displayContent = mService.mRoot.getDisplayContentOrCreate(
- displayId);
- displayContent.stepAppWindowsAnimation(mCurrentTime);
+ final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+ dc.stepAppWindowsAnimation(mCurrentTime);
DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
final ScreenRotationAnimation screenRotationAnimation =
@@ -630,11 +249,10 @@
displayAnimator.mScreenRotationAnimation = null;
//TODO (multidisplay): Accessibility supported only for the default display.
- if (mService.mAccessibilityController != null
- && displayId == Display.DEFAULT_DISPLAY) {
- // We just finished rotation animation which means we did not
- // anounce the rotation and waited for it to end, announce now.
- mService.mAccessibilityController.onRotationChangedLocked(
+ if (accessibilityController != null && dc.isDefaultDisplay) {
+ // We just finished rotation animation which means we did not announce
+ // the rotation and waited for it to end, announce now.
+ accessibilityController.onRotationChangedLocked(
mService.getDefaultDisplayContentLocked(), mService.mRotation);
}
}
@@ -642,22 +260,17 @@
// Update animations of all applications, including those
// associated with exiting/removed apps
- updateWindowsLocked(displayId);
- updateWallpaperLocked(displayId);
-
- final WindowList windows = mService.getWindowListLocked(displayId);
- final int N = windows.size();
- for (int j = 0; j < N; j++) {
- windows.get(j).mWinAnimator.prepareSurfaceLocked(true);
- }
+ ++mAnimTransactionSequence;
+ dc.updateWindowsForAnimator(this);
+ dc.updateWallpaperForAnimator(this);
+ dc.prepareWindowSurfaces();
}
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent displayContent = mService.mRoot.getDisplayContentOrCreate(
- displayId);
+ final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
- displayContent.checkAppWindowsReadyToShow();
+ dc.checkAppWindowsReadyToShow();
final ScreenRotationAnimation screenRotationAnimation =
mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
@@ -665,11 +278,11 @@
screenRotationAnimation.updateSurfacesInTransaction();
}
- orAnimating(displayContent.animateDimLayers());
- orAnimating(displayContent.getDockedDividerController().animate(mCurrentTime));
+ orAnimating(dc.animateDimLayers());
+ orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && displayContent.isDefaultDisplay) {
- mService.mAccessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
+ if (accessibilityController != null && dc.isDefaultDisplay) {
+ accessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
}
}
@@ -688,8 +301,7 @@
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
mService.closeSurfaceTransaction();
- if (SHOW_TRANSACTIONS) Slog.i(
- TAG, "<<< CLOSE TRANSACTION animateLocked");
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animateLocked");
}
boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
@@ -726,7 +338,7 @@
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ " mPendingLayoutChanges(DEFAULT_DISPLAY)="
- + Integer.toHexString(getPendingLayoutChanges(Display.DEFAULT_DISPLAY)));
+ + Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY)));
}
}
@@ -758,15 +370,10 @@
pw.print(prefix); pw.print("DisplayContentsAnimator #");
pw.print(mDisplayContentsAnimators.keyAt(i));
pw.println(":");
- DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
- final WindowList windows =
- mService.getWindowListLocked(mDisplayContentsAnimators.keyAt(i));
- final int N = windows.size();
- for (int j = 0; j < N; j++) {
- WindowStateAnimator wanim = windows.get(j).mWinAnimator;
- pw.print(subPrefix); pw.print("Window #"); pw.print(j);
- pw.print(": "); pw.println(wanim);
- }
+ final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
+ final DisplayContent dc =
+ mService.mRoot.getDisplayContentOrCreate(mDisplayContentsAnimators.keyAt(i));
+ dc.dumpWindowAnimators(pw, subPrefix);
if (displayAnimator.mScreenRotationAnimation != null) {
pw.print(subPrefix); pw.println("mScreenRotationAnimation:");
displayAnimator.mScreenRotationAnimation.printTo(subSubPrefix, pw);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 285c40b..db61c3e 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -71,10 +71,14 @@
final protected void setParent(WindowContainer parent) {
mParent = parent;
- // Update full configuration of this container and all its children.
- onConfigurationChanged(mParent != null ? mParent.mFullConfiguration : Configuration.EMPTY);
- // Update merged override configuration of this container and all its children.
- onMergedOverrideConfigurationChanged();
+ // Removing parent usually means that we've detached this entity to destroy it or to attach
+ // to another parent. In both cases we don't need to update the configuration now.
+ if (mParent != null) {
+ // Update full configuration of this container and all its children.
+ onConfigurationChanged(mParent.mFullConfiguration);
+ // Update merged override configuration of this container and all its children.
+ onMergedOverrideConfigurationChanged();
+ }
}
// Temp. holders for a chain of containers we are currently processing.
@@ -95,22 +99,25 @@
+ " is already a child of container=" + child.getParent().getName()
+ " can't add to container=" + getName());
}
- child.setParent(this);
- if (mChildren.isEmpty() || comparator == null) {
- mChildren.add(child);
- return;
- }
-
- final int count = mChildren.size();
- for (int i = 0; i < count; i++) {
- if (comparator.compare(child, mChildren.get(i)) < 0) {
- mChildren.add(i, child);
- return;
+ int positionToAdd = -1;
+ if (comparator != null) {
+ final int count = mChildren.size();
+ for (int i = 0; i < count; i++) {
+ if (comparator.compare(child, mChildren.get(i)) < 0) {
+ positionToAdd = i;
+ break;
+ }
}
}
- mChildren.add(child);
+ if (positionToAdd == -1) {
+ mChildren.add(child);
+ } else {
+ mChildren.add(positionToAdd, child);
+ }
+ // Set the parent after we've actually added a child in case a subclass depends on this.
+ child.setParent(this);
}
/** Adds the input window container has a child of this container at the input index. */
@@ -121,8 +128,9 @@
+ " is already a child of container=" + child.getParent().getName()
+ " can't add to container=" + getName());
}
- child.setParent(this);
mChildren.add(index, child);
+ // Set the parent after we've actually added a child in case a subclass depends on this.
+ child.setParent(this);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 68c2be9..e184e39 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -48,12 +48,10 @@
* <li>replaced windows, which need to live above their normal level, because they anticipate
* an animation</li>.
*/
-public class WindowLayersController {
+class WindowLayersController {
private final WindowManagerService mService;
- private int mInputMethodAnimLayerAdjustment;
-
- public WindowLayersController(WindowManagerService service) {
+ WindowLayersController(WindowManagerService service) {
mService = service;
}
@@ -65,7 +63,7 @@
private WindowState mDockDivider = null;
private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
- final void assignLayersLocked(WindowList windows) {
+ final void assignWindowLayers(WindowList windows) {
if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based on windows=" + windows,
new RuntimeException("here").fillInStackTrace());
@@ -87,7 +85,7 @@
// TODO: Preserved old behavior of code here but not sure comparing
// oldLayer to mAnimLayer and mLayer makes sense...though the
- // worst case would be unintentionalp layer reassignment.
+ // worst case would be unintentional layer reassignment.
if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
layerChanged = true;
anyLayerChanged = true;
@@ -115,41 +113,6 @@
if (DEBUG_LAYERS) logDebugLayers(windows);
}
- void setInputMethodAnimLayerAdjustment(int adj) {
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "Setting im layer adj to " + adj);
- mInputMethodAnimLayerAdjustment = adj;
- final WindowState imw = mService.mInputMethodWindow;
- if (imw != null) {
- imw.adjustAnimLayer(adj);
- }
- for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) {
- final WindowState dialog = mService.mInputMethodDialogs.get(i);
- // TODO: This and other places setting mAnimLayer can probably use WS.adjustAnimLayer,
- // but need to make sure we are not setting things twice for child windows that are
- // already in the list.
- dialog.mWinAnimator.mAnimLayer = dialog.mLayer + adj;
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
- + " anim layer: " + dialog.mWinAnimator.mAnimLayer);
- }
- }
-
- int getSpecialWindowAnimLayerAdjustment(WindowState win) {
- if (win.mIsImWindow) {
- return mInputMethodAnimLayerAdjustment;
- } else if (win.mIsWallpaper) {
- return mService.mWallpaperControllerLocked.getAnimLayerAdjustment();
- }
- return 0;
- }
-
- /**
- * @return The layer used for dimming the apps when dismissing docked/fullscreen stack. Just
- * above all application surfaces.
- */
- int getResizeDimLayer() {
- return (mDockDivider != null) ? mDockDivider.mLayer - 1 : LAYER_OFFSET_DIM;
- }
-
private void logDebugLayers(WindowList windows) {
for (int i = 0, n = windows.size(); i < n; i++) {
final WindowState w = windows.get(i);
@@ -250,21 +213,11 @@
private void assignAnimLayer(WindowState w, int layer) {
w.mLayer = layer;
- w.mWinAnimator.mAnimLayer = w.mLayer + w.getAnimLayerAdjustment() +
- getSpecialWindowAnimLayerAdjustment(w);
+ w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
+ + w.getSpecialWindowAnimLayerAdjustment();
if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0
&& w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
}
}
-
- void dump(PrintWriter pw, String s) {
- if (mInputMethodAnimLayerAdjustment != 0 ||
- mService.mWallpaperControllerLocked.getAnimLayerAdjustment() != 0) {
- pw.print(" mInputMethodAnimLayerAdjustment=");
- pw.print(mInputMethodAnimLayerAdjustment);
- pw.print(" mWallpaperAnimLayerAdjustment=");
- pw.println(mService.mWallpaperControllerLocked.getAnimLayerAdjustment());
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 02d04cc..1b08f16 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -172,6 +172,7 @@
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
@@ -189,6 +190,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -514,9 +516,9 @@
// The root of the device window hierarchy.
RootWindowContainer mRoot;
- // TODO: Move several of this states to the RootWindowContainer
+ // TODO: Move several of this states to the RootWindowContainer or DisplayContent
int mRotation = 0;
- int mLastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
boolean mAltOrientation = false;
private boolean mKeyguardWaitingForActivityDrawn;
@@ -529,8 +531,7 @@
boolean mForceResizableTasks = false;
int getDragLayerLocked() {
- return mPolicy.windowTypeToLayerLw(LayoutParams.TYPE_DRAG) * TYPE_LAYER_MULTIPLIER
- + TYPE_LAYER_OFFSET;
+ return mPolicy.windowTypeToLayerLw(TYPE_DRAG) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
}
class RotationWatcher {
@@ -561,8 +562,8 @@
boolean mClientFreezingScreen = false;
int mAppsFreezingScreen = 0;
- int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
- int mLastKeyguardForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ int mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+ int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
int mLayoutSeq = 0;
@@ -607,6 +608,16 @@
boolean mInputMethodTargetWaitingAnim;
WindowState mInputMethodWindow = null;
+ // TODO: Remove with extreme prejudice! This list is maintained so that we can keep track of
+ // dialogs that should go on top of the IME so they can be re-arranged in the window list any
+ // time the IME changes position in the window list. Normally you would have a dialog be a child
+ // window of the IME, but they don't share the same token since they are added by different
+ // clients. This doesn't really affect what the user sees on screen since this dialogs have an
+ // higher base layer than the IME window, but it will affect users of the window list that
+ // expect the list to represent the order of things on-screen (e.g input service). This makes
+ // the code for managing the window list hard to follow (see all the places it is used).
+ // We can remove the use of this field when we automatically assign layers and z-order the
+ // window list before it is used whenever window container order changes.
final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<>();
/** Temporary list for comparison. Always clear this after use so we don't end up with
@@ -673,10 +684,6 @@
}
}
- WallpaperController mWallpaperControllerLocked;
-
- final WindowLayersController mLayersController;
-
boolean mAnimateWallpaperWithTarget;
// TODO: Move to RootWindowContainer
@@ -685,19 +692,19 @@
PowerManager mPowerManager;
PowerManagerInternal mPowerManagerInternal;
- float mWindowAnimationScaleSetting = 1.0f;
- float mTransitionAnimationScaleSetting = 1.0f;
- float mAnimatorDurationScaleSetting = 1.0f;
- boolean mAnimationsDisabled = false;
+ private float mWindowAnimationScaleSetting = 1.0f;
+ private float mTransitionAnimationScaleSetting = 1.0f;
+ private float mAnimatorDurationScaleSetting = 1.0f;
+ private boolean mAnimationsDisabled = false;
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
final DisplayManager mDisplayManager;
- final Display[] mDisplays;
+ private final Display[] mDisplays;
// Who is holding the screen on.
- Session mHoldingScreenOn;
- PowerManager.WakeLock mHoldingScreenWakeLock;
+ private Session mHoldingScreenOn;
+ private PowerManager.WakeLock mHoldingScreenWakeLock;
boolean mTurnOnScreen;
@@ -708,7 +715,7 @@
DragState mDragState = null;
// For frozen screen animations.
- int mExitAnimId, mEnterAnimId;
+ private int mExitAnimId, mEnterAnimId;
boolean mAnimationScheduled;
@@ -970,15 +977,18 @@
mDisplaySettings = new DisplaySettings();
mDisplaySettings.readSettingsLocked();
- mWallpaperControllerLocked = new WallpaperController(this);
mWindowPlacerLocked = new WindowSurfacePlacer(this);
- mLayersController = new WindowLayersController(this);
mPolicy = policy;
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
- mPointerEventDispatcher = mInputManager != null
- ? new PointerEventDispatcher(mInputManager.monitorInput(TAG_WM)) : null;
+ if(mInputManager != null) {
+ final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
+ mPointerEventDispatcher = inputChannel != null
+ ? new PointerEventDispatcher(inputChannel) : null;
+ } else {
+ mPointerEventDispatcher = null;
+ }
mFxSession = new SurfaceSession();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
@@ -1085,353 +1095,6 @@
}
}
- static boolean canBeImeTarget(WindowState w) {
- final int fl = w.mAttrs.flags
- & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM);
- final int type = w.mAttrs.type;
- if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)
- || type == TYPE_APPLICATION_STARTING) {
- if (DEBUG_INPUT_METHOD) {
- Slog.i(TAG_WM, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding());
- if (!w.isVisibleOrAdding()) {
- Slog.i(TAG_WM, " mSurfaceController=" + w.mWinAnimator.mSurfaceController
- + " relayoutCalled=" + w.mRelayoutCalled
- + " viewVis=" + w.mViewVisibility
- + " policyVis=" + w.mPolicyVisibility
- + " policyVisAfterAnim=" + w.mPolicyVisibilityAfterAnim
- + " parentHidden=" + w.isParentWindowHidden()
- + " exiting=" + w.mAnimatingExit + " destroying=" + w.mDestroying);
- if (w.mAppToken != null) {
- Slog.i(TAG_WM, " mAppToken.hiddenRequested=" + w.mAppToken.hiddenRequested);
- }
- }
- }
- return w.isVisibleOrAdding();
- }
- return false;
- }
-
- /**
- * Dig through the WindowStates and find the one that the Input Method will target.
- * @param willMove
- * @return The index+1 in mWindows of the discovered target.
- */
- int findDesiredInputMethodWindowIndexLocked(boolean willMove) {
- // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the
- // same display. Or even when the current IME/target are not on the same screen as the next
- // IME/target. For now only look for input windows on the main screen.
- WindowList windows = getDefaultWindowListLocked();
- WindowState w = null;
- int i;
- for (i = windows.size() - 1; i >= 0; --i) {
- WindowState win = windows.get(i);
-
- if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG_WM, "Checking window @" + i
- + " " + win + " fl=0x" + Integer.toHexString(win.mAttrs.flags));
- if (canBeImeTarget(win)) {
- w = win;
- //Slog.i(TAG_WM, "Putting input method here!");
-
- // Yet more tricksyness! If this window is a "starting"
- // window, we do actually want to be on top of it, but
- // it is not -really- where input will go. So if the caller
- // is not actually looking to move the IME, look down below
- // for a real window to target...
- if (!willMove
- && w.mAttrs.type == TYPE_APPLICATION_STARTING
- && i > 0) {
- WindowState wb = windows.get(i-1);
- if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) {
- i--;
- w = wb;
- }
- }
- break;
- }
- }
-
- // Now w is either mWindows[0] or an IME (or null if mWindows is empty).
-
- if (DEBUG_INPUT_METHOD && willMove) Slog.v(TAG_WM, "Proposed new IME target: " + w);
-
- // Now, a special case -- if the last target's window is in the
- // process of exiting, and is above the new target, keep on the
- // last target to avoid flicker. Consider for example a Dialog with
- // the IME shown: when the Dialog is dismissed, we want to keep
- // the IME above it until it is completely gone so it doesn't drop
- // behind the dialog or its full-screen scrim.
- final WindowState curTarget = mInputMethodTarget;
- if (curTarget != null
- && curTarget.isDisplayedLw()
- && curTarget.isClosing()
- && (w == null || curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer)) {
- if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Current target higher, not changing");
- return windows.indexOf(curTarget) + 1;
- }
-
- if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Desired input method target="
- + w + " willMove=" + willMove);
-
- if (willMove && w != null) {
- AppWindowToken token = curTarget == null ? null : curTarget.mAppToken;
- if (token != null) {
-
- // Now some fun for dealing with window animations that
- // modify the Z order. We need to look at all windows below
- // the current target that are in this app, finding the highest
- // visible one in layering.
- WindowState highestTarget = null;
- int highestPos = 0;
- if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) {
- WindowList curWindows = token.getDisplayContent().getWindowList();
- int pos = curWindows.indexOf(curTarget);
- while (pos >= 0) {
- WindowState win = curWindows.get(pos);
- if (win.mAppToken != token) {
- break;
- }
- if (!win.mRemoved) {
- if (highestTarget == null || win.mWinAnimator.mAnimLayer >
- highestTarget.mWinAnimator.mAnimLayer) {
- highestTarget = win;
- highestPos = pos;
- }
- }
- pos--;
- }
- }
-
- if (highestTarget != null) {
- if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, mAppTransition + " " + highestTarget
- + " animating=" + highestTarget.mWinAnimator.isAnimationSet()
- + " layer=" + highestTarget.mWinAnimator.mAnimLayer
- + " new layer=" + w.mWinAnimator.mAnimLayer);
-
- if (mAppTransition.isTransitionSet()) {
- // If we are currently setting up for an animation,
- // hold everything until we can find out what will happen.
- mInputMethodTargetWaitingAnim = true;
- mInputMethodTarget = highestTarget;
- return highestPos + 1;
- } else if (highestTarget.mWinAnimator.isAnimationSet() &&
- highestTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer) {
- // If the window we are currently targeting is involved
- // with an animation, and it is on top of the next target
- // we will be over, then hold off on moving until
- // that is done.
- mInputMethodTargetWaitingAnim = true;
- mInputMethodTarget = highestTarget;
- return highestPos + 1;
- }
- }
- }
- }
-
- //Slog.i(TAG_WM, "Placing input method @" + (i+1));
- if (w != null) {
- if (willMove) {
- if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget + " to "
- + w + (SHOW_STACK_CRAWLS ? " Callers=" + Debug.getCallers(4) : ""));
- mInputMethodTarget = w;
- mInputMethodTargetWaitingAnim = false;
- if (w.mAppToken != null) {
- mLayersController.setInputMethodAnimLayerAdjustment(
- w.mAppToken.mAppAnimator.animLayerAdjustment);
- } else {
- mLayersController.setInputMethodAnimLayerAdjustment(0);
- }
- }
-
- // If the docked divider is visible, we still need to go through this whole
- // excercise to find the appropriate input method target (used for animations
- // and dialog adjustments), but for purposes of Z ordering we simply wish to
- // place it above the docked divider. Unless it is already above the divider.
- final WindowState dockedDivider =
- w.getDisplayContent().mDividerControllerLocked.getWindow();
- if (dockedDivider != null && dockedDivider.isVisibleLw()) {
- int dividerIndex = windows.indexOf(dockedDivider);
- if (dividerIndex > 0 && dividerIndex > i) {
- return dividerIndex + 1;
- }
- }
- return i+1;
- }
- if (willMove) {
- if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget + " to null."
- + (SHOW_STACK_CRAWLS ? " Callers=" + Debug.getCallers(4) : ""));
- mInputMethodTarget = null;
- mLayersController.setInputMethodAnimLayerAdjustment(0);
- }
- return -1;
- }
-
- private void reAddWindowToListInOrderLocked(WindowState win) {
- win.mToken.addWindow(win);
- // This is a hack to get all of the child windows added as well at the right position. Child
- // windows should be rare and this case should be rare, so it shouldn't be that big a deal.
- WindowList windows = win.getWindowList();
- int wpos = windows.indexOf(win);
- if (wpos >= 0) {
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "ReAdd removing from " + wpos + ": " + win);
- windows.remove(wpos);
- mWindowsChanged = true;
- win.reAddWindow(wpos);
- }
- }
-
- private void logWindowList(final WindowList windows, String prefix) {
- int N = windows.size();
- while (N > 0) {
- N--;
- Slog.v(TAG_WM, prefix + "#" + N + ": " + windows.get(N));
- }
- }
-
- void moveInputMethodDialogsLocked(int pos) {
- ArrayList<WindowState> dialogs = mInputMethodDialogs;
-
- // TODO(multidisplay): IMEs are only supported on the default display.
- WindowList windows = getDefaultWindowListLocked();
- final int N = dialogs.size();
- if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Removing " + N + " dialogs w/pos=" + pos);
- for (int i=0; i<N; i++) {
- pos = dialogs.get(i).removeFromWindowList(pos);
- }
- if (DEBUG_INPUT_METHOD) {
- Slog.v(TAG_WM, "Window list w/pos=" + pos);
- logWindowList(windows, " ");
- }
-
- if (pos >= 0) {
- // Skip windows owned by the input method.
- if (mInputMethodWindow != null) {
- while (pos < windows.size()) {
- WindowState wp = windows.get(pos);
- if (wp == mInputMethodWindow || wp.getParentWindow() == mInputMethodWindow) {
- pos++;
- continue;
- }
- break;
- }
- }
- if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Adding " + N + " dialogs at pos=" + pos);
- for (int i=0; i<N; i++) {
- WindowState win = dialogs.get(i);
- pos = win.reAddWindow(pos);
- }
- if (DEBUG_INPUT_METHOD) {
- Slog.v(TAG_WM, "Final window list:");
- logWindowList(windows, " ");
- }
- return;
- }
- for (int i=0; i<N; i++) {
- WindowState win = dialogs.get(i);
- reAddWindowToListInOrderLocked(win);
- if (DEBUG_INPUT_METHOD) {
- Slog.v(TAG_WM, "No IM target, final list:");
- logWindowList(windows, " ");
- }
- }
- }
-
- boolean moveInputMethodWindowsIfNeededLocked(boolean needAssignLayers) {
- final WindowState imWin = mInputMethodWindow;
- final int DN = mInputMethodDialogs.size();
- if (imWin == null && DN == 0) {
- return false;
- }
-
- // TODO(multidisplay): IMEs are only supported on the default display.
- WindowList windows = getDefaultWindowListLocked();
-
- int imPos = findDesiredInputMethodWindowIndexLocked(true);
- if (imPos >= 0) {
- // In this case, the input method windows are to be placed
- // immediately above the window they are targeting.
-
- // First check to see if the input method windows are already
- // located here, and contiguous.
- final int N = windows.size();
- final WindowState firstImWin = imPos < N ? windows.get(imPos) : null;
-
- // Figure out the actual input method window that should be
- // at the bottom of their stack.
- WindowState baseImWin = imWin != null ? imWin : mInputMethodDialogs.get(0);
- final WindowState cw = baseImWin.getBottomChild();
- if (cw != null && cw.mSubLayer < 0) {
- baseImWin = cw;
- }
-
- if (firstImWin == baseImWin) {
- // The windows haven't moved... but are they still contiguous?
- // First find the top IM window.
- int pos = imPos+1;
- while (pos < N) {
- if (!(windows.get(pos)).mIsImWindow) {
- break;
- }
- pos++;
- }
- pos++;
- // Now there should be no more input method windows above.
- while (pos < N) {
- if ((windows.get(pos)).mIsImWindow) {
- break;
- }
- pos++;
- }
- if (pos >= N) {
- return false;
- }
- }
-
- if (imWin != null) {
- if (DEBUG_INPUT_METHOD) {
- Slog.v(TAG_WM, "Moving IM from " + imPos);
- logWindowList(windows, " ");
- }
- imPos = imWin.removeFromWindowList(imPos);
- if (DEBUG_INPUT_METHOD) {
- Slog.v(TAG_WM, "List after removing with new pos " + imPos + ":");
- logWindowList(windows, " ");
- }
- imWin.reAddWindow(imPos);
- if (DEBUG_INPUT_METHOD) {
- Slog.v(TAG_WM, "List after moving IM to " + imPos + ":");
- logWindowList(windows, " ");
- }
- if (DN > 0) moveInputMethodDialogsLocked(imPos+1);
- } else {
- moveInputMethodDialogsLocked(imPos);
- }
-
- } else {
- // In this case, the input method windows go in a fixed layer,
- // because they aren't currently associated with a focus window.
-
- if (imWin != null) {
- if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Moving IM from " + imPos);
- imWin.removeFromWindowList(0);
- reAddWindowToListInOrderLocked(imWin);
- if (DEBUG_INPUT_METHOD) {
- Slog.v(TAG_WM, "List with no IM target:");
- logWindowList(windows, " ");
- }
- if (DN > 0) moveInputMethodDialogsLocked(-1);
- } else {
- moveInputMethodDialogsLocked(-1);
- }
-
- }
-
- if (needAssignLayers) {
- mLayersController.assignLayersLocked(windows);
- }
-
- return true;
- }
-
static boolean excludeWindowTypeFromTapOutTask(int windowType) {
switch (windowType) {
case TYPE_STATUS_BAR:
@@ -1725,16 +1388,17 @@
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
mInputMethodDialogs.add(win);
win.mToken.addWindow(win);
- moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
+ displayContent.moveInputMethodDialogs(
+ displayContent.findDesiredInputMethodWindowIndex(true));
imMayMove = false;
} else {
win.mToken.addWindow(win);
if (type == TYPE_WALLPAPER) {
- mWallpaperControllerLocked.clearLastWallpaperTimeoutTime();
+ displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- } else if (mWallpaperControllerLocked.isBelowWallpaperTarget(win)) {
+ } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
// If there is currently a wallpaper being shown, and
// the base layer of the new window is below the current
// layer of the target window, then adjust the wallpaper.
@@ -1749,7 +1413,7 @@
win.applyAdjustForImeIfNeeded();
if (type == TYPE_DOCK_DIVIDER) {
- getDefaultDisplayContentLocked().getDockedDividerController().setWindow(win);
+ mRoot.getDisplayContent(displayId).getDockedDividerController().setWindow(win);
}
final WindowStateAnimator winAnimator = win.mWinAnimator;
@@ -1801,12 +1465,12 @@
}
if (imMayMove) {
- moveInputMethodWindowsIfNeededLocked(false);
+ displayContent.moveInputMethodWindowsIfNeeded(false);
}
- mLayersController.assignLayersLocked(displayContent.getWindowList());
// Don't do layout here, the window must call
// relayout to be displayed, so we'll do it there.
+ displayContent.assignWindowLayers(false /* setLayoutNeeded */);
if (focusChanged) {
mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
@@ -1816,13 +1480,13 @@
if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
+ client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
- if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
+ if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false, displayId)) {
reportNewConfig = true;
}
}
if (reportNewConfig) {
- sendNewConfiguration();
+ sendNewConfiguration(displayId);
}
Binder.restoreCallingIdentity(origId);
@@ -2008,19 +1672,17 @@
atoken.postWindowRemoveStartingWindowCleanup(win);
}
+ final DisplayContent dc = win.getDisplayContent();
if (win.mAttrs.type == TYPE_WALLPAPER) {
- mWallpaperControllerLocked.clearLastWallpaperTimeoutTime();
- getDefaultDisplayContentLocked().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ dc.mWallpaperController.clearLastWallpaperTimeoutTime();
+ dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
- getDefaultDisplayContentLocked().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
- final WindowList windows = win.getWindowList();
- if (windows != null) {
- windows.remove(win);
+ if (dc != null && dc.removeFromWindowList(win)) {
if (!mWindowPlacerLocked.isInLayout()) {
- mLayersController.assignLayersLocked(windows);
- win.setDisplayLayoutNeeded();
+ dc.assignWindowLayers(true /* setLayoutNeeded */);
mWindowPlacerLocked.performSurfacePlacement();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
@@ -2031,7 +1693,7 @@
mInputMonitor.updateInputWindowsLw(true /*force*/);
}
- public void updateAppOpsState() {
+ private void updateAppOpsState() {
synchronized(mWindowMap) {
mRoot.updateAppOpsState();
}
@@ -2125,7 +1787,7 @@
if (mAccessibilityController != null) {
WindowState window = mWindowMap.get(token);
//TODO (multidisplay): Magnification is supported only for the default display.
- if (window != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ if (window != null && window.getDisplayId() == DEFAULT_DISPLAY) {
mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle);
}
}
@@ -2219,11 +1881,13 @@
== PackageManager.PERMISSION_GRANTED;
long origId = Binder.clearCallingIdentity();
+ final int displayId;
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
+ displayId = win.getDisplayId();
WindowStateAnimator winAnimator = win.mWinAnimator;
if (viewVisibility != View.GONE) {
@@ -2392,27 +2056,29 @@
// updateFocusedWindowLocked() already assigned layers so we only need to
// reassign them at this point if the IM window state gets shuffled
boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
- if (imMayMove && (moveInputMethodWindowsIfNeededLocked(false) || toBeDisplayed)) {
- // Little hack here -- we -should- be able to rely on the
- // function to return true if the IME has moved and needs
- // its layer recomputed. However, if the IME was hidden
- // and isn't actually moved in the list, its layer may be
- // out of data so we make sure to recompute it.
- mLayersController.assignLayersLocked(win.getWindowList());
+ final DisplayContent dc = win.getDisplayContent();
+ if (imMayMove && (dc.moveInputMethodWindowsIfNeeded(false) || toBeDisplayed)) {
+ // Little hack here -- we -should- be able to rely on the function to return true if
+ // the IME has moved and needs its layer recomputed. However, if the IME was hidden
+ // and isn't actually moved in the list, its layer may be out of data so we make
+ // sure to recompute it.
+ // TODO: Probably not needed once the window list always has the right z-ordering
+ // when the window hierarchy is updated.
+ dc.assignWindowLayers(false /* setLayoutNeeded */);
}
if (wallpaperMayMove) {
- getDefaultDisplayContentLocked().pendingLayoutChanges |=
+ win.getDisplayContent().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
win.setDisplayLayoutNeeded();
win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
- configChanged = updateOrientationFromAppTokensLocked(false);
+ configChanged = updateOrientationFromAppTokensLocked(false, displayId);
mWindowPlacerLocked.performSurfacePlacement();
if (toBeDisplayed && win.mIsWallpaper) {
- DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
- mWallpaperControllerLocked.updateWallpaperOffset(
+ DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo();
+ dc.mWallpaperController.updateWallpaperOffset(
win, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
}
if (win.mAppToken != null) {
@@ -2457,7 +2123,7 @@
}
if (configChanged) {
- sendNewConfiguration();
+ sendNewConfiguration(displayId);
}
Binder.restoreCallingIdentity(origId);
return result;
@@ -2480,7 +2146,7 @@
// an exit.
win.mAnimatingExit = true;
win.mWinAnimator.mAnimating = true;
- } else if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
+ } else if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) {
// If the wallpaper is currently behind this
// window, we need to change both of them inside
// of a transaction to avoid artifacts.
@@ -2492,9 +2158,8 @@
}
win.destroyOrSaveSurface();
}
- //TODO (multidisplay): Magnification is supported only for the default
- if (mAccessibilityController != null
- && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ // TODO(multidisplay): Magnification is supported only for the default display.
+ if (mAccessibilityController != null && win.getDisplayId() == DEFAULT_DISPLAY) {
mAccessibilityController.onWindowTransitionLocked(win, transit);
}
return focusMayChange;
@@ -2614,7 +2279,7 @@
}
}
- public void finishDrawingWindow(Session session, IWindow client) {
+ void finishDrawingWindow(Session session, IWindow client) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mWindowMap) {
@@ -2623,7 +2288,7 @@
+ (win != null ? win.mWinAnimator.drawStateToString() : "null"));
if (win != null && win.mWinAnimator.finishDrawingLocked()) {
if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
- getDefaultDisplayContentLocked().pendingLayoutChanges |=
+ win.getDisplayContent().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
win.setDisplayLayoutNeeded();
@@ -2751,7 +2416,7 @@
final WindowToken wtoken = removedTokens.get(i);
wtoken.setExiting();
if (wtoken.windowType == TYPE_WALLPAPER) {
- mWallpaperControllerLocked.removeWallpaperToken(wtoken);
+ wtoken.getDisplayContent().mWallpaperController.removeWallpaperToken(wtoken);
}
mInputMonitor.updateInputWindowsLw(true /*force*/);
@@ -2865,107 +2530,35 @@
}
}
- public int getOrientationLocked() {
- if (mDisplayFrozen) {
- if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "Display is frozen, return " + mLastWindowForcedOrientation);
- // If the display is frozen, some activities may be in the middle
- // of restarting, and thus have removed their old window. If the
- // window has the flag to hide the lock screen, then the lock screen
- // can re-appear and inflict its own orientation on us. Keep the
- // orientation stable until this all settles down.
- return mLastWindowForcedOrientation;
- } else if (mPolicy.isKeyguardLocked()) {
- // Use the last orientation the while the display is frozen with the
- // keyguard locked. This could be the keyguard forced orientation or
- // from a SHOW_WHEN_LOCKED window. We don't want to check the show when
- // locked window directly though as things aren't stable while
- // the display is frozen, for example the window could be momentarily unavailable
- // due to activity relaunch.
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display is frozen while keyguard locked, "
- + "return " + mLastOrientation);
- return mLastOrientation;
- }
- } else {
- // TODO(multidisplay): Change to the correct display.
- final WindowList windows = getDefaultWindowListLocked();
- for (int pos = windows.size() - 1; pos >= 0; --pos) {
- WindowState win = windows.get(pos);
- if (win.mAppToken != null) {
- // We hit an application window. so the orientation will be determined by the
- // app window. No point in continuing further.
- break;
- }
- if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) {
- continue;
- }
- int req = win.mAttrs.screenOrientation;
- if(req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND) {
- continue;
- }
-
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req);
- if (mPolicy.isKeyguardHostWindow(win.mAttrs)) {
- mLastKeyguardForcedOrientation = req;
- }
- return (mLastWindowForcedOrientation = req);
- }
- mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
-
- if (mPolicy.isKeyguardLocked()) {
- // The screen is locked and no top system window is requesting an orientation.
- // Return either the orientation of the show-when-locked app (if there is any) or
- // the orientation of the keyguard. No point in searching from the rest of apps.
- WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
- AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
- null : winShowWhenLocked.mAppToken;
- if (appShowWhenLocked != null) {
- int req = appShowWhenLocked.getOrientation();
- if (req == SCREEN_ORIENTATION_BEHIND) {
- req = mLastKeyguardForcedOrientation;
- }
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Done at " + appShowWhenLocked
- + " -- show when locked, return " + req);
- return req;
- }
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "No one is requesting an orientation when the screen is locked");
- return mLastKeyguardForcedOrientation;
- }
- }
-
- // Top system windows are not requesting an orientation. Start searching from apps.
- return getDefaultDisplayContentLocked().getOrientation();
- }
-
@Override
- public Configuration updateOrientationFromAppTokens(
- Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
+ public Configuration updateOrientationFromAppTokens(Configuration currentConfig,
+ IBinder freezeThisOneIfNeeded, int displayId) {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "updateOrientationFromAppTokens()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
- Configuration config = null;
- long ident = Binder.clearCallingIdentity();
-
- synchronized(mWindowMap) {
- config = updateOrientationFromAppTokensLocked(currentConfig,
- freezeThisOneIfNeeded);
+ final Configuration config;
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized(mWindowMap) {
+ config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded,
+ displayId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- Binder.restoreCallingIdentity(ident);
return config;
}
- private Configuration updateOrientationFromAppTokensLocked(
- Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
+ private Configuration updateOrientationFromAppTokensLocked(Configuration currentConfig,
+ IBinder freezeThisOneIfNeeded, int displayId) {
if (!mDisplayReady) {
return null;
}
Configuration config = null;
- if (updateOrientationFromAppTokensLocked(false)) {
+ if (updateOrientationFromAppTokensLocked(false, displayId)) {
// If we changed the orientation but mOrientationChangeComplete is already true,
// we used seamless rotation, and we don't need to freeze the screen.
if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) {
@@ -2974,7 +2567,7 @@
atoken.startFreezingScreen();
}
}
- config = computeNewConfigurationLocked();
+ config = computeNewConfigurationLocked(displayId);
} else if (currentConfig != null) {
// No obvious action we need to take, but if our current state mismatches the activity
@@ -2984,10 +2577,10 @@
// to keep override configs clear of non-empty values (e.g. fontSize).
mTempConfiguration.unset();
mTempConfiguration.updateFrom(currentConfig);
- computeScreenConfigurationLocked(mTempConfiguration);
+ computeScreenConfigurationLocked(mTempConfiguration, displayId);
if (currentConfig.diff(mTempConfiguration) != 0) {
mWaitingForConfig = true;
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
displayContent.setLayoutNeeded();
int anim[] = new int[2];
if (displayContent.isDimming()) {
@@ -3003,30 +2596,28 @@
return config;
}
- /*
- * Determine the new desired orientation of the display, returning
- * a non-null new Configuration if it has changed from the current
- * orientation. IF TRUE IS RETURNED SOMEONE MUST CALL
- * setNewConfiguration() TO TELL THE WINDOW MANAGER IT CAN UNFREEZE THE
- * SCREEN. This will typically be done for you if you call
- * sendNewConfiguration().
+ /**
+ * Determine the new desired orientation of the display, returning a non-null new Configuration
+ * if it has changed from the current orientation. IF TRUE IS RETURNED SOMEONE MUST CALL
+ * {@link #setNewDisplayOverrideConfiguration(Configuration, int)} TO TELL THE WINDOW MANAGER IT
+ * CAN UNFREEZE THE SCREEN. This will typically be done for you if you call
+ * {@link #sendNewConfiguration(int)}.
*
- * The orientation is computed from non-application windows first. If none of
- * the non-application windows specify orientation, the orientation is computed from
- * application tokens.
- * @see android.view.IWindowManager#updateOrientationFromAppTokens(
- * android.os.IBinder)
+ * The orientation is computed from non-application windows first. If none of the
+ * non-application windows specify orientation, the orientation is computed from application
+ * tokens.
+ * @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int)
*/
- boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
+ boolean updateOrientationFromAppTokensLocked(boolean inTransaction, int displayId) {
long ident = Binder.clearCallingIdentity();
try {
- int req = getOrientationLocked();
+ final int req = mRoot.getDisplayContent(displayId).getOrientation();
if (req != mLastOrientation) {
mLastOrientation = req;
//send a message to Policy indicating orientation change to take
//action like disabling/enabling sensors etc.,
mPolicy.setCurrentOrientationLw(req);
- if (updateRotationUncheckedLocked(inTransaction)) {
+ if (updateRotationUncheckedLocked(inTransaction, displayId)) {
// changed
return true;
}
@@ -3052,8 +2643,8 @@
}
@Override
- public int[] setNewConfiguration(Configuration config) {
- if (!checkCallingPermission(MANAGE_APP_TOKENS, "setNewConfiguration()")) {
+ public int[] setNewDisplayOverrideConfiguration(Configuration overrideConfig, int displayId) {
+ if (!checkCallingPermission(MANAGE_APP_TOKENS, "setNewDisplayOverrideConfiguration()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
@@ -3062,7 +2653,7 @@
mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
- return mRoot.setGlobalConfigurationIfNeeded(config);
+ return mRoot.setDisplayOverrideConfigurationIfNeeded(overrideConfig, displayId);
}
}
@@ -3368,7 +2959,8 @@
return false;
}
if (windowShowWallpaper) {
- if (mWallpaperControllerLocked.getWallpaperTarget() == null) {
+ if (wtoken.getDisplayContent().mWallpaperController.getWallpaperTarget()
+ == null) {
// If this theme is requesting a wallpaper, and the wallpaper
// is not currently visible, then this effectively serves as
// an opaque window and our starting window transition animation
@@ -3719,7 +3311,7 @@
if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/)) {
- mLayersController.assignLayersLocked(displayContent.getWindowList());
+ displayContent.assignWindowLayers(false /* setLayoutNeeded */);
}
mInputMonitor.setUpdateInputWindowsNeededLw();
@@ -3815,21 +3407,16 @@
}
}
- public void detachStack(int stackId) {
+ public void removeStack(int stackId) {
synchronized (mWindowMap) {
final TaskStack stack = mStackIdToStack.get(stackId);
if (stack != null) {
stack.removeIfPossible();
+ mStackIdToStack.remove(stackId);
}
}
}
- public void removeStack(int stackId) {
- synchronized (mWindowMap) {
- mStackIdToStack.remove(stackId);
- }
- }
-
public void removeTask(int taskId) {
synchronized (mWindowMap) {
Task task = mTaskIdToTask.get(taskId);
@@ -4550,64 +4137,7 @@
performEnableScreen();
}
- private boolean checkWaitingForWindowsLocked() {
-
- boolean haveBootMsg = false;
- boolean haveApp = false;
- // if the wallpaper service is disabled on the device, we're never going to have
- // wallpaper, don't bother waiting for it
- boolean haveWallpaper = false;
- boolean wallpaperEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableWallpaperService)
- && !mOnlyCore;
- boolean haveKeyguard = true;
- // TODO(multidisplay): Expand to all displays?
- final WindowList windows = getDefaultWindowListLocked();
- final int N = windows.size();
- for (int i=0; i<N; i++) {
- WindowState w = windows.get(i);
- if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
- return true;
- }
- if (w.isDrawnLw()) {
- if (w.mAttrs.type == TYPE_BOOT_PROGRESS) {
- haveBootMsg = true;
- } else if (w.mAttrs.type == TYPE_APPLICATION
- || w.mAttrs.type == TYPE_DRAWN_APPLICATION) {
- haveApp = true;
- } else if (w.mAttrs.type == TYPE_WALLPAPER) {
- haveWallpaper = true;
- } else if (w.mAttrs.type == TYPE_STATUS_BAR) {
- haveKeyguard = mPolicy.isKeyguardDrawnLw();
- }
- }
- }
-
- if (DEBUG_SCREEN_ON || DEBUG_BOOT) {
- Slog.i(TAG_WM, "******** booted=" + mSystemBooted + " msg=" + mShowingBootMessages
- + " haveBoot=" + haveBootMsg + " haveApp=" + haveApp
- + " haveWall=" + haveWallpaper + " wallEnabled=" + wallpaperEnabled
- + " haveKeyguard=" + haveKeyguard);
- }
-
- // If we are turning on the screen to show the boot message,
- // don't do it until the boot message is actually displayed.
- if (!mSystemBooted && !haveBootMsg) {
- return true;
- }
-
- // If we are turning on the screen after the boot is completed
- // normally, don't do so until we have the application and
- // wallpaper.
- if (mSystemBooted && ((!haveApp && !haveKeyguard) ||
- (wallpaperEnabled && !haveWallpaper))) {
- return true;
- }
-
- return false;
- }
-
- public void performEnableScreen() {
+ private void performEnableScreen() {
synchronized(mWindowMap) {
if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled
+ " mForceDisplayEnabled=" + mForceDisplayEnabled
@@ -4623,7 +4153,9 @@
}
// Don't enable the screen until all existing windows have been drawn.
- if (!mForceDisplayEnabled && checkWaitingForWindowsLocked()) {
+ if (!mForceDisplayEnabled
+ // TODO(multidisplay): Expand to all displays?
+ && getDefaultDisplayContentLocked().checkWaitingForWindows()) {
return;
}
@@ -4899,8 +4431,9 @@
}
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
- return screenshotApplicationsInner(null, Display.DEFAULT_DISPLAY, -1, -1, true, 1f,
- Bitmap.Config.ARGB_8888, true);
+ return screenshotApplicationsInner(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */,
+ -1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */,
+ Bitmap.Config.ARGB_8888, true /* wallpaperOnly */);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
@@ -4918,15 +4451,13 @@
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
}
- FgThread.getHandler().post(new Runnable() {
- @Override
- public void run() {
- Bitmap bm = screenshotApplicationsInner(null, Display.DEFAULT_DISPLAY, -1, -1,
- true, 1f, Bitmap.Config.ARGB_8888, false);
- try {
- receiver.send(bm);
- } catch (RemoteException e) {
- }
+ FgThread.getHandler().post(() -> {
+ Bitmap bm = screenshotApplicationsInner(null /* appToken */, DEFAULT_DISPLAY,
+ -1 /* width */, -1 /* height */, true /* includeFullDisplay */,
+ 1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */);
+ try {
+ receiver.send(bm);
+ } catch (RemoteException e) {
}
});
@@ -5193,7 +4724,7 @@
}
ScreenRotationAnimation screenRotationAnimation =
- mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
+ mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
final boolean inRotation = screenRotationAnimation != null &&
screenRotationAnimation.isAnimating();
if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM,
@@ -5327,16 +4858,17 @@
if (mDeferredRotationPauseCount > 0) {
mDeferredRotationPauseCount -= 1;
if (mDeferredRotationPauseCount == 0) {
- boolean changed = updateRotationUncheckedLocked(false);
+ // TODO(multi-display): Update rotation for different displays separately.
+ final int displayId = DEFAULT_DISPLAY;
+ final boolean changed = updateRotationUncheckedLocked(false, displayId);
if (changed) {
- mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
+ mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
}
}
}
- private void updateRotationUnchecked(boolean alwaysSendConfiguration,
- boolean forceRelayout) {
+ private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:"
+ " alwaysSendConfiguration=" + alwaysSendConfiguration
+ " forceRelayout=" + forceRelayout);
@@ -5345,8 +4877,10 @@
try {
final boolean rotationChanged;
+ // TODO(multi-display): Update rotation for different displays separately.
+ int displayId = DEFAULT_DISPLAY;
synchronized (mWindowMap) {
- rotationChanged = updateRotationUncheckedLocked(false);
+ rotationChanged = updateRotationUncheckedLocked(false, displayId);
if (!rotationChanged || forceRelayout) {
getDefaultDisplayContentLocked().setLayoutNeeded();
mWindowPlacerLocked.performSurfacePlacement();
@@ -5354,22 +4888,20 @@
}
if (rotationChanged || alwaysSendConfiguration) {
- sendNewConfiguration();
+ sendNewConfiguration(displayId);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
-
- // TODO(multidisplay): Rotate any display?
/**
- * Updates the current rotation.
+ * Updates the current rotation of the specified display.
*
- * Returns true if the rotation has been changed. In this case YOU
- * MUST CALL sendNewConfiguration() TO UNFREEZE THE SCREEN.
+ * Returns true if the rotation has been changed. In this case YOU MUST CALL
+ * {@link #sendNewConfiguration(int)} TO UNFREEZE THE SCREEN.
*/
- boolean updateRotationUncheckedLocked(boolean inTransaction) {
+ boolean updateRotationUncheckedLocked(boolean inTransaction, int displayId) {
if (mDeferredRotationPauseCount > 0) {
// Rotation updates have been paused temporarily. Defer the update until
// updates have been resumed.
@@ -5378,7 +4910,7 @@
}
ScreenRotationAnimation screenRotationAnimation =
- mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
+ mAnimator.getScreenRotationAnimationLocked(displayId);
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
// Rotation updates cannot be performed while the previous rotation change
// animation is still in progress. Skip this update. We will try updating
@@ -5400,7 +4932,7 @@
return false;
}
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
final WindowList windows = displayContent.getWindowList();
final int oldRotation = mRotation;
@@ -5480,7 +5012,7 @@
startFreezingDisplayLocked(inTransaction, anim[0], anim[1]);
// startFreezingDisplayLocked can reset the ScreenRotationAnimation.
screenRotationAnimation =
- mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
+ mAnimator.getScreenRotationAnimationLocked(displayId);
} else {
// The screen rotation animation uses a screenshot to freeze the screen
// while windows resize underneath.
@@ -5498,7 +5030,7 @@
// the top of the method, the caller is obligated to call computeNewConfigurationLocked().
// By updating the Display info here it will be available to
// computeScreenConfigurationLocked later.
- updateDisplayAndOrientationLocked(mRoot.getConfiguration().uiMode);
+ updateDisplayAndOrientationLocked(displayContent.getConfiguration().uiMode, displayId);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (!inTransaction) {
@@ -5566,7 +5098,7 @@
// Announce rotation only if we will not animate as we already have the
// windows in final state. Otherwise, we make this call at the rotation end.
if (screenRotationAnimation == null && mAccessibilityController != null
- && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ && displayContent.getDisplayId() == DEFAULT_DISPLAY) {
mAccessibilityController.onRotationChangedLocked(getDefaultDisplayContentLocked(),
rotation);
}
@@ -6031,13 +5563,14 @@
}
/**
- * Instruct the Activity Manager to fetch new configurations, update global configuration
- * and broadcast changes to config-changed listeners if appropriate.
+ * Instruct the Activity Manager to fetch and update the current display's configuration and
+ * broadcast them to config-changed listeners if appropriate.
* NOTE: Can't be called with the window manager lock held since it call into activity manager.
*/
- void sendNewConfiguration() {
+ void sendNewConfiguration(int displayId) {
try {
- final boolean configUpdated = mActivityManager.updateConfiguration(null);
+ final boolean configUpdated = mActivityManager.updateDisplayOverrideConfiguration(
+ null /* values */, displayId);
if (!configUpdated) {
// Something changed (E.g. device rotation), but no configuration update is needed.
// E.g. changing device rotation by 180 degrees. Go ahead and perform surface
@@ -6047,7 +5580,7 @@
if (mWaitingForConfig) {
mWaitingForConfig = false;
mLastFinishedFreezeSource = "config-unchanged";
- getDefaultDisplayContentLocked().setLayoutNeeded();
+ mRoot.getDisplayContent(displayId).setLayoutNeeded();
mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -6056,18 +5589,18 @@
}
}
- public Configuration computeNewConfiguration() {
+ public Configuration computeNewConfiguration(int displayId) {
synchronized (mWindowMap) {
- return computeNewConfigurationLocked();
+ return computeNewConfigurationLocked(displayId);
}
}
- private Configuration computeNewConfigurationLocked() {
+ private Configuration computeNewConfigurationLocked(int displayId) {
if (!mDisplayReady) {
return null;
}
- Configuration config = new Configuration();
- computeScreenConfigurationLocked(config);
+ final Configuration config = new Configuration();
+ computeScreenConfigurationLocked(config, displayId);
return config;
}
@@ -6176,9 +5709,8 @@
}
/** Do not call if mDisplayReady == false */
- DisplayInfo updateDisplayAndOrientationLocked(int uiMode) {
- // TODO(multidisplay): For now, apply Configuration to main screen only.
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ private DisplayInfo updateDisplayAndOrientationLocked(int uiMode, int displayId) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == Surface.ROTATION_90
@@ -6239,9 +5771,8 @@
}
/** Do not call if mDisplayReady == false */
- void computeScreenConfigurationLocked(Configuration config) {
- final DisplayInfo displayInfo = updateDisplayAndOrientationLocked(
- config.uiMode);
+ private void computeScreenConfigurationLocked(Configuration config, int displayId) {
+ final DisplayInfo displayInfo = updateDisplayAndOrientationLocked(config.uiMode, displayId);
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
@@ -6803,7 +6334,7 @@
synchronized(mWindowMap) {
// TODO(multidisplay): Accessibility supported only of default desiplay.
if (mAccessibilityController != null && getDefaultDisplayContentLocked()
- .getDisplayId() == Display.DEFAULT_DISPLAY) {
+ .getDisplayId() == DEFAULT_DISPLAY) {
accessibilityController = mAccessibilityController;
}
@@ -6880,11 +6411,9 @@
View view = null;
try {
- final Configuration overrideConfig =
- wtoken != null ? wtoken.getMergedOverrideConfiguration() : null;
view = mPolicy.addStartingWindow(wtoken.token, sd.pkg, sd.theme,
sd.compatInfo, sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo,
- sd.windowFlags, overrideConfig);
+ sd.windowFlags, wtoken.getMergedOverrideConfiguration());
} catch (Exception e) {
Slog.w(TAG_WM, "Exception when adding starting window", e);
}
@@ -7022,21 +6551,7 @@
case WINDOW_FREEZE_TIMEOUT: {
// TODO(multidisplay): Can non-default displays rotate?
synchronized (mWindowMap) {
- Slog.w(TAG_WM, "Window freeze timeout expired.");
- mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
- final WindowList windows = getDefaultWindowListLocked();
- int i = windows.size();
- while (i > 0) {
- i--;
- WindowState w = windows.get(i);
- if (w.mOrientationChanging) {
- w.mOrientationChanging = false;
- w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mDisplayFreezeTime);
- Slog.w(TAG_WM, "Force clearing orientation change: " + w);
- }
- }
- mWindowPlacerLocked.performSurfacePlacement();
+ getDefaultDisplayContentLocked().onWindowFreezeTimeout();
}
break;
}
@@ -7145,8 +6660,9 @@
}
case SEND_NEW_CONFIGURATION: {
- removeMessages(SEND_NEW_CONFIGURATION);
- sendNewConfiguration();
+ removeMessages(SEND_NEW_CONFIGURATION, msg.obj);
+ final int displayId = (Integer) msg.obj;
+ sendNewConfiguration(displayId);
break;
}
@@ -7337,7 +6853,7 @@
break;
case WALLPAPER_DRAW_PENDING_TIMEOUT: {
synchronized (mWindowMap) {
- if (mWallpaperControllerLocked.processWallpaperDrawPendingTimeout()) {
+ if (mRoot.mWallpaperController.processWallpaperDrawPendingTimeout()) {
mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -7483,44 +6999,9 @@
@Override
public boolean inputMethodClientHasFocus(IInputMethodClient client) {
synchronized (mWindowMap) {
- // The focus for the client is the window immediately below
- // where we would place the input method window.
- int idx = findDesiredInputMethodWindowIndexLocked(false);
- if (idx > 0) {
- // TODO(multidisplay): IMEs are only supported on the default display.
- WindowState imFocus = getDefaultWindowListLocked().get(idx-1);
- if (DEBUG_INPUT_METHOD) {
- Slog.i(TAG_WM, "Desired input method target: " + imFocus);
- Slog.i(TAG_WM, "Current focus: " + mCurrentFocus);
- Slog.i(TAG_WM, "Last focus: " + mLastFocus);
- }
- if (imFocus != null) {
- // This may be a starting window, in which case we still want
- // to count it as okay.
- if (imFocus.mAttrs.type == TYPE_APPLICATION_STARTING
- && imFocus.mAppToken != null) {
- // The client has definitely started, so it really should
- // have a window in this app token. Let's look for it.
- final WindowState w = imFocus.mAppToken.getFirstNonStartingWindow();
- if (w != null) {
- if (DEBUG_INPUT_METHOD) Slog.i(TAG_WM,
- "Switching to real app window: " + w);
- imFocus = w;
- }
- }
- if (DEBUG_INPUT_METHOD) {
- Slog.i(TAG_WM, "IM target client: " + imFocus.mSession.mClient);
- if (imFocus.mSession.mClient != null) {
- Slog.i(TAG_WM, "IM target client binder: "
- + imFocus.mSession.mClient.asBinder());
- Slog.i(TAG_WM, "Requesting client binder: " + client.asBinder());
- }
- }
- if (imFocus.mSession.mClient != null &&
- imFocus.mSession.mClient.asBinder() == client.asBinder()) {
- return true;
- }
- }
+ // TODO: multi-display
+ if (getDefaultDisplayContentLocked().inputMethodClientHasFocus(client)) {
+ return true;
}
// Okay, how about this... what is the current focus?
@@ -7569,7 +7050,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != Display.DEFAULT_DISPLAY) {
+ if (displayId != DEFAULT_DISPLAY) {
throw new IllegalArgumentException("Can only set the default display");
}
final long ident = Binder.clearCallingIdentity();
@@ -7604,7 +7085,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != Display.DEFAULT_DISPLAY) {
+ if (displayId != DEFAULT_DISPLAY) {
throw new IllegalArgumentException("Can only set the default display");
}
final long ident = Binder.clearCallingIdentity();
@@ -7687,7 +7168,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != Display.DEFAULT_DISPLAY) {
+ if (displayId != DEFAULT_DISPLAY) {
throw new IllegalArgumentException("Can only set the default display");
}
final long ident = Binder.clearCallingIdentity();
@@ -7736,7 +7217,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != Display.DEFAULT_DISPLAY) {
+ if (displayId != DEFAULT_DISPLAY) {
throw new IllegalArgumentException("Can only set the default display");
}
@@ -7767,7 +7248,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != Display.DEFAULT_DISPLAY) {
+ if (displayId != DEFAULT_DISPLAY) {
throw new IllegalArgumentException("Can only set the default display");
}
@@ -7829,16 +7310,19 @@
configureDisplayPolicyLocked(displayContent);
displayContent.setLayoutNeeded();
- boolean configChanged = updateOrientationFromAppTokensLocked(false);
- final Configuration globalConfig = mRoot.getConfiguration();
- mTempConfiguration.setTo(globalConfig);
- computeScreenConfigurationLocked(mTempConfiguration);
- configChanged |= globalConfig.diff(mTempConfiguration) != 0;
+ final int displayId = displayContent.getDisplayId();
+ boolean configChanged = updateOrientationFromAppTokensLocked(false /* inTransaction */,
+ displayId);
+ final Configuration currentDisplayConfig = displayContent.getConfiguration();
+ mTempConfiguration.setTo(currentDisplayConfig);
+ computeScreenConfigurationLocked(mTempConfiguration, displayId);
+ configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0;
if (configChanged) {
mWaitingForConfig = true;
- startFreezingDisplayLocked(false, 0, 0);
- mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
+ startFreezingDisplayLocked(false /* inTransaction */, 0 /* exitAnim */,
+ 0 /* enterAnim */);
+ mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
mWindowPlacerLocked.performSurfacePlacement();
@@ -7960,14 +7444,17 @@
}
mNoAnimationNotifyOnTransitionFinished.clear();
- mWallpaperControllerLocked.hideDeferredWallpapersIfNeeded();
+ // TODO: multi-display.
+ final DisplayContent dc = getDefaultDisplayContentLocked();
- getDefaultDisplayContentLocked().onAppTransitionDone();
+ dc.mWallpaperController.hideDeferredWallpapersIfNeeded();
+
+ dc.onAppTransitionDone();
changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG_WM,
"Wallpaper layer changed: assigning layers + relayout");
- moveInputMethodWindowsIfNeededLocked(true);
+ dc.moveInputMethodWindowsIfNeeded(true);
mRoot.mWallpaperMayChange = true;
// Since the window list has been rebuilt, focus might have to be recomputed since the
// actual order of windows might have changed again.
@@ -8049,20 +7536,6 @@
}
}
- /** If a window that has an animation specifying a colored background and the current wallpaper
- * is visible, then the color goes *below* the wallpaper so we don't cause the wallpaper to
- * suddenly disappear. */
- int adjustAnimationBackground(WindowStateAnimator winAnimator) {
- WindowList windows = winAnimator.mWin.getWindowList();
- for (int i = windows.size() - 1; i >= 0; --i) {
- WindowState testWin = windows.get(i);
- if (testWin.mIsWallpaper && testWin.isVisibleNow()) {
- return testWin.mWinAnimator.mAnimLayer;
- }
- }
- return winAnimator.mAnimLayer;
- }
-
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
WindowState newFocus = mRoot.computeFocusedWindow();
if (mCurrentFocus != newFocus) {
@@ -8073,7 +7546,7 @@
mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
// TODO(multidisplay): Focused windows on default display only.
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- final boolean imWindowChanged = moveInputMethodWindowsIfNeededLocked(
+ final boolean imWindowChanged = displayContent.moveInputMethodWindowsIfNeeded(
mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
&& mode != UPDATE_FOCUS_WILL_PLACE_SURFACES);
if (imWindowChanged) {
@@ -8098,7 +7571,7 @@
} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
// Client will do the layout, but we need to assign layers
// for handleNewWindowLocked() below.
- mLayersController.assignLayersLocked(displayContent.getWindowList());
+ displayContent.assignWindowLayers(false /* setLayoutNeeded */);
}
}
@@ -8180,16 +7653,7 @@
}
// Check whether the current screen contains any secure content.
- boolean isSecure = false;
- final WindowList windows = getDefaultWindowListLocked();
- final int N = windows.size();
- for (int i = 0; i < N; i++) {
- WindowState ws = windows.get(i);
- if (ws.isOnScreen() && (ws.mAttrs.flags & FLAG_SECURE) != 0) {
- isSecure = true;
- break;
- }
- }
+ boolean isSecure = displayContent.hasSecureWindowOnScreen();
// TODO(multidisplay): rotation on main screen only.
displayContent.updateDisplayInfo();
@@ -8276,7 +7740,7 @@
// to avoid inconsistent states. However, something interesting
// could have actually changed during that time so re-evaluate it
// now to catch that.
- configChanged = updateOrientationFromAppTokensLocked(false);
+ configChanged = updateOrientationFromAppTokensLocked(false, displayId);
// A little kludge: a lot could have happened while the
// display was frozen, so now that we are coming back we
@@ -8290,11 +7754,11 @@
if (updateRotation) {
if (DEBUG_ORIENTATION) Slog.d(TAG_WM, "Performing post-rotate rotation");
- configChanged |= updateRotationUncheckedLocked(false);
+ configChanged |= updateRotationUncheckedLocked(false, displayId);
}
if (configChanged) {
- mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
+ mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
}
@@ -8395,8 +7859,8 @@
}
}
- // TOOD(multidisplay): StatusBar on multiple screens?
- boolean updateStatusBarVisibilityLocked(int visibility) {
+ // TODO(multidisplay): StatusBar on multiple screens?
+ private boolean updateStatusBarVisibilityLocked(int visibility) {
if (mLastDispatchedSystemUiVisibility == visibility) {
return false;
}
@@ -8409,26 +7873,7 @@
mLastDispatchedSystemUiVisibility = visibility;
mInputManager.setSystemUiVisibility(visibility);
- final WindowList windows = getDefaultWindowListLocked();
- final int N = windows.size();
- for (int i = 0; i < N; i++) {
- WindowState ws = windows.get(i);
- try {
- int curValue = ws.mSystemUiVisibility;
- int diff = (curValue ^ visibility) & globalDiff;
- int newValue = (curValue&~diff) | (visibility&diff);
- if (newValue != curValue) {
- ws.mSeq++;
- ws.mSystemUiVisibility = newValue;
- }
- if (newValue != curValue || ws.mAttrs.hasSystemUiListeners) {
- ws.mClient.dispatchSystemUiVisibilityChanged(ws.mSeq,
- visibility, newValue, diff);
- }
- } catch (RemoteException e) {
- // so sorry
- }
- }
+ getDefaultDisplayContentLocked().updateSystemUiVisibility(visibility, globalDiff);
return true;
}
@@ -8573,7 +8018,7 @@
private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) {
pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)");
mRoot.dumpTokens(pw, dumpAll);
- mWallpaperControllerLocked.dumpTokens(pw, " ", dumpAll);
+ mRoot.mWallpaperController.dumpTokens(pw, " ", dumpAll);
if (!mFinishedStarting.isEmpty()) {
pw.println();
pw.println(" Finishing start of application tokens:");
@@ -8757,8 +8202,7 @@
pw.print(" mInputMethodWindow="); pw.println(mInputMethodWindow);
}
mWindowPlacerLocked.dump(pw, " ");
- mWallpaperControllerLocked.dump(pw, " ");
- mLayersController.dump(pw, " ");
+ mRoot.mWallpaperController.dump(pw, " ");
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
@@ -9022,7 +8466,7 @@
}
// TODO: All the display method below should probably be moved into the RootWindowContainer...
- public void createDisplayContentLocked(final Display display) {
+ private void createDisplayContentLocked(final Display display) {
if (display == null) {
throw new IllegalArgumentException("getDisplayContent: display must not be null");
}
@@ -9030,28 +8474,14 @@
}
// There is an inherent assumption that this will never return null.
- public DisplayContent getDefaultDisplayContentLocked() {
- return mRoot.getDisplayContentOrCreate(Display.DEFAULT_DISPLAY);
+ DisplayContent getDefaultDisplayContentLocked() {
+ return mRoot.getDisplayContentOrCreate(DEFAULT_DISPLAY);
}
- public WindowList getDefaultWindowListLocked() {
- return getDefaultDisplayContentLocked().getWindowList();
- }
-
- public DisplayInfo getDefaultDisplayInfoLocked() {
+ private DisplayInfo getDefaultDisplayInfoLocked() {
return getDefaultDisplayContentLocked().getDisplayInfo();
}
- /**
- * Return the list of WindowStates associated on the passed display.
- * @param displayId The screen to return windows from.
- * @return The list of WindowStates on the screen, or null if the there is no screen.
- */
- WindowList getWindowListLocked(final int displayId) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
- return displayContent != null ? displayContent.getWindowList() : null;
- }
-
public void onDisplayAdded(int displayId) {
mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_ADDED, displayId, 0));
}
@@ -9274,15 +8704,19 @@
}
@Override
- public void getStableInsets(Rect outInsets) throws RemoteException {
+ public void getStableInsets(int displayId, Rect outInsets) throws RemoteException {
synchronized (mWindowMap) {
- getStableInsetsLocked(outInsets);
+ getStableInsetsLocked(displayId, outInsets);
}
}
- void getStableInsetsLocked(Rect outInsets) {
- final DisplayInfo di = getDefaultDisplayInfoLocked();
- mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, outInsets);
+ void getStableInsetsLocked(int displayId, Rect outInsets) {
+ outInsets.setEmpty();
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc != null) {
+ final DisplayInfo di = dc.getDisplayInfo();
+ mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, outInsets);
+ }
}
private void getNonDecorInsetsLocked(Rect outInsets) {
@@ -9298,7 +8732,7 @@
*/
public void subtractStableInsets(Rect inOutBounds) {
synchronized (mWindowMap) {
- getStableInsetsLocked(mTmpRect2);
+ getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect2);
final DisplayInfo di = getDefaultDisplayInfoLocked();
mTmpRect.set(0, 0, di.logicalWidth, di.logicalHeight);
subtractInsets(mTmpRect, mTmpRect2, inOutBounds);
@@ -9453,8 +8887,9 @@
if (DEBUG_ORIENTATION) {
Slog.i(TAG, "Performing post-rotate rotation after seamless rotation");
}
- if (updateRotationUncheckedLocked(false)) {
- mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
+ final int displayId = w.getDisplayId();
+ if (updateRotationUncheckedLocked(false, displayId)) {
+ mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
}
}
@@ -9581,24 +9016,7 @@
boolean allWindowsDrawn = false;
synchronized (mWindowMap) {
mWaitingForDrawnCallback = callback;
- final WindowList windows = getDefaultWindowListLocked();
- for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
- final WindowState win = windows.get(winNdx);
- final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs);
- final boolean keyguard = mPolicy.isKeyguardHostWindow(win.mAttrs);
- if (win.isVisibleLw()
- && (win.mAppToken != null || isForceHiding || keyguard)) {
- win.mWinAnimator.mDrawState = DRAW_PENDING;
- // Force add to mResizingWindows.
- win.mLastContentInsets.set(-1, -1, -1, -1);
- mWaitingForDrawn.add(win);
-
- // No need to wait for the windows below Keyguard.
- if (isForceHiding) {
- break;
- }
- }
- }
+ getDefaultDisplayContentLocked().waitForAllWindowsDrawn();
mWindowPlacerLocked.requestTraversal();
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
if (mWaitingForDrawn.isEmpty()) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fa4dbc4..a7b46111 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -38,7 +38,6 @@
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.TimeUtils;
-import android.view.Display;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IApplicationToken;
@@ -86,7 +85,6 @@
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
@@ -890,8 +888,10 @@
getDisplayContent().getLogicalDisplayRect(mTmpRect);
// Override right and/or bottom insets in case if the frame doesn't fit the screen in
// non-fullscreen mode.
- boolean overrideRightInset = !fullscreenTask && mFrame.right > mTmpRect.right;
- boolean overrideBottomInset = !fullscreenTask && mFrame.bottom > mTmpRect.bottom;
+ boolean overrideRightInset = !windowsAreFloating && !fullscreenTask &&
+ mFrame.right > mTmpRect.right;
+ boolean overrideBottomInset = !windowsAreFloating && !fullscreenTask &&
+ mFrame.bottom > mTmpRect.bottom;
mContentInsets.set(mContentFrame.left - mFrame.left,
mContentFrame.top - mFrame.top,
overrideRightInset ? mTmpRect.right - mContentFrame.right
@@ -941,7 +941,7 @@
final DisplayContent displayContent = getDisplayContent();
if (displayContent != null) {
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- mService.mWallpaperControllerLocked.updateWallpaperOffset(
+ getDisplayContent().mWallpaperController.updateWallpaperOffset(
this, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
}
}
@@ -1009,30 +1009,7 @@
@Override
public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) {
- int index = -1;
- WindowState ws = this;
- WindowList windows = getWindowList();
- while (true) {
- if (ws.mAttrs.needsMenuKey != WindowManager.LayoutParams.NEEDS_MENU_UNSET) {
- return ws.mAttrs.needsMenuKey == WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE;
- }
- // If we reached the bottom of the range of windows we are considering,
- // assume no menu is needed.
- if (ws == bottom) {
- return false;
- }
- // The current window hasn't specified whether menu key is needed;
- // look behind it.
- // First, we may need to determine the starting position.
- if (index < 0) {
- index = windows.indexOf(ws);
- }
- index--;
- if (index < 0) {
- return false;
- }
- ws = windows.get(index);
- }
+ return getDisplayContent().getNeedsMenu(this, bottom);
}
@Override
@@ -1692,7 +1669,7 @@
//TODO (multidisplay): Accessibility supported only for the default display.
if (mService.mAccessibilityController != null
- && getDisplayContent().getDisplayId() == Display.DEFAULT_DISPLAY) {
+ && getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {
mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
@@ -1784,14 +1761,14 @@
mReplacementWindow.mSkipEnterAnimationForSeamlessReplacement = false;
}
+ final DisplayContent dc = getDisplayContent();
if (mService.mInputMethodTarget == this) {
- mService.moveInputMethodWindowsIfNeededLocked(false);
+ dc.moveInputMethodWindowsIfNeeded(false);
}
final int type = mAttrs.type;
if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
- final DisplayContent displaycontent = getDisplayContent();
- displaycontent.mTapExcludedWindows.remove(this);
+ dc.mTapExcludedWindows.remove(this);
}
mPolicy.removeWindowLw(this);
@@ -1853,6 +1830,8 @@
// Visibility of the removed window. Will be used later to update orientation later on.
boolean wasVisible = false;
+ final int displayId = getDisplayId();
+
// First, see if we need to run an animation. If we do, we have to hold off on removing the
// window until the animation is done. If the display is frozen, just remove immediately,
// since the animation wouldn't be seen.
@@ -1913,8 +1892,7 @@
mAnimatingExit = true;
}
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null
- && getDisplayId() == Display.DEFAULT_DISPLAY) {
+ if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
mService.mAccessibilityController.onWindowTransitionLocked(this, transit);
}
}
@@ -1944,8 +1922,8 @@
removeImmediately();
// Removing a visible window will effect the computed orientation
// So just update orientation if needed.
- if (wasVisible && mService.updateOrientationFromAppTokensLocked(false)) {
- mService.mH.sendEmptyMessage(SEND_NEW_CONFIGURATION);
+ if (wasVisible && mService.updateOrientationFromAppTokensLocked(false, displayId)) {
+ mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
Binder.restoreCallingIdentity(origId);
@@ -1986,6 +1964,17 @@
return 0;
}
+ int getSpecialWindowAnimLayerAdjustment() {
+ int specialAdjustment = 0;
+ if (mIsImWindow) {
+ specialAdjustment = getDisplayContent().mInputMethodAnimLayerAdjustment;
+ } else if (mIsWallpaper) {
+ specialAdjustment = getDisplayContent().mWallpaperController.getAnimLayerAdjustment();
+ }
+
+ return mLayer + specialAdjustment;
+ }
+
void scheduleAnimationIfDimming() {
final DisplayContent dc = getDisplayContent();
if (dc == null) {
@@ -2562,7 +2551,7 @@
clearAnimatingWithSavedSurface();
mDestroying = true;
mWinAnimator.hide("stopUsingSavedSurface");
- mService.mWallpaperControllerLocked.hideWallpapers(this);
+ getDisplayContent().mWallpaperController.hideWallpapers(this);
}
void markSavedSurfaceExiting() {
@@ -2948,8 +2937,7 @@
applyInsets(outRegion, frame, mGivenVisibleInsets);
break;
case TOUCHABLE_INSETS_REGION: {
- final Region givenTouchableRegion = mGivenTouchableRegion;
- outRegion.set(givenTouchableRegion);
+ outRegion.set(mGivenTouchableRegion);
outRegion.translate(frame.left, frame.top);
break;
}
@@ -2957,7 +2945,7 @@
cropRegionToStackBoundsIfNeeded(outRegion);
}
- void cropRegionToStackBoundsIfNeeded(Region region) {
+ private void cropRegionToStackBoundsIfNeeded(Region region) {
final Task task = getTask();
if (task == null || !task.cropWindowsToStackBounds()) {
return;
@@ -2972,13 +2960,6 @@
region.op(mTmpRect, Region.Op.INTERSECT);
}
- // TODO: This is one reason why WindowList are bad...prime candidate for removal once we
- // figure-out a good way to replace WindowList with WindowContainer hierarchy.
- WindowList getWindowList() {
- final DisplayContent displayContent = getDisplayContent();
- return displayContent == null ? null : displayContent.getWindowList();
- }
-
/**
* Report a focus change. Must be called with no locks held, and consistently
* from the same serialized thread (such as dispatched from a handler).
@@ -3056,8 +3037,7 @@
}
//TODO (multidisplay): Accessibility supported only for the default display.
- if (mService.mAccessibilityController != null
- && getDisplayId() == Display.DEFAULT_DISPLAY) {
+ if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
@@ -3867,7 +3847,7 @@
// Or, it is probably not going to matter anyways if we are successful in getting rid of
// the WindowList concept.
int reAddWindow(int index) {
- final WindowList windows = getWindowList();
+ final DisplayContent dc = getDisplayContent();
// Adding child windows relies on child windows being ordered by mSubLayer using
// {@link #sWindowSubLayerComparator}.
final int childCount = mChildren.size();
@@ -3878,51 +3858,25 @@
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM,
"Re-adding child window at " + index + ": " + child);
mRebuilding = false;
- windows.add(index, this);
+ dc.addToWindowList(this, index);
index++;
winAdded = true;
}
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at " + index + ": " + child);
child.mRebuilding = false;
- windows.add(index, child);
+ dc.addToWindowList(child, index);
index++;
}
if (!winAdded) {
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at " + index + ": " + this);
mRebuilding = false;
- windows.add(index, this);
+ dc.addToWindowList(this, index);
index++;
}
mService.mWindowsChanged = true;
return index;
}
- int removeFromWindowList(int interestingPos) {
- final WindowList windows = getWindowList();
- int wpos = windows.indexOf(this);
- if (wpos < 0) {
- return interestingPos;
- }
-
- if (wpos < interestingPos) interestingPos--;
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing at " + wpos + ": " + this);
- windows.remove(wpos);
- mService.mWindowsChanged = true;
- int childCount = mChildren.size();
- while (childCount > 0) {
- childCount--;
- final WindowState cw = mChildren.get(childCount);
- int cpos = windows.indexOf(cw);
- if (cpos >= 0) {
- if (cpos < interestingPos) interestingPos--;
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM,
- "Temp removing child at " + cpos + ": " + cw);
- windows.remove(cpos);
- }
- }
- return interestingPos;
- }
-
boolean isWindowAnimationSet() {
if (mWinAnimator.isWindowAnimationSet()) {
return true;
@@ -4004,7 +3958,7 @@
}
}
mAnimatingExit = false;
- mService.mWallpaperControllerLocked.hideWallpapers(this);
+ getDisplayContent().mWallpaperController.hideWallpapers(this);
}
boolean clearAnimatingFlags() {
@@ -4071,7 +4025,7 @@
*/
void dispatchWallpaperVisibility(final boolean visible) {
final boolean hideAllowed =
- mService.mWallpaperControllerLocked.mDeferredHideWallpaper == null;
+ getDisplayContent().mWallpaperController.mDeferredHideWallpaper == null;
// Only send notification if the visibility actually changed and we are not trying to hide
// the wallpaper when we are deferring hiding of the wallpaper.
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 53fb99b..2aeb50b 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -262,7 +262,7 @@
mSession = win.mSession;
mAttrType = win.mAttrs.type;
mIsWallpaper = win.mIsWallpaper;
- mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
+ mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
}
public void setAnimation(Animation anim, long startTime, int stackClip) {
@@ -461,8 +461,7 @@
if (mAnimator.mWindowDetachedWallpaper == mWin) {
mAnimator.mWindowDetachedWallpaper = null;
}
- mAnimLayer = mWin.mLayer
- + mService.mLayersController.getSpecialWindowAnimLayerAdjustment(mWin);
+ mAnimLayer = mWin.getSpecialWindowAnimLayerAdjustment();
if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer);
mHasTransformation = false;
mHasLocalTransformation = false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 368484a..c48a585 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -58,6 +58,12 @@
private float mSurfaceW = 0;
private float mSurfaceH = 0;
+ // Initialize to the identity matrix.
+ private float mLastDsdx = 1;
+ private float mLastDtdx = 0;
+ private float mLastDsdy = 0;
+ private float mLastDtdy = 1;
+
private float mSurfaceAlpha = 0;
private int mSurfaceLayer = 0;
@@ -285,6 +291,17 @@
void setMatrixInTransaction(float dsdx, float dtdx, float dsdy, float dtdy,
boolean recoveringMemory) {
+ final boolean matrixChanged = mLastDsdx != dsdx || mLastDtdx != dtdx ||
+ mLastDsdy != dsdy || mLastDtdy != dtdy;
+ if (!matrixChanged) {
+ return;
+ }
+
+ mLastDsdx = dsdx;
+ mLastDtdx = dtdx;
+ mLastDsdy = dsdy;
+ mLastDtdy = dtdy;
+
try {
if (SHOW_TRANSACTIONS) logSurface(
"MATRIX [" + dsdx + "," + dtdx + "," + dsdy + "," + dtdy + "]", null);
@@ -300,7 +317,6 @@
mAnimator.reclaimSomeSurfaceMemory("matrix", true);
}
}
- return;
}
boolean setSizeInTransaction(int width, int height, boolean recoveringMemory) {
@@ -337,6 +353,10 @@
mSurfaceControl.setAlpha(alpha);
mSurfaceLayer = layer;
mSurfaceControl.setLayer(layer);
+ mLastDsdx = dsdx;
+ mLastDtdx = dtdx;
+ mLastDsdy = dsdy;
+ mLastDtdy = dtdy;
mSurfaceControl.setMatrix(
dsdx, dtdx, dsdy, dtdy);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 285a75c..04e00c4 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -96,7 +96,7 @@
public WindowSurfacePlacer(WindowManagerService service) {
mService = service;
- mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
+ mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
}
/**
@@ -383,10 +383,9 @@
}
/**
- * @param windows List of windows on default display.
* @return bitmap indicating if another pass through layout must be made.
*/
- int handleAppTransitionReadyLocked(WindowList windows) {
+ int handleAppTransitionReadyLocked() {
int appsCount = mService.mOpeningApps.size();
if (!transitionGoodToGo(appsCount)) {
return 0;
@@ -429,7 +428,7 @@
// (like the clearAnimatingFlags() above) might affect wallpaper target result.
// Or, the opening app window should be a wallpaper target.
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(displayContent,
- mService.mOpeningApps, windows);
+ mService.mOpeningApps);
final WindowState lowerWallpaperTarget =
mWallpaperControllerLocked.getLowerWallpaperTarget();
@@ -530,9 +529,11 @@
displayContent.setLayoutNeeded();
// TODO(multidisplay): IMEs are only supported on the default display.
- if (windows == mService.getDefaultWindowListLocked()
- && !mService.moveInputMethodWindowsIfNeededLocked(true)) {
- mService.mLayersController.assignLayersLocked(windows);
+ // TODO: Probably not needed once the window list always has the right z-ordering
+ // when the window hierarchy is updated.
+ final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+ if (!dc.moveInputMethodWindowsIfNeeded(true)) {
+ dc.assignWindowLayers(false /*setLayoutNeeded*/);
}
mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
true /*updateInputWindows*/);
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 218972a..afcdc41 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -140,7 +140,7 @@
highestAnimLayer = winHighestAnimLayer;
}
if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
- mService.mLayersController.setInputMethodAnimLayerAdjustment(adj);
+ mDisplayContent.setInputMethodAnimLayerAdjustment(adj);
}
}
return highestAnimLayer;
@@ -188,11 +188,11 @@
}
void addImeWindow(WindowState win) {
- int pos = mService.findDesiredInputMethodWindowIndexLocked(true);
+ int pos = mDisplayContent.findDesiredInputMethodWindowIndex(true);
if (pos < 0) {
addWindow(win);
- mService.moveInputMethodDialogsLocked(pos);
+ mDisplayContent.moveInputMethodDialogs(pos);
return;
}
@@ -203,7 +203,7 @@
addChild(win, null);
}
mService.mWindowsChanged = true;
- mService.moveInputMethodDialogsLocked(pos + 1);
+ mDisplayContent.moveInputMethodDialogs(pos + 1);
}
/** Return the first window in the token window list that isn't a starting window or null. */
@@ -269,7 +269,7 @@
}
void updateWallpaperOffset(int dw, int dh, boolean sync) {
- final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
+ final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
final WindowState wallpaper = mChildren.get(wallpaperNdx);
if (wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, sync)) {
@@ -294,7 +294,7 @@
mDisplayContent.setLayoutNeeded();
}
- final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
+ final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
final WindowState wallpaper = mChildren.get(wallpaperNdx);
if (visible) {
@@ -317,7 +317,7 @@
mDisplayContent.setLayoutNeeded();
}
- final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
+ final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
final WindowState wallpaper = mChildren.get(wallpaperNdx);
@@ -427,11 +427,13 @@
@Override
void removeImmediately() {
- super.removeImmediately();
if (mDisplayContent != null) {
mDisplayContent.removeWindowToken(token);
mService.mRoot.removeWindowTokenIfPossible(token);
}
+ // Needs to occur after the token is removed from the display above to avoid attempt at
+ // duplicate removal of this window container from it's parent.
+ super.removeImmediately();
}
void onDisplayChanged(DisplayContent dc) {
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index d328ade..2c46413 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -70,3 +70,6 @@
android.hardware.power@1.0 \
android.hardware.vibrator@1.0 \
android.hardware.light@2.0 \
+ android.hardware.vr@1.0 \
+ android.hardware.audio.common@2.0 \
+ android.hardware.tv.input@1.0 \
diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp
index 795f6aa..a2b79ff 100644
--- a/services/core/jni/com_android_server_UsbHostManager.cpp
+++ b/services/core/jni/com_android_server_UsbHostManager.cpp
@@ -34,6 +34,8 @@
namespace android
{
+static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
+
static struct parcel_file_descriptor_offsets_t
{
jclass mClass;
@@ -69,10 +71,13 @@
jobject thiz = (jobject)client_data;
const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
- char *manufacturer = usb_device_get_manufacturer_name(device);
- char *product = usb_device_get_product_name(device);
+ char *manufacturer = usb_device_get_manufacturer_name(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ char *product = usb_device_get_product_name(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
int version = usb_device_get_version(device);
- char *serial = usb_device_get_serial(device);
+ char *serial = usb_device_get_serial(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
jstring deviceName = env->NewStringUTF(devname);
jstring manufacturerName = AndroidRuntime::NewStringLatin1(env, manufacturer);
@@ -99,7 +104,8 @@
while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
if (desc->bDescriptorType == USB_DT_CONFIG) {
struct usb_config_descriptor *config = (struct usb_config_descriptor *)desc;
- char *name = usb_device_get_string(device, config->iConfiguration);
+ char *name = usb_device_get_string(device, config->iConfiguration,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
jstring configName = AndroidRuntime::NewStringLatin1(env, name);
env->CallVoidMethod(thiz, method_addUsbConfiguration,
@@ -110,7 +116,8 @@
free(name);
} else if (desc->bDescriptorType == USB_DT_INTERFACE) {
struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
- char *name = usb_device_get_string(device, interface->iInterface);
+ char *name = usb_device_get_string(device, interface->iInterface,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
jstring interfaceName = AndroidRuntime::NewStringLatin1(env, name);
env->CallVoidMethod(thiz, method_addUsbInterface,
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index e34a8e8..179fba0 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -24,6 +24,9 @@
#include "JNIHelp.h"
#include "jni.h"
+#include <android/hardware/tv/input/1.0/ITvInputCallback.h>
+#include <android/hardware/tv/input/1.0/ITvInput.h>
+#include <android/hardware/tv/input/1.0/types.h>
#include <gui/Surface.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
@@ -32,6 +35,20 @@
#include <utils/NativeHandle.h>
#include <hardware/tv_input.h>
+using ::android::hardware::audio::common::V2_0::AudioDevice;
+using ::android::hardware::tv::input::V1_0::ITvInput;
+using ::android::hardware::tv::input::V1_0::ITvInputCallback;
+using ::android::hardware::tv::input::V1_0::Result;
+using ::android::hardware::tv::input::V1_0::TvInputDeviceInfo;
+using ::android::hardware::tv::input::V1_0::TvInputEvent;
+using ::android::hardware::tv::input::V1_0::TvInputEventType;
+using ::android::hardware::tv::input::V1_0::TvInputType;
+using ::android::hardware::tv::input::V1_0::TvStreamConfig;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+
namespace android {
static struct {
@@ -239,9 +256,9 @@
int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface);
int removeStream(int deviceId, int streamId);
- const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
+ const hidl_vec<TvStreamConfig> getStreamConfigs(int deviceId);
- void onDeviceAvailable(const tv_input_device_info_t& info);
+ void onDeviceAvailable(const TvInputDeviceInfo& info);
void onDeviceUnavailable(int deviceId);
void onStreamConfigurationsChanged(int deviceId);
void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
@@ -263,73 +280,60 @@
class NotifyHandler : public MessageHandler {
public:
- NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event);
- ~NotifyHandler();
+ NotifyHandler(JTvInputHal* hal, const TvInputEvent& event);
virtual void handleMessage(const Message& message);
private:
- tv_input_event_t mEvent;
+ TvInputEvent mEvent;
JTvInputHal* mHal;
};
- JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev, const sp<Looper>& looper);
+ class TvInputCallback : public ITvInputCallback {
+ public:
+ TvInputCallback(JTvInputHal* hal);
+ Return<void> notify(const TvInputEvent& event) override;
+ private:
+ JTvInputHal* mHal;
+ };
- static void notify(
- tv_input_device_t* dev, tv_input_event_t* event, void* data);
-
- static void cloneTvInputEvent(
- tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent);
+ JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput, const sp<Looper>& looper);
Mutex mLock;
jweak mThiz;
- tv_input_device_t* mDevice;
- tv_input_callback_ops_t mCallback;
sp<Looper> mLooper;
KeyedVector<int, KeyedVector<int, Connection> > mConnections;
+
+ sp<ITvInput> mTvInput;
+ sp<ITvInputCallback> mTvInputCallback;
};
-JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device,
+JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput,
const sp<Looper>& looper) {
mThiz = env->NewWeakGlobalRef(thiz);
- mDevice = device;
- mCallback.notify = &JTvInputHal::notify;
+ mTvInput = tvInput;
mLooper = looper;
-
- mDevice->initialize(mDevice, &mCallback, this);
+ mTvInputCallback = new TvInputCallback(this);
+ mTvInput->setCallback(mTvInputCallback);
}
JTvInputHal::~JTvInputHal() {
- mDevice->common.close((hw_device_t*)mDevice);
-
+ mTvInput->setCallback(nullptr);
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteWeakGlobalRef(mThiz);
mThiz = NULL;
}
JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
- tv_input_module_t* module = NULL;
- status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID,
- (hw_module_t const**)&module);
- if (err) {
- ALOGE("Couldn't load %s module (%s)",
- TV_INPUT_HARDWARE_MODULE_ID, strerror(-err));
- return 0;
+ // TODO(b/31632518)
+ sp<ITvInput> tvInput = ITvInput::getService("tv.input");
+ if (tvInput == nullptr) {
+ ALOGE("Couldn't get tv.input service.");
+ return nullptr;
}
- tv_input_device_t* device = NULL;
- err = module->common.methods->open(
- (hw_module_t*)module,
- TV_INPUT_DEFAULT_DEVICE,
- (hw_device_t**)&device);
- if (err) {
- ALOGE("Couldn't open %s device (%s)",
- TV_INPUT_DEFAULT_DEVICE, strerror(-err));
- return 0;
- }
-
- return new JTvInputHal(env, thiz, device, looper);
+ return new JTvInputHal(env, thiz, tvInput, looper);
}
int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
@@ -353,16 +357,22 @@
}
if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
// Need to configure stream
- int numConfigs = 0;
- const tv_stream_config_t* configs = NULL;
- if (mDevice->get_stream_configurations(
- mDevice, deviceId, &numConfigs, &configs) != 0) {
- ALOGE("Couldn't get stream configs");
+ Result result = Result::UNKNOWN;
+ hidl_vec<TvStreamConfig> list;
+ mTvInput->getStreamConfigurations(deviceId,
+ [&result, &list](Result res, hidl_vec<TvStreamConfig> configs) {
+ result = res;
+ if (res == Result::OK) {
+ list = configs;
+ }
+ });
+ if (result != Result::OK) {
+ ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, result);
return UNKNOWN_ERROR;
}
int configIndex = -1;
- for (int i = 0; i < numConfigs; ++i) {
- if (configs[i].stream_id == streamId) {
+ for (size_t i = 0; i < list.size(); ++i) {
+ if (list[i].streamId == streamId) {
configIndex = i;
break;
}
@@ -371,34 +381,27 @@
ALOGE("Cannot find a config with given stream ID: %d", streamId);
return BAD_VALUE;
}
- connection.mStreamType = configs[configIndex].type;
+ connection.mStreamType = TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE;
- tv_stream_t stream;
- stream.stream_id = configs[configIndex].stream_id;
- if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
- stream.buffer_producer.width = configs[configIndex].max_video_width;
- stream.buffer_producer.height = configs[configIndex].max_video_height;
- }
- if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
- ALOGE("Couldn't add stream");
+ result = Result::UNKNOWN;
+ const native_handle_t* sidebandStream;
+ mTvInput->openStream(deviceId, streamId,
+ [&result, &sidebandStream](Result res, const native_handle_t* handle) {
+ result = res;
+ if (res == Result::OK) {
+ sidebandStream = handle;
+ }
+ });
+ if (result != Result::OK) {
+ ALOGE("Couldn't open stream. device id:%d stream id:%d result:%d", deviceId, streamId,
+ result);
return UNKNOWN_ERROR;
}
- if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
- connection.mSourceHandle = NativeHandle::create(
- stream.sideband_stream_source_handle, false);
- } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
- if (connection.mThread != NULL) {
- connection.mThread->shutdown();
- }
- connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
- connection.mThread->run("BufferProducerThread");
- }
+ connection.mSourceHandle = NativeHandle::create((native_handle_t*)sidebandStream, false);
}
connection.mSurface = surface;
- if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+ if (connection.mSurface != nullptr) {
connection.mSurface->setSidebandStream(connection.mSourceHandle);
- } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
- connection.mThread->setSurface(surface);
}
return NO_ERROR;
}
@@ -421,8 +424,8 @@
connection.mThread->shutdown();
connection.mThread.clear();
}
- if (mDevice->close_stream(mDevice, deviceId, streamId) != 0) {
- ALOGE("Couldn't remove stream");
+ if (mTvInput->closeStream(deviceId, streamId) != Result::OK) {
+ ALOGE("Couldn't close stream. device id:%d stream id:%d", deviceId, streamId);
return BAD_VALUE;
}
if (connection.mSourceHandle != NULL) {
@@ -431,41 +434,26 @@
return NO_ERROR;
}
-const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
- const tv_stream_config_t* configs = NULL;
- if (mDevice->get_stream_configurations(
- mDevice, deviceId, numConfigs, &configs) != 0) {
- ALOGE("Couldn't get stream configs");
- return NULL;
+const hidl_vec<TvStreamConfig> JTvInputHal::getStreamConfigs(int deviceId) {
+ Result result = Result::UNKNOWN;
+ hidl_vec<TvStreamConfig> list;
+ mTvInput->getStreamConfigurations(deviceId,
+ [&result, &list](Result res, hidl_vec<TvStreamConfig> configs) {
+ result = res;
+ if (res == Result::OK) {
+ list = configs;
+ }
+ });
+ if (result != Result::OK) {
+ ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, result);
}
- return configs;
+ return list;
}
-// static
-void JTvInputHal::notify(
- tv_input_device_t* dev, tv_input_event_t* event, void* data) {
- JTvInputHal* thiz = (JTvInputHal*)data;
- thiz->mLooper->sendMessage(new NotifyHandler(thiz, event), event->type);
-}
-
-// static
-void JTvInputHal::cloneTvInputEvent(
- tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent) {
- memcpy(dstEvent, srcEvent, sizeof(tv_input_event_t));
- if ((srcEvent->type == TV_INPUT_EVENT_DEVICE_AVAILABLE ||
- srcEvent->type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE ||
- srcEvent->type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) &&
- srcEvent->device_info.audio_address != NULL){
- char* audio_address = new char[strlen(srcEvent->device_info.audio_address) + 1];
- strcpy(audio_address, srcEvent->device_info.audio_address);
- dstEvent->device_info.audio_address = audio_address;
- }
-}
-
-void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
+void JTvInputHal::onDeviceAvailable(const TvInputDeviceInfo& info) {
{
Mutex::Autolock autoLock(&mLock);
- mConnections.add(info.device_id, KeyedVector<int, Connection>());
+ mConnections.add(info.deviceId, KeyedVector<int, Connection>());
}
JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -473,17 +461,20 @@
gTvInputHardwareInfoBuilderClassInfo.clazz,
gTvInputHardwareInfoBuilderClassInfo.constructor);
env->CallObjectMethod(
- builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.device_id);
+ builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.deviceId);
env->CallObjectMethod(
builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type);
- if (info.type == TV_INPUT_TYPE_HDMI) {
+ if (info.type == TvInputType::HDMI) {
env->CallObjectMethod(
- builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.hdmi.port_id);
+ builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.portId);
}
env->CallObjectMethod(
- builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audio_type);
- if (info.audio_type != AUDIO_DEVICE_NONE) {
- jstring audioAddress = env->NewStringUTF(info.audio_address);
+ builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audioType);
+ if (info.audioType != AudioDevice::NONE) {
+ uint8_t buffer[info.audioAddress.size() + 1];
+ memcpy(buffer, info.audioAddress.data(), info.audioAddress.size());
+ buffer[info.audioAddress.size()] = '\0';
+ jstring audioAddress = env->NewStringUTF(reinterpret_cast<const char *>(buffer));
env->CallObjectMethod(
builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress);
env->DeleteLocalRef(audioAddress);
@@ -556,48 +547,37 @@
}
}
-JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event) {
+JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const TvInputEvent& event) {
mHal = hal;
- cloneTvInputEvent(&mEvent, event);
-}
-
-JTvInputHal::NotifyHandler::~NotifyHandler() {
- if ((mEvent.type == TV_INPUT_EVENT_DEVICE_AVAILABLE ||
- mEvent.type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE ||
- mEvent.type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) &&
- mEvent.device_info.audio_address != NULL) {
- delete mEvent.device_info.audio_address;
- }
+ mEvent = event;
}
void JTvInputHal::NotifyHandler::handleMessage(const Message& message) {
switch (mEvent.type) {
- case TV_INPUT_EVENT_DEVICE_AVAILABLE: {
- mHal->onDeviceAvailable(mEvent.device_info);
+ case TvInputEventType::DEVICE_AVAILABLE: {
+ mHal->onDeviceAvailable(mEvent.deviceInfo);
} break;
- case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: {
- mHal->onDeviceUnavailable(mEvent.device_info.device_id);
+ case TvInputEventType::DEVICE_UNAVAILABLE: {
+ mHal->onDeviceUnavailable(mEvent.deviceInfo.deviceId);
} break;
- case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
- mHal->onStreamConfigurationsChanged(mEvent.device_info.device_id);
- } break;
- case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
- mHal->onCaptured(mEvent.capture_result.device_id,
- mEvent.capture_result.stream_id,
- mEvent.capture_result.seq,
- true /* succeeded */);
- } break;
- case TV_INPUT_EVENT_CAPTURE_FAILED: {
- mHal->onCaptured(mEvent.capture_result.device_id,
- mEvent.capture_result.stream_id,
- mEvent.capture_result.seq,
- false /* succeeded */);
+ case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: {
+ mHal->onStreamConfigurationsChanged(mEvent.deviceInfo.deviceId);
} break;
default:
ALOGE("Unrecognizable event");
}
}
+JTvInputHal::TvInputCallback::TvInputCallback(JTvInputHal* hal) {
+ mHal = hal;
+}
+
+Return<void> JTvInputHal::TvInputCallback::notify(const TvInputEvent& event) {
+ // TODO(b/32200867): Ensure the event type values are in sync with the framework code.
+ mHal->mLooper->sendMessage(new NotifyHandler(mHal, event), static_cast<int>(event.type));
+ return Void();
+}
+
////////////////////////////////////////////////////////////////////////////////
static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) {
@@ -628,22 +608,22 @@
static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz,
jlong ptr, jint deviceId, jint generation) {
JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
- int numConfigs = 0;
- const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
+ const hidl_vec<TvStreamConfig> configs = tvInputHal->getStreamConfigs(deviceId);
- jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL);
- for (int i = 0; i < numConfigs; ++i) {
+ jobjectArray result = env->NewObjectArray(configs.size(), gTvStreamConfigClassInfo.clazz, NULL);
+ for (size_t i = 0; i < configs.size(); ++i) {
jobject builder = env->NewObject(
gTvStreamConfigBuilderClassInfo.clazz,
gTvStreamConfigBuilderClassInfo.constructor);
env->CallObjectMethod(
- builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id);
+ builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].streamId);
env->CallObjectMethod(
- builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type);
+ builder, gTvStreamConfigBuilderClassInfo.type,
+ TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE);
env->CallObjectMethod(
- builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width);
+ builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].maxVideoWidth);
env->CallObjectMethod(
- builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height);
+ builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].maxVideoHeight);
env->CallObjectMethod(
builder, gTvStreamConfigBuilderClassInfo.generation, generation);
diff --git a/services/core/jni/com_android_server_vr_VrManagerService.cpp b/services/core/jni/com_android_server_vr_VrManagerService.cpp
index 1aba43b..e06e051 100644
--- a/services/core/jni/com_android_server_vr_VrManagerService.cpp
+++ b/services/core/jni/com_android_server_vr_VrManagerService.cpp
@@ -20,44 +20,41 @@
#include <jni.h>
#include <JNIHelp.h>
+#include <android/hardware/vr/1.0/IVr.h>
#include <utils/Errors.h>
#include <utils/Log.h>
-#include <hardware/hardware.h>
-#include <hardware/vr.h>
namespace android {
-static vr_module_t *gVrHardwareModule = NULL;
+using ::android::hardware::vr::V1_0::IVr;
+static sp<IVr> gVr;
static void init_native(JNIEnv* /* env */, jclass /* clazz */) {
- if (gVrHardwareModule != NULL) {
+ // TODO(b/31632518)
+ if (gVr != nullptr) {
// This call path should never be hit.
- ALOGE("%s: May not initialize VR hardware module more than once!", __FUNCTION__);
+ ALOGE("%s: May not initialize IVr interface module more than once!", __FUNCTION__);
return;
}
- int err = hw_get_module(VR_HARDWARE_MODULE_ID, (hw_module_t const**)&gVrHardwareModule);
- if (err) {
- ALOGW("%s: Could not open VR hardware module, error %s (%d).", __FUNCTION__,
- strerror(-err), err);
+ gVr = IVr::getService("vr");
+ if (gVr == nullptr) {
+ ALOGW("%s: Could not open IVr interface", __FUNCTION__);
return;
}
- // Call init method if implemented.
- if (gVrHardwareModule->init) {
- gVrHardwareModule->init(gVrHardwareModule);
- }
+ gVr->init();
}
static void setVrMode_native(JNIEnv* /* env */, jclass /* clazz */, jboolean enabled) {
- if (gVrHardwareModule == NULL) {
+ if (gVr == nullptr) {
// There is no VR hardware module implemented, do nothing.
return;
}
// Call set_vr_mode method, this must be implemented if the HAL exists.
- gVrHardwareModule->set_vr_mode(gVrHardwareModule, static_cast<bool>(enabled));
+ gVr->setVrMode(static_cast<bool>(enabled));
}
static const JNINativeMethod method_table[] = {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 88d8dd0..eb85e89 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -22,6 +22,7 @@
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.content.pm.PackageManager.GET_UNINSTALLED_PACKAGES;
+import static com.android.internal.logging.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -50,6 +51,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
+import android.app.admin.PasswordMetrics;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
import android.app.admin.SystemUpdatePolicy;
@@ -130,6 +132,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
@@ -317,6 +320,12 @@
*/
private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
+ /**
+ * Strings logged with {@link #PROVISIONING_ENTRY_POINT_ADB}.
+ */
+ private static final String LOG_TAG_PROFILE_OWNER = "profile-owner";
+ private static final String LOG_TAG_DEVICE_OWNER = "device-owner";
+
final Context mContext;
final Injector mInjector;
final IPackageManager mIPackageManager;
@@ -417,14 +426,7 @@
}
public static class DevicePolicyData {
- int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
- int mActivePasswordLength = 0;
- int mActivePasswordUpperCase = 0;
- int mActivePasswordLowerCase = 0;
- int mActivePasswordLetters = 0;
- int mActivePasswordNumeric = 0;
- int mActivePasswordSymbols = 0;
- int mActivePasswordNonLetter = 0;
+ @NonNull PasswordMetrics mActivePasswordMetrics = new PasswordMetrics();
int mFailedPasswordAttempts = 0;
int mUserHandle;
@@ -590,31 +592,23 @@
final DeviceAdminInfo info;
- int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-
- static final int DEF_MINIMUM_PASSWORD_LENGTH = 0;
- int minimumPasswordLength = DEF_MINIMUM_PASSWORD_LENGTH;
static final int DEF_PASSWORD_HISTORY_LENGTH = 0;
int passwordHistoryLength = DEF_PASSWORD_HISTORY_LENGTH;
- static final int DEF_MINIMUM_PASSWORD_UPPER_CASE = 0;
- int minimumPasswordUpperCase = DEF_MINIMUM_PASSWORD_UPPER_CASE;
-
- static final int DEF_MINIMUM_PASSWORD_LOWER_CASE = 0;
- int minimumPasswordLowerCase = DEF_MINIMUM_PASSWORD_LOWER_CASE;
-
+ static final int DEF_MINIMUM_PASSWORD_LENGTH = 0;
static final int DEF_MINIMUM_PASSWORD_LETTERS = 1;
- int minimumPasswordLetters = DEF_MINIMUM_PASSWORD_LETTERS;
-
+ static final int DEF_MINIMUM_PASSWORD_UPPER_CASE = 0;
+ static final int DEF_MINIMUM_PASSWORD_LOWER_CASE = 0;
static final int DEF_MINIMUM_PASSWORD_NUMERIC = 1;
- int minimumPasswordNumeric = DEF_MINIMUM_PASSWORD_NUMERIC;
-
static final int DEF_MINIMUM_PASSWORD_SYMBOLS = 1;
- int minimumPasswordSymbols = DEF_MINIMUM_PASSWORD_SYMBOLS;
-
static final int DEF_MINIMUM_PASSWORD_NON_LETTER = 0;
- int minimumPasswordNonLetter = DEF_MINIMUM_PASSWORD_NON_LETTER;
+ @NonNull
+ PasswordMetrics minimumPasswordMetrics = new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, DEF_MINIMUM_PASSWORD_LENGTH,
+ DEF_MINIMUM_PASSWORD_LETTERS, DEF_MINIMUM_PASSWORD_UPPER_CASE,
+ DEF_MINIMUM_PASSWORD_LOWER_CASE, DEF_MINIMUM_PASSWORD_NUMERIC,
+ DEF_MINIMUM_PASSWORD_SYMBOLS, DEF_MINIMUM_PASSWORD_NON_LETTER);
static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0;
long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK;
@@ -720,13 +714,15 @@
out.startTag(null, TAG_POLICIES);
info.writePoliciesToXml(out);
out.endTag(null, TAG_POLICIES);
- if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+ if (minimumPasswordMetrics.quality
+ != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
out.startTag(null, TAG_PASSWORD_QUALITY);
- out.attribute(null, ATTR_VALUE, Integer.toString(passwordQuality));
+ out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.quality));
out.endTag(null, TAG_PASSWORD_QUALITY);
- if (minimumPasswordLength != DEF_MINIMUM_PASSWORD_LENGTH) {
+ if (minimumPasswordMetrics.length != DEF_MINIMUM_PASSWORD_LENGTH) {
out.startTag(null, TAG_MIN_PASSWORD_LENGTH);
- out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordLength));
+ out.attribute(
+ null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.length));
out.endTag(null, TAG_MIN_PASSWORD_LENGTH);
}
if(passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
@@ -734,34 +730,40 @@
out.attribute(null, ATTR_VALUE, Integer.toString(passwordHistoryLength));
out.endTag(null, TAG_PASSWORD_HISTORY_LENGTH);
}
- if (minimumPasswordUpperCase != DEF_MINIMUM_PASSWORD_UPPER_CASE) {
+ if (minimumPasswordMetrics.upperCase != DEF_MINIMUM_PASSWORD_UPPER_CASE) {
out.startTag(null, TAG_MIN_PASSWORD_UPPERCASE);
- out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordUpperCase));
+ out.attribute(
+ null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.upperCase));
out.endTag(null, TAG_MIN_PASSWORD_UPPERCASE);
}
- if (minimumPasswordLowerCase != DEF_MINIMUM_PASSWORD_LOWER_CASE) {
+ if (minimumPasswordMetrics.lowerCase != DEF_MINIMUM_PASSWORD_LOWER_CASE) {
out.startTag(null, TAG_MIN_PASSWORD_LOWERCASE);
- out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordLowerCase));
+ out.attribute(
+ null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.lowerCase));
out.endTag(null, TAG_MIN_PASSWORD_LOWERCASE);
}
- if (minimumPasswordLetters != DEF_MINIMUM_PASSWORD_LETTERS) {
+ if (minimumPasswordMetrics.letters != DEF_MINIMUM_PASSWORD_LETTERS) {
out.startTag(null, TAG_MIN_PASSWORD_LETTERS);
- out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordLetters));
+ out.attribute(
+ null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.letters));
out.endTag(null, TAG_MIN_PASSWORD_LETTERS);
}
- if (minimumPasswordNumeric != DEF_MINIMUM_PASSWORD_NUMERIC) {
+ if (minimumPasswordMetrics.numeric != DEF_MINIMUM_PASSWORD_NUMERIC) {
out.startTag(null, TAG_MIN_PASSWORD_NUMERIC);
- out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordNumeric));
+ out.attribute(
+ null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.numeric));
out.endTag(null, TAG_MIN_PASSWORD_NUMERIC);
}
- if (minimumPasswordSymbols != DEF_MINIMUM_PASSWORD_SYMBOLS) {
+ if (minimumPasswordMetrics.symbols != DEF_MINIMUM_PASSWORD_SYMBOLS) {
out.startTag(null, TAG_MIN_PASSWORD_SYMBOLS);
- out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordSymbols));
+ out.attribute(
+ null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.symbols));
out.endTag(null, TAG_MIN_PASSWORD_SYMBOLS);
}
- if (minimumPasswordNonLetter > DEF_MINIMUM_PASSWORD_NON_LETTER) {
+ if (minimumPasswordMetrics.nonLetter > DEF_MINIMUM_PASSWORD_NON_LETTER) {
out.startTag(null, TAG_MIN_PASSWORD_NONLETTER);
- out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordNonLetter));
+ out.attribute(
+ null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.nonLetter));
out.endTag(null, TAG_MIN_PASSWORD_NONLETTER);
}
}
@@ -960,31 +962,31 @@
if (TAG_POLICIES.equals(tag)) {
info.readPoliciesFromXml(parser);
} else if (TAG_PASSWORD_QUALITY.equals(tag)) {
- passwordQuality = Integer.parseInt(
+ minimumPasswordMetrics.quality = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_LENGTH.equals(tag)) {
- minimumPasswordLength = Integer.parseInt(
+ minimumPasswordMetrics.length = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_PASSWORD_HISTORY_LENGTH.equals(tag)) {
passwordHistoryLength = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_UPPERCASE.equals(tag)) {
- minimumPasswordUpperCase = Integer.parseInt(
+ minimumPasswordMetrics.upperCase = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_LOWERCASE.equals(tag)) {
- minimumPasswordLowerCase = Integer.parseInt(
+ minimumPasswordMetrics.lowerCase = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_LETTERS.equals(tag)) {
- minimumPasswordLetters = Integer.parseInt(
+ minimumPasswordMetrics.letters = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_NUMERIC.equals(tag)) {
- minimumPasswordNumeric = Integer.parseInt(
+ minimumPasswordMetrics.numeric = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_SYMBOLS.equals(tag)) {
- minimumPasswordSymbols = Integer.parseInt(
+ minimumPasswordMetrics.symbols = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) {
- minimumPasswordNonLetter = Integer.parseInt(
+ minimumPasswordMetrics.nonLetter = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
maximumTimeToUnlock = Long.parseLong(
@@ -1224,23 +1226,23 @@
}
}
pw.print(prefix); pw.print("passwordQuality=0x");
- pw.println(Integer.toHexString(passwordQuality));
+ pw.println(Integer.toHexString(minimumPasswordMetrics.quality));
pw.print(prefix); pw.print("minimumPasswordLength=");
- pw.println(minimumPasswordLength);
+ pw.println(minimumPasswordMetrics.length);
pw.print(prefix); pw.print("passwordHistoryLength=");
pw.println(passwordHistoryLength);
pw.print(prefix); pw.print("minimumPasswordUpperCase=");
- pw.println(minimumPasswordUpperCase);
+ pw.println(minimumPasswordMetrics.upperCase);
pw.print(prefix); pw.print("minimumPasswordLowerCase=");
- pw.println(minimumPasswordLowerCase);
+ pw.println(minimumPasswordMetrics.lowerCase);
pw.print(prefix); pw.print("minimumPasswordLetters=");
- pw.println(minimumPasswordLetters);
+ pw.println(minimumPasswordMetrics.letters);
pw.print(prefix); pw.print("minimumPasswordNumeric=");
- pw.println(minimumPasswordNumeric);
+ pw.println(minimumPasswordMetrics.numeric);
pw.print(prefix); pw.print("minimumPasswordSymbols=");
- pw.println(minimumPasswordSymbols);
+ pw.println(minimumPasswordMetrics.symbols);
pw.print(prefix); pw.print("minimumPasswordNonLetter=");
- pw.println(minimumPasswordNonLetter);
+ pw.println(minimumPasswordMetrics.nonLetter);
pw.print(prefix); pw.print("maximumTimeToUnlock=");
pw.println(maximumTimeToUnlock);
pw.print(prefix); pw.print("strongAuthUnlockTimeout=");
@@ -2273,20 +2275,17 @@
out.endTag(null, "failed-password-attempts");
}
- if (policy.mActivePasswordQuality != 0 || policy.mActivePasswordLength != 0
- || policy.mActivePasswordUpperCase != 0 || policy.mActivePasswordLowerCase != 0
- || policy.mActivePasswordLetters != 0 || policy.mActivePasswordNumeric != 0
- || policy.mActivePasswordSymbols != 0 || policy.mActivePasswordNonLetter != 0) {
+ final PasswordMetrics metrics = policy.mActivePasswordMetrics;
+ if (!metrics.isDefault()) {
out.startTag(null, "active-password");
- out.attribute(null, "quality", Integer.toString(policy.mActivePasswordQuality));
- out.attribute(null, "length", Integer.toString(policy.mActivePasswordLength));
- out.attribute(null, "uppercase", Integer.toString(policy.mActivePasswordUpperCase));
- out.attribute(null, "lowercase", Integer.toString(policy.mActivePasswordLowerCase));
- out.attribute(null, "letters", Integer.toString(policy.mActivePasswordLetters));
- out.attribute(null, "numeric", Integer
- .toString(policy.mActivePasswordNumeric));
- out.attribute(null, "symbols", Integer.toString(policy.mActivePasswordSymbols));
- out.attribute(null, "nonletter", Integer.toString(policy.mActivePasswordNonLetter));
+ out.attribute(null, "quality", Integer.toString(metrics.quality));
+ out.attribute(null, "length", Integer.toString(metrics.length));
+ out.attribute(null, "uppercase", Integer.toString(metrics.upperCase));
+ out.attribute(null, "lowercase", Integer.toString(metrics.lowerCase));
+ out.attribute(null, "letters", Integer.toString(metrics.letters));
+ out.attribute(null, "numeric", Integer.toString(metrics.numeric));
+ out.attribute(null, "symbols", Integer.toString(metrics.symbols));
+ out.attribute(null, "nonletter", Integer.toString(metrics.nonLetter));
out.endTag(null, "active-password");
}
@@ -2456,22 +2455,15 @@
policy.mPasswordOwner = Integer.parseInt(
parser.getAttributeValue(null, "value"));
} else if ("active-password".equals(tag)) {
- policy.mActivePasswordQuality = Integer.parseInt(
- parser.getAttributeValue(null, "quality"));
- policy.mActivePasswordLength = Integer.parseInt(
- parser.getAttributeValue(null, "length"));
- policy.mActivePasswordUpperCase = Integer.parseInt(
- parser.getAttributeValue(null, "uppercase"));
- policy.mActivePasswordLowerCase = Integer.parseInt(
- parser.getAttributeValue(null, "lowercase"));
- policy.mActivePasswordLetters = Integer.parseInt(
- parser.getAttributeValue(null, "letters"));
- policy.mActivePasswordNumeric = Integer.parseInt(
- parser.getAttributeValue(null, "numeric"));
- policy.mActivePasswordSymbols = Integer.parseInt(
- parser.getAttributeValue(null, "symbols"));
- policy.mActivePasswordNonLetter = Integer.parseInt(
- parser.getAttributeValue(null, "nonletter"));
+ final PasswordMetrics m = policy.mActivePasswordMetrics;
+ m.quality = Integer.parseInt(parser.getAttributeValue(null, "quality"));
+ m.length = Integer.parseInt(parser.getAttributeValue(null, "length"));
+ m.upperCase = Integer.parseInt(parser.getAttributeValue(null, "uppercase"));
+ m.lowerCase = Integer.parseInt(parser.getAttributeValue(null, "lowercase"));
+ m.letters = Integer.parseInt(parser.getAttributeValue(null, "letters"));
+ m.numeric = Integer.parseInt(parser.getAttributeValue(null, "numeric"));
+ m.symbols = Integer.parseInt(parser.getAttributeValue(null, "symbols"));
+ m.nonLetter = Integer.parseInt(parser.getAttributeValue(null, "nonletter"));
} else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) {
policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME));
} else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
@@ -2517,19 +2509,12 @@
final long identity = mInjector.binderClearCallingIdentity();
try {
int actualPasswordQuality = mLockPatternUtils.getActivePasswordQuality(userHandle);
- if (actualPasswordQuality < policy.mActivePasswordQuality) {
+ if (actualPasswordQuality < policy.mActivePasswordMetrics.quality) {
Slog.w(LOG_TAG, "Active password quality 0x"
- + Integer.toHexString(policy.mActivePasswordQuality)
+ + Integer.toHexString(policy.mActivePasswordMetrics.quality)
+ " does not match actual quality 0x"
+ Integer.toHexString(actualPasswordQuality));
- policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
- policy.mActivePasswordLength = 0;
- policy.mActivePasswordUpperCase = 0;
- policy.mActivePasswordLowerCase = 0;
- policy.mActivePasswordLetters = 0;
- policy.mActivePasswordNumeric = 0;
- policy.mActivePasswordSymbols = 0;
- policy.mActivePasswordNonLetter = 0;
+ policy.mActivePasswordMetrics = new PasswordMetrics();
}
} finally {
mInjector.binderRestoreCallingIdentity(identity);
@@ -3119,8 +3104,8 @@
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- if (ap.passwordQuality != quality) {
- ap.passwordQuality = quality;
+ if (ap.minimumPasswordMetrics.quality != quality) {
+ ap.minimumPasswordMetrics.quality = quality;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3137,7 +3122,7 @@
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.passwordQuality : mode;
+ return admin != null ? admin.minimumPasswordMetrics.quality : mode;
}
// Return the strictest policy across all participating admins.
@@ -3146,8 +3131,8 @@
final int N = admins.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = admins.get(i);
- if (mode < admin.passwordQuality) {
- mode = admin.passwordQuality;
+ if (mode < admin.minimumPasswordMetrics.quality) {
+ mode = admin.minimumPasswordMetrics.quality;
}
}
return mode;
@@ -3206,8 +3191,8 @@
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- if (ap.minimumPasswordLength != length) {
- ap.minimumPasswordLength = length;
+ if (ap.minimumPasswordMetrics.length != length) {
+ ap.minimumPasswordMetrics.length = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3224,7 +3209,7 @@
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordLength : length;
+ return admin != null ? admin.minimumPasswordMetrics.length : length;
}
// Return the strictest policy across all participating admins.
@@ -3233,8 +3218,8 @@
final int N = admins.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = admins.get(i);
- if (length < admin.minimumPasswordLength) {
- length = admin.minimumPasswordLength;
+ if (length < admin.minimumPasswordMetrics.length) {
+ length = admin.minimumPasswordMetrics.length;
}
}
return length;
@@ -3461,8 +3446,8 @@
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- if (ap.minimumPasswordUpperCase != length) {
- ap.minimumPasswordUpperCase = length;
+ if (ap.minimumPasswordMetrics.upperCase != length) {
+ ap.minimumPasswordMetrics.upperCase = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3479,7 +3464,7 @@
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordUpperCase : length;
+ return admin != null ? admin.minimumPasswordMetrics.upperCase : length;
}
// Return the strictest policy across all participating admins.
@@ -3488,8 +3473,8 @@
final int N = admins.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = admins.get(i);
- if (length < admin.minimumPasswordUpperCase) {
- length = admin.minimumPasswordUpperCase;
+ if (length < admin.minimumPasswordMetrics.upperCase) {
+ length = admin.minimumPasswordMetrics.upperCase;
}
}
return length;
@@ -3502,8 +3487,8 @@
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- if (ap.minimumPasswordLowerCase != length) {
- ap.minimumPasswordLowerCase = length;
+ if (ap.minimumPasswordMetrics.lowerCase != length) {
+ ap.minimumPasswordMetrics.lowerCase = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3520,7 +3505,7 @@
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordLowerCase : length;
+ return admin != null ? admin.minimumPasswordMetrics.lowerCase : length;
}
// Return the strictest policy across all participating admins.
@@ -3529,8 +3514,8 @@
final int N = admins.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = admins.get(i);
- if (length < admin.minimumPasswordLowerCase) {
- length = admin.minimumPasswordLowerCase;
+ if (length < admin.minimumPasswordMetrics.lowerCase) {
+ length = admin.minimumPasswordMetrics.lowerCase;
}
}
return length;
@@ -3546,8 +3531,8 @@
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- if (ap.minimumPasswordLetters != length) {
- ap.minimumPasswordLetters = length;
+ if (ap.minimumPasswordMetrics.letters != length) {
+ ap.minimumPasswordMetrics.letters = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3564,7 +3549,7 @@
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordLetters : length;
+ return admin != null ? admin.minimumPasswordMetrics.letters : length;
}
// Return the strictest policy across all participating admins.
@@ -3576,8 +3561,8 @@
if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
continue;
}
- if (length < admin.minimumPasswordLetters) {
- length = admin.minimumPasswordLetters;
+ if (length < admin.minimumPasswordMetrics.letters) {
+ length = admin.minimumPasswordMetrics.letters;
}
}
return length;
@@ -3593,8 +3578,8 @@
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- if (ap.minimumPasswordNumeric != length) {
- ap.minimumPasswordNumeric = length;
+ if (ap.minimumPasswordMetrics.numeric != length) {
+ ap.minimumPasswordMetrics.numeric = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3611,7 +3596,7 @@
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordNumeric : length;
+ return admin != null ? admin.minimumPasswordMetrics.numeric : length;
}
// Return the strictest policy across all participating admins.
@@ -3623,8 +3608,8 @@
if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
continue;
}
- if (length < admin.minimumPasswordNumeric) {
- length = admin.minimumPasswordNumeric;
+ if (length < admin.minimumPasswordMetrics.numeric) {
+ length = admin.minimumPasswordMetrics.numeric;
}
}
return length;
@@ -3640,8 +3625,8 @@
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- if (ap.minimumPasswordSymbols != length) {
- ap.minimumPasswordSymbols = length;
+ if (ap.minimumPasswordMetrics.symbols != length) {
+ ap.minimumPasswordMetrics.symbols = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3658,7 +3643,7 @@
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordSymbols : length;
+ return admin != null ? admin.minimumPasswordMetrics.symbols : length;
}
// Return the strictest policy across all participating admins.
@@ -3670,8 +3655,8 @@
if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
continue;
}
- if (length < admin.minimumPasswordSymbols) {
- length = admin.minimumPasswordSymbols;
+ if (length < admin.minimumPasswordMetrics.symbols) {
+ length = admin.minimumPasswordMetrics.symbols;
}
}
return length;
@@ -3687,8 +3672,8 @@
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- if (ap.minimumPasswordNonLetter != length) {
- ap.minimumPasswordNonLetter = length;
+ if (ap.minimumPasswordMetrics.nonLetter != length) {
+ ap.minimumPasswordMetrics.nonLetter = length;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
@@ -3705,7 +3690,7 @@
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordNonLetter : length;
+ return admin != null ? admin.minimumPasswordMetrics.nonLetter : length;
}
// Return the strictest policy across all participating admins.
@@ -3717,8 +3702,8 @@
if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
continue;
}
- if (length < admin.minimumPasswordNonLetter) {
- length = admin.minimumPasswordNonLetter;
+ if (length < admin.minimumPasswordMetrics.nonLetter) {
+ length = admin.minimumPasswordMetrics.nonLetter;
}
}
return length;
@@ -3759,28 +3744,28 @@
private boolean isActivePasswordSufficientForUserLocked(
DevicePolicyData policy, int userHandle, boolean parent) {
final int requiredPasswordQuality = getPasswordQuality(null, userHandle, parent);
- if (policy.mActivePasswordQuality < requiredPasswordQuality) {
+ if (policy.mActivePasswordMetrics.quality < requiredPasswordQuality) {
return false;
}
if (requiredPasswordQuality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
- && policy.mActivePasswordLength < getPasswordMinimumLength(
+ && policy.mActivePasswordMetrics.length < getPasswordMinimumLength(
null, userHandle, parent)) {
return false;
}
if (requiredPasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
return true;
}
- return policy.mActivePasswordUpperCase >= getPasswordMinimumUpperCase(
+ return policy.mActivePasswordMetrics.upperCase >= getPasswordMinimumUpperCase(
null, userHandle, parent)
- && policy.mActivePasswordLowerCase >= getPasswordMinimumLowerCase(
+ && policy.mActivePasswordMetrics.lowerCase >= getPasswordMinimumLowerCase(
null, userHandle, parent)
- && policy.mActivePasswordLetters >= getPasswordMinimumLetters(
+ && policy.mActivePasswordMetrics.letters >= getPasswordMinimumLetters(
null, userHandle, parent)
- && policy.mActivePasswordNumeric >= getPasswordMinimumNumeric(
+ && policy.mActivePasswordMetrics.numeric >= getPasswordMinimumNumeric(
null, userHandle, parent)
- && policy.mActivePasswordSymbols >= getPasswordMinimumSymbols(
+ && policy.mActivePasswordMetrics.symbols >= getPasswordMinimumSymbols(
null, userHandle, parent)
- && policy.mActivePasswordNonLetter >= getPasswordMinimumNonLetter(
+ && policy.mActivePasswordMetrics.nonLetter >= getPasswordMinimumNonLetter(
null, userHandle, parent);
}
@@ -3974,8 +3959,9 @@
if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
+ final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
- int realQuality = LockPatternUtils.computePasswordQuality(password);
+ final int realQuality = metrics.quality;
if (realQuality < quality
&& quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
Slog.w(LOG_TAG, "resetPassword: password quality 0x"
@@ -3993,67 +3979,48 @@
return false;
}
if (quality == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
- int letters = 0;
- int uppercase = 0;
- int lowercase = 0;
- int numbers = 0;
- int symbols = 0;
- int nonletter = 0;
- for (int i = 0; i < password.length(); i++) {
- char c = password.charAt(i);
- if (c >= 'A' && c <= 'Z') {
- letters++;
- uppercase++;
- } else if (c >= 'a' && c <= 'z') {
- letters++;
- lowercase++;
- } else if (c >= '0' && c <= '9') {
- numbers++;
- nonletter++;
- } else {
- symbols++;
- nonletter++;
- }
- }
int neededLetters = getPasswordMinimumLetters(null, userHandle, /* parent */ false);
- if(letters < neededLetters) {
- Slog.w(LOG_TAG, "resetPassword: number of letters " + letters
+ if(metrics.letters < neededLetters) {
+ Slog.w(LOG_TAG, "resetPassword: number of letters " + metrics.letters
+ " does not meet required number of letters " + neededLetters);
return false;
}
- int neededNumbers = getPasswordMinimumNumeric(null, userHandle, /* parent */ false);
- if (numbers < neededNumbers) {
- Slog.w(LOG_TAG, "resetPassword: number of numerical digits " + numbers
+ int neededNumeric = getPasswordMinimumNumeric(null, userHandle, /* parent */ false);
+ if (metrics.numeric < neededNumeric) {
+ Slog.w(LOG_TAG, "resetPassword: number of numerical digits " + metrics.numeric
+ " does not meet required number of numerical digits "
- + neededNumbers);
+ + neededNumeric);
return false;
}
int neededLowerCase = getPasswordMinimumLowerCase(
null, userHandle, /* parent */ false);
- if (lowercase < neededLowerCase) {
- Slog.w(LOG_TAG, "resetPassword: number of lowercase letters " + lowercase
+ if (metrics.lowerCase < neededLowerCase) {
+ Slog.w(LOG_TAG, "resetPassword: number of lowercase letters "
+ + metrics.lowerCase
+ " does not meet required number of lowercase letters "
+ neededLowerCase);
return false;
}
int neededUpperCase = getPasswordMinimumUpperCase(
null, userHandle, /* parent */ false);
- if (uppercase < neededUpperCase) {
- Slog.w(LOG_TAG, "resetPassword: number of uppercase letters " + uppercase
+ if (metrics.upperCase < neededUpperCase) {
+ Slog.w(LOG_TAG, "resetPassword: number of uppercase letters "
+ + metrics.upperCase
+ " does not meet required number of uppercase letters "
+ neededUpperCase);
return false;
}
int neededSymbols = getPasswordMinimumSymbols(null, userHandle, /* parent */ false);
- if (symbols < neededSymbols) {
- Slog.w(LOG_TAG, "resetPassword: number of special symbols " + symbols
+ if (metrics.symbols < neededSymbols) {
+ Slog.w(LOG_TAG, "resetPassword: number of special symbols " + metrics.symbols
+ " does not meet required number of special symbols " + neededSymbols);
return false;
}
int neededNonLetter = getPasswordMinimumNonLetter(
null, userHandle, /* parent */ false);
- if (nonletter < neededNonLetter) {
- Slog.w(LOG_TAG, "resetPassword: number of non-letter characters " + nonletter
+ if (metrics.nonLetter < neededNonLetter) {
+ Slog.w(LOG_TAG, "resetPassword: number of non-letter characters "
+ + metrics.nonLetter
+ " does not meet required number of non-letter characters "
+ neededNonLetter);
return false;
@@ -4842,8 +4809,7 @@
}
@Override
- public void setActivePasswordState(int quality, int length, int letters, int uppercase,
- int lowercase, int numbers, int symbols, int nonletter, int userHandle) {
+ public void setActivePasswordState(PasswordMetrics metrics, int userHandle) {
if (!mHasFeature) {
return;
}
@@ -4856,21 +4822,14 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
- validateQualityConstant(quality);
+ validateQualityConstant(metrics.quality);
DevicePolicyData policy = getUserData(userHandle);
long ident = mInjector.binderClearCallingIdentity();
try {
synchronized (this) {
- policy.mActivePasswordQuality = quality;
- policy.mActivePasswordLength = length;
- policy.mActivePasswordLetters = letters;
- policy.mActivePasswordLowerCase = lowercase;
- policy.mActivePasswordUpperCase = uppercase;
- policy.mActivePasswordNumeric = numbers;
- policy.mActivePasswordSymbols = symbols;
- policy.mActivePasswordNonLetter = nonletter;
+ policy.mActivePasswordMetrics = metrics;
policy.mFailedPasswordAttempts = 0;
saveSettingsLocked(userHandle);
updatePasswordExpirationsLocked(userHandle);
@@ -5891,6 +5850,11 @@
mInjector.binderRestoreCallingIdentity(ident);
}
+ if (isAdb()) {
+ // Log device owner provisioning was started using adb.
+ MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_DEVICE_OWNER);
+ }
+
mOwners.setDeviceOwner(admin, ownerName, userId);
mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
@@ -6073,6 +6037,11 @@
throw new IllegalArgumentException("Not active admin: " + who);
}
+ if (isAdb()) {
+ // Log profile owner provisioning was started using adb.
+ MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_PROFILE_OWNER);
+ }
+
mOwners.setProfileOwner(who, ownerName, userHandle);
mOwners.writeProfileOwner(userHandle);
Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
@@ -6206,8 +6175,7 @@
boolean transitionCheckNeeded = true;
// Calling identity/permission checks.
- final int callingUid = mInjector.binderGetCallingUid();
- if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+ if (isAdb()) {
// ADB shell can only move directly from un-managed to finalized as part of directly
// setting profile-owner or device-owner.
if (getUserProvisioningState(userHandle) !=
@@ -6410,8 +6378,7 @@
throw new IllegalStateException("Trying to set the profile owner, but the user "
+ "already has a device owner.");
}
- int callingUid = mInjector.binderGetCallingUid();
- if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+ if (isAdb()) {
if (hasUserSetupCompleted(userHandle)
&& hasIncompatibleAccountsLocked(userHandle, owner)) {
throw new IllegalStateException("Not allowed to set the profile owner because "
@@ -6431,13 +6398,11 @@
* permission.
*/
private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId) {
- int callingUid = mInjector.binderGetCallingUid();
- boolean isAdb = callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
- if (!isAdb) {
+ if (!isAdb()) {
enforceCanManageProfileAndDeviceOwners();
}
- final int code = checkSetDeviceOwnerPreConditionLocked(owner, userId, isAdb);
+ final int code = checkSetDeviceOwnerPreConditionLocked(owner, userId, isAdb());
switch (code) {
case CODE_OK:
return;
@@ -8423,10 +8388,10 @@
/**
* Returns true if specified admin is allowed to limit passwords and has a
- * {@code passwordQuality} of at least {@code minPasswordQuality}
+ * {@code minimumPasswordMetrics.quality} of at least {@code minPasswordQuality}
*/
private static boolean isLimitPasswordAllowed(ActiveAdmin admin, int minPasswordQuality) {
- if (admin.passwordQuality < minPasswordQuality) {
+ if (admin.minimumPasswordMetrics.quality < minPasswordQuality) {
return false;
}
return admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
@@ -9483,4 +9448,9 @@
return false;
}
}
+
+ private boolean isAdb() {
+ final int callingUid = mInjector.binderGetCallingUid();
+ return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index deb5238..62947eb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -118,6 +118,8 @@
import java.util.Timer;
import java.util.TimerTask;
+import static android.view.Display.DEFAULT_DISPLAY;
+
public final class SystemServer {
private static final String TAG = "SystemServer";
@@ -732,7 +734,7 @@
traceBeginAndSlog("IpConnectivityMetrics");
mSystemServiceManager.startService(IpConnectivityMetrics.class);
- Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ traceEnd();
traceBeginAndSlog("PinnerService");
mSystemServiceManager.startService(PinnerService.class);
@@ -1403,7 +1405,7 @@
// Update the configuration for this context by hand, because we're going
// to start using it before the config change done in wm.systemReady() will
// propagate to it.
- Configuration config = wm.computeNewConfiguration();
+ final Configuration config = wm.computeNewConfiguration(DEFAULT_DISPLAY);
DisplayMetrics metrics = new DisplayMetrics();
WindowManager w = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
w.getDefaultDisplay().getMetrics(metrics);
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index ffbea9f..8dd05b1 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -40,6 +40,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.system.PacketSocketAddress;
+import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -369,6 +370,13 @@
if (PACKET_DBG) {
Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
}
+ if (e.errorCode == DhcpErrorEvent.DHCP_NO_COOKIE) {
+ int snetTagId = 0x534e4554;
+ String bugId = "31850211";
+ int uid = -1;
+ String data = DhcpPacket.ParseException.class.getName();
+ EventLog.writeEvent(snetTagId, bugId, uid, data);
+ }
logError(e.errorCode);
}
}
diff --git a/services/tests/runtests.py b/services/tests/runtests.py
new file mode 100755
index 0000000..35fec90f
--- /dev/null
+++ b/services/tests/runtests.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+
+# 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.
+
+import os
+import subprocess
+import sys
+
+INSTRUMENTED_PACKAGE_RUNNER = ('com.android.frameworks.servicestests/'
+ 'android.support.test.runner.AndroidJUnitRunner')
+
+PACKAGE_WHITELIST = (
+ 'android.net',
+ 'com.android.server.connectivity',
+)
+
+COLOR_RED = '\033[0;31m'
+COLOR_NONE ='\033[0m'
+
+def run(shell_command, echo=True):
+ if echo:
+ print '%s + %s%s' % (
+ COLOR_RED,
+ echo if isinstance(echo, str) else shell_command,
+ COLOR_NONE)
+ return subprocess.check_call(shell_command, shell=True)
+
+
+def main():
+ build_top = os.environ.get('ANDROID_BUILD_TOP', None)
+ out_dir = os.environ.get('OUT', None)
+ if build_top is None or out_dir is None:
+ print 'You need to source and lunch before you can use this script'
+ return 1
+
+ print 'Building tests...'
+ run('make -j32 -C %s -f build/core/main.mk '
+ 'MODULES-IN-frameworks-base-services-tests-servicestests' % build_top,
+ echo='mmma -j32 %s/frameworks/base/services/tests/servicestests' %
+ build_top)
+
+ print 'Installing tests...'
+ run('adb root')
+ run('adb wait-for-device')
+ apk_path = (
+ '%s/data/app/FrameworksServicesTests/FrameworksServicesTests.apk' %
+ out_dir)
+ run('adb install -r -g "%s"' % apk_path)
+
+ print 'Running tests...'
+ if len(sys.argv) != 1:
+ run('adb shell am instrument -w "%s" %s' %
+ (INSTRUMENTED_PACKAGE_RUNNER, ' '.join(sys.argv[1:])))
+ return 0
+
+ # It would be nice if the activity manager accepted a list of packages, but
+ # in lieu of that...
+ for package in PACKAGE_WHITELIST:
+ run('adb shell am instrument -w -e package %s %s' %
+ (package, INSTRUMENTED_PACKAGE_RUNNER))
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 0f180af..826aeeb 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -68,6 +68,7 @@
import android.os.SystemClock;
import android.provider.Settings;
import android.test.AndroidTestCase;
+import android.test.FlakyTest;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
@@ -215,8 +216,20 @@
mService.waitForIdle();
assertEquals(i, mCm.getNetworkCapabilities(n).getSignalStrength());
}
+ }
+
+ @FlakyTest(tolerance = 3)
+ public void testNotWaitingForIdleCausesRaceConditions() {
+ // Bring up a network that we can use to send messages to ConnectivityService.
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ waitFor(cv);
+ Network n = mWiFiNetworkAgent.getNetwork();
+ assertNotNull(n);
// Ensure that not calling waitForIdle causes a race condition.
+ final int attempts = 50; // Causes the test to take about 200ms on bullhead-eng.
for (int i = 0; i < attempts; i++) {
mWiFiNetworkAgent.setSignalStrength(i);
if (i != mCm.getNetworkCapabilities(n).getSignalStrength()) {
@@ -236,6 +249,7 @@
private final IdleableHandlerThread mHandlerThread;
private final ConditionVariable mDisconnected = new ConditionVariable();
private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
+ private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
private int mScore;
private NetworkAgent mNetworkAgent;
private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
@@ -291,6 +305,11 @@
mRedirectUrl = redirectUrl;
mNetworkStatusReceived.open();
}
+
+ @Override
+ protected void preventAutomaticReconnect() {
+ mPreventReconnectReceived.open();
+ }
};
// Waits for the NetworkAgent to be registered, which includes the creation of the
// NetworkMonitor.
@@ -375,11 +394,6 @@
mWrappedNetworkMonitor.gen204ProbeResult = 200;
mWrappedNetworkMonitor.gen204ProbeRedirectUrl = redirectUrl;
connect(false);
- waitFor(new Criteria() { public boolean get() {
- NetworkCapabilities caps = mCm.getNetworkCapabilities(getNetwork());
- return caps != null && caps.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL);} });
- mWrappedNetworkMonitor.gen204ProbeResult = 500;
- mWrappedNetworkMonitor.gen204ProbeRedirectUrl = null;
}
public void disconnect() {
@@ -391,6 +405,10 @@
return new Network(mNetworkAgent.netId);
}
+ public ConditionVariable getPreventReconnectReceived() {
+ return mPreventReconnectReceived;
+ }
+
public ConditionVariable getDisconnectedCV() {
return mDisconnected;
}
@@ -597,6 +615,7 @@
@Override
protected CaptivePortalProbeResult isCaptivePortal() {
+ if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); }
return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null);
}
}
@@ -743,6 +762,9 @@
mService.systemReady();
mCm = new WrappedConnectivityManager(getContext(), mService);
mCm.bindProcessToNetwork(null);
+
+ // Ensure that the default setting for Captive Portals is used for most tests
+ setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
}
public void tearDown() throws Exception {
@@ -1710,6 +1732,47 @@
validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
}
+ @LargeTest
+ public void testAvoidOrIgnoreCaptivePortals() {
+ final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
+ final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
+ mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback);
+
+ final TestNetworkCallback validatedCallback = new TestNetworkCallback();
+ final NetworkRequest validatedRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_VALIDATED).build();
+ mCm.registerNetworkCallback(validatedRequest, validatedCallback);
+
+ setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_AVOID);
+ // Bring up a network with a captive portal.
+ // Expect it to fail to connect and not result in any callbacks.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ String firstRedirectUrl = "http://example.com/firstPath";
+
+ ConditionVariable disconnectCv = mWiFiNetworkAgent.getDisconnectedCV();
+ ConditionVariable avoidCv = mWiFiNetworkAgent.getPreventReconnectReceived();
+ mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
+ waitFor(disconnectCv);
+ waitFor(avoidCv);
+
+ assertNoCallbacks(captivePortalCallback, validatedCallback);
+
+ // Now test ignore mode.
+ setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE);
+
+ // Bring up a network with a captive portal.
+ // Since we're ignoring captive portals, the network will validate.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ String secondRedirectUrl = "http://example.com/secondPath";
+ mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
+
+ // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
+ validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ // But there should be no CaptivePortal callback.
+ captivePortalCallback.assertNoCallback();
+ }
+
@SmallTest
public void testInvalidNetworkSpecifier() {
boolean execptionCalled = true;
@@ -1850,6 +1913,11 @@
mCm.unregisterNetworkCallback(cellNetworkCallback);
}
+ private void setCaptivePortalMode(int mode) {
+ ContentResolver cr = mServiceContext.getContentResolver();
+ Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode);
+ }
+
private void setMobileDataAlwaysOn(boolean enable) {
ContentResolver cr = mServiceContext.getContentResolver();
Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0);
diff --git a/services/tests/servicestests/src/com/android/server/am/ConfigurationContainerTests.java b/services/tests/servicestests/src/com/android/server/am/ConfigurationContainerTests.java
index 92c442e..fd2fb4b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ConfigurationContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ConfigurationContainerTests.java
@@ -97,22 +97,30 @@
final Configuration childOverrideConfig = new Configuration();
childOverrideConfig.densityDpi = 320;
child.onOverrideConfigurationChanged(childOverrideConfig);
+ final Configuration mergedOverrideConfig = new Configuration(root.getConfiguration());
+ mergedOverrideConfig.updateFrom(childOverrideConfig);
// Check configuration update when child is removed from parent.
root.removeChild(child);
assertEquals(childOverrideConfig, child.getOverrideConfiguration());
- assertEquals(childOverrideConfig, child.getMergedOverrideConfiguration());
- assertEquals(childOverrideConfig, child.getConfiguration());
+ assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
+ assertEquals(mergedOverrideConfig, child.getConfiguration());
// It may be paranoia... but let's check if parent's config didn't change after removal.
assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
assertEquals(rootOverrideConfig, root.getConfiguration());
- // Check configuration update when child is added to parent.
- final Configuration mergedOverrideConfig = new Configuration(root.getConfiguration());
+ // Init different root
+ final TestConfigurationContainer root2 = new TestConfigurationContainer();
+ final Configuration rootOverrideConfig2 = new Configuration();
+ rootOverrideConfig2.fontScale = 1.1f;
+ root2.onOverrideConfigurationChanged(rootOverrideConfig2);
+
+ // Check configuration update when child is added to different parent.
+ mergedOverrideConfig.setTo(rootOverrideConfig2);
mergedOverrideConfig.updateFrom(childOverrideConfig);
- root.addChild(child);
+ root2.addChild(child);
assertEquals(childOverrideConfig, child.getOverrideConfiguration());
assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
assertEquals(mergedOverrideConfig, child.getConfiguration());
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index aed3635..6ff0c5a 100644
--- a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -16,6 +16,17 @@
package com.android.server.connectivity;
+import static com.android.server.connectivity.MetricsTestUtil.aBool;
+import static com.android.server.connectivity.MetricsTestUtil.aByteArray;
+import static com.android.server.connectivity.MetricsTestUtil.aLong;
+import static com.android.server.connectivity.MetricsTestUtil.aString;
+import static com.android.server.connectivity.MetricsTestUtil.aType;
+import static com.android.server.connectivity.MetricsTestUtil.anInt;
+import static com.android.server.connectivity.MetricsTestUtil.anIntArray;
+import static com.android.server.connectivity.MetricsTestUtil.b;
+import static com.android.server.connectivity.MetricsTestUtil.describeIpEvent;
+import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityLog;
+
import android.net.ConnectivityMetricsEvent;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
@@ -28,21 +39,10 @@
import android.net.metrics.NetworkEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
-import com.google.protobuf.nano.MessageNano;
-import java.util.Arrays;
+
import junit.framework.TestCase;
-import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityLog;
-import static com.android.server.connectivity.MetricsTestUtil.aBool;
-import static com.android.server.connectivity.MetricsTestUtil.aByteArray;
-import static com.android.server.connectivity.MetricsTestUtil.aLong;
-import static com.android.server.connectivity.MetricsTestUtil.aString;
-import static com.android.server.connectivity.MetricsTestUtil.aType;
-import static com.android.server.connectivity.MetricsTestUtil.anInt;
-import static com.android.server.connectivity.MetricsTestUtil.anIntArray;
-import static com.android.server.connectivity.MetricsTestUtil.b;
-import static com.android.server.connectivity.MetricsTestUtil.describeIpEvent;
-import static com.android.server.connectivity.MetricsTestUtil.ipEv;
+import java.util.Arrays;
public class IpConnectivityEventBuilderTest extends TestCase {
@@ -71,7 +71,8 @@
" transport_types: 3",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -93,7 +94,8 @@
" state_transition: \"SomeState\"",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -114,7 +116,8 @@
" state_transition: \"\"",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -160,7 +163,8 @@
" return_codes: 178",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -181,7 +185,8 @@
" latency_ms: 5678",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -200,7 +205,8 @@
" if_name: \"wlan0\"",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -223,7 +229,8 @@
" >",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -248,7 +255,8 @@
" probe_result: 204",
" probe_type: 1",
" >",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -274,7 +282,8 @@
" program_length: 2048",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -305,7 +314,8 @@
" zero_lifetime_ras: 1",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -332,7 +342,8 @@
" router_lifetime: 2000",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -340,8 +351,7 @@
static void verifySerialization(String want, ConnectivityMetricsEvent... input) {
try {
byte[] got = IpConnectivityEventBuilder.serialize(0, Arrays.asList(input));
- IpConnectivityLog log = new IpConnectivityLog();
- MessageNano.mergeFrom(log, got);
+ IpConnectivityLog log = IpConnectivityLog.parseFrom(got);
assertEquals(want, log.toString());
} catch (Exception e) {
fail(e.toString());
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 3fc89b9..c7982b1 100644
--- a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -16,9 +16,13 @@
package com.android.server.connectivity;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
import android.content.Context;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
+import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
import android.net.metrics.DefaultNetworkEvent;
import android.net.metrics.DhcpClientEvent;
@@ -29,22 +33,21 @@
import android.net.metrics.ValidationProbeEvent;
import android.os.Parcelable;
import android.util.Base64;
+
import com.android.server.connectivity.metrics.IpConnectivityLogClass;
-import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
-import junit.framework.TestCase;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
public class IpConnectivityMetricsTest extends TestCase {
static final IpReachabilityEvent FAKE_EV =
@@ -57,7 +60,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- mService = new IpConnectivityMetrics(mCtx);
+ mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000);
}
public void testLoggingEvents() throws Exception {
@@ -112,6 +115,27 @@
assertEquals("", output3);
}
+ public void testRateLimiting() {
+ final IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
+ final ApfProgramEvent ev = new ApfProgramEvent(0, 0, 0, 0, 0);
+ final long fakeTimestamp = 1;
+
+ int attempt = 100; // More than burst quota, but less than buffer size.
+ for (int i = 0; i < attempt; i++) {
+ logger.log(ev);
+ }
+
+ String output1 = getdump("flush");
+ assertFalse("".equals(output1));
+
+ for (int i = 0; i < attempt; i++) {
+ assertFalse("expected event to be dropped", logger.log(fakeTimestamp, ev));
+ }
+
+ String output2 = getdump("flush");
+ assertEquals("", output2);
+ }
+
public void testEndToEndLogging() {
IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
@@ -204,7 +228,8 @@
" router_lifetime: 2000",
" >",
" time_ms: 700",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, getdump("flush"));
}
@@ -231,8 +256,7 @@
try {
byte[] got = Base64.decode(output, Base64.DEFAULT);
IpConnectivityLogClass.IpConnectivityLog log =
- new IpConnectivityLogClass.IpConnectivityLog();
- MessageNano.mergeFrom(log, got);
+ IpConnectivityLogClass.IpConnectivityLog.parseFrom(got);
assertEquals(want, log.toString());
} catch (Exception e) {
fail(e.toString());
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/TetheringTest.java b/services/tests/servicestests/src/com/android/server/connectivity/TetheringTest.java
new file mode 100644
index 0000000..a9f68c8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/connectivity/TetheringTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
+import android.os.INetworkManagementService;
+import android.os.PersistableBundle;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CarrierConfigManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TetheringTest {
+ private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+
+ @Mock private Context mContext;
+ @Mock private INetworkManagementService mNMService;
+ @Mock private INetworkStatsService mStatsService;
+ @Mock private INetworkPolicyManager mPolicyManager;
+ @Mock private MockableSystemProperties mSystemProperties;
+ @Mock private Resources mResources;
+ @Mock private CarrierConfigManager mCarrierConfigManager;
+
+ // Like so many Android system APIs, these cannot be mocked because it is marked final.
+ // We have to use the real versions.
+ private final PersistableBundle mCarrierConfig = new PersistableBundle();
+ private final TestLooper mLooper = new TestLooper();
+
+ private Tethering mTethering;
+
+ @Before public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
+ .thenReturn(new String[0]);
+ when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
+ .thenReturn(new String[0]);
+ when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
+ .thenReturn(new String[0]);
+ when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
+ .thenReturn(new String[0]);
+ when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
+ .thenReturn(new int[0]);
+ mTethering = new Tethering(mContext, mNMService, mStatsService, mPolicyManager,
+ mLooper.getLooper(), mSystemProperties);
+ }
+
+ private void setupForRequiredProvisioning() {
+ // Produce some acceptable looking provision app setting if requested.
+ when(mResources.getStringArray(
+ com.android.internal.R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(PROVISIONING_APP_NAME);
+ // Don't disable tethering provisioning unless requested.
+ when(mSystemProperties.getBoolean(eq(Tethering.DISABLE_PROVISIONING_SYSPROP_KEY),
+ anyBoolean())).thenReturn(false);
+ // Act like the CarrierConfigManager is present and ready unless told otherwise.
+ when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+ .thenReturn(mCarrierConfigManager);
+ when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
+ mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+ }
+
+ @Test
+ public void canRequireProvisioning() {
+ setupForRequiredProvisioning();
+ assertTrue(mTethering.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void toleratesCarrierConfigManagerMissing() {
+ setupForRequiredProvisioning();
+ when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+ .thenReturn(null);
+ // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
+ // We therefore still require provisioning.
+ assertTrue(mTethering.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void toleratesCarrierConfigMissing() {
+ setupForRequiredProvisioning();
+ when(mCarrierConfigManager.getConfig()).thenReturn(null);
+ // We still have a provisioning app configured, so still require provisioning.
+ assertTrue(mTethering.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void provisioningNotRequiredWhenAppNotFound() {
+ setupForRequiredProvisioning();
+ when(mResources.getStringArray(
+ com.android.internal.R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(null);
+ assertTrue(!mTethering.isTetherProvisioningRequired());
+ when(mResources.getStringArray(
+ com.android.internal.R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(new String[] {"malformedApp"});
+ assertTrue(!mTethering.isTetherProvisioningRequired());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/GroupHelperTest.java
new file mode 100644
index 0000000..22b674b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GroupHelperTest {
+ private @Mock GroupHelper.Callback mCallback;
+
+ private GroupHelper mGroupHelper;
+
+ private Context getContext() {
+ return InstrumentationRegistry.getTargetContext();
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mGroupHelper = new GroupHelper(mCallback);
+ }
+
+ private StatusBarNotification getSbn(String pkg, int id, String tag,
+ UserHandle user, String groupKey) {
+ Notification.Builder nb = new Notification.Builder(getContext())
+ .setContentTitle("A")
+ .setWhen(1205);
+ if (groupKey != null) {
+ nb.setGroup(groupKey);
+ }
+ return new StatusBarNotification(pkg, pkg, id, tag, 0, 0, 0, nb.build(), user);
+ }
+
+ private StatusBarNotification getSbn(String pkg, int id, String tag,
+ UserHandle user) {
+ return getSbn(pkg, id, tag, user, null);
+ }
+
+ @Test
+ public void testNoGroup_postingUnderLimit() throws Exception {
+ final String pkg = "package";
+ for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+ mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+ verify(mCallback, never()).addAutoGroupSummary(
+ eq(UserHandle.USER_SYSTEM), eq(pkg), anyString());
+ verify(mCallback, never()).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ }
+
+ @Test
+ public void testNoGroup_multiPackage() throws Exception {
+ final String pkg = "package";
+ final String pkg2 = "package2";
+ for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+ mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+ mGroupHelper.onNotificationPosted(
+ getSbn(pkg2, GroupHelper.AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM));
+ verify(mCallback, never()).addAutoGroupSummary(
+ eq(UserHandle.USER_SYSTEM), eq(pkg), anyString());
+ verify(mCallback, never()).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ }
+
+ @Test
+ public void testNoGroup_multiUser() throws Exception {
+ final String pkg = "package";
+ for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+ mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+ mGroupHelper.onNotificationPosted(
+ getSbn(pkg, GroupHelper.AUTOGROUP_AT_COUNT, "four", UserHandle.ALL));
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
+ verify(mCallback, never()).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ }
+
+ @Test
+ public void testNoGroup_someAreGrouped() throws Exception {
+ final String pkg = "package";
+ for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+ mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+ mGroupHelper.onNotificationPosted(
+ getSbn(pkg, GroupHelper.AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a"));
+ verify(mCallback, never()).addAutoGroupSummary(
+ eq(UserHandle.USER_SYSTEM), eq(pkg), anyString());
+ verify(mCallback, never()).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ }
+
+
+ @Test
+ public void testPostingOverLimit() throws Exception {
+ final String pkg = "package";
+ for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+ mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
+ verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ }
+
+ @Test
+ public void testDropBelowLimitRemoveGroup() throws Exception {
+ final String pkg = "package";
+ List<StatusBarNotification> posted = new ArrayList<>();
+ for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+ final StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ posted.add(sbn);
+ mGroupHelper.onNotificationPosted(sbn);
+ }
+ mGroupHelper.onNotificationRemoved(posted.remove(0));
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
+ verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT - 1)).removeAutoGroup(anyString());
+ verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index b5eb77c..763c50b 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -118,4 +118,7 @@
}
throw new NameNotFoundException();
}
+
+ @Override
+ public void setMultiprocessEnabled(boolean enabled) {}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index fee4783..03cbb43 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import com.android.internal.policy.IShortcutService;
+import com.android.server.input.InputManagerService;
import android.content.Context;
import android.content.res.CompatibilityInfo;
@@ -79,7 +80,9 @@
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-public class TestWindowManagerPolicy implements WindowManagerPolicy {
+import static org.mockito.Mockito.mock;
+
+class TestWindowManagerPolicy implements WindowManagerPolicy {
private static final String TAG = "TestWindowManagerPolicy";
private static WindowManagerService sWm = null;
@@ -88,8 +91,8 @@
if (sWm == null) {
// We only want to do this once for the test process as we don't want WM to try to
// register a bunch of local services again.
- sWm = WindowManagerService.main(
- context, null, true, false, false, new TestWindowManagerPolicy());
+ sWm = WindowManagerService.main(context, mock(InputManagerService.class), true, false,
+ false, new TestWindowManagerPolicy());
}
return sWm;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index 6eb347b..128317c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -434,22 +434,30 @@
final Configuration childOverrideConfig = new Configuration();
childOverrideConfig.densityDpi = 320;
child.onOverrideConfigurationChanged(childOverrideConfig);
+ final Configuration mergedOverrideConfig = new Configuration(root.getConfiguration());
+ mergedOverrideConfig.updateFrom(childOverrideConfig);
- // Check configuration update when child is removed from parent.
+ // Check configuration update when child is removed from parent - it should remain same.
root.removeChild(child);
assertEquals(childOverrideConfig, child.getOverrideConfiguration());
- assertEquals(childOverrideConfig, child.getMergedOverrideConfiguration());
- assertEquals(childOverrideConfig, child.getConfiguration());
+ assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
+ assertEquals(mergedOverrideConfig, child.getConfiguration());
// It may be paranoia... but let's check if parent's config didn't change after removal.
assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
assertEquals(rootOverrideConfig, root.getConfiguration());
- // Check configuration update when child is added to parent.
- final Configuration mergedOverrideConfig = new Configuration(root.getConfiguration());
+ // Init different root
+ final TestWindowContainer root2 = builder.setLayer(0).build();
+ final Configuration rootOverrideConfig2 = new Configuration();
+ rootOverrideConfig2.fontScale = 1.1f;
+ root2.onOverrideConfigurationChanged(rootOverrideConfig2);
+
+ // Check configuration update when child is added to different parent.
+ mergedOverrideConfig.setTo(rootOverrideConfig2);
mergedOverrideConfig.updateFrom(childOverrideConfig);
- root.addChildWindow(child);
+ root2.addChildWindow(child);
assertEquals(childOverrideConfig, child.getOverrideConfiguration());
assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
assertEquals(mergedOverrideConfig, child.getConfiguration());
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
index 1a4dff9..546c7da 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -21,6 +21,7 @@
import org.junit.runner.RunWith;
import android.content.Context;
+import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -28,12 +29,14 @@
import android.view.IWindow;
import android.view.WindowManager;
+import static android.app.AppOpsManager.OP_NONE;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
/**
* Tests for the {@link WindowState} class.
@@ -49,6 +52,7 @@
private WindowManagerService mWm = null;
private final IWindow mIWindow = new TestIWindow();
+ private final Session mMockSession = mock(Session.class);
@Before
public void setUp() throws Exception {
@@ -86,6 +90,28 @@
}
@Test
+ public void testChildRemoval() throws Exception {
+ final TestWindowToken token = new TestWindowToken();
+ final DisplayContent dc = mWm.getDefaultDisplayContentLocked();
+
+ assertEquals(token, dc.getWindowToken(token.token));
+
+ final WindowState window1 = createWindow(null, TYPE_APPLICATION, token);
+ final WindowState window2 = createWindow(null, TYPE_APPLICATION, token);
+ token.addWindow(window1);
+ token.addWindow(window2);
+
+ window2.removeImmediately();
+ // The token should still be mapped in the display content since it still has a child.
+ assertEquals(token, dc.getWindowToken(token.token));
+
+ window1.removeImmediately();
+ // The token should have been removed from the display content since it no longer has a
+ // child.
+ assertEquals(null, dc.getWindowToken(token.token));
+ }
+
+ @Test
public void testAdjustAnimLayer() throws Exception {
final TestWindowToken token = new TestWindowToken();
final WindowState window1 = createWindow(null, TYPE_APPLICATION, token);
@@ -157,14 +183,14 @@
private WindowState createWindow(WindowState parent, int type, WindowToken token) {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
- return new WindowState(mWm, null, mIWindow, token, parent, 0, 0, attrs, 0, 0);
+ return new WindowState(mWm, mMockSession, mIWindow, token, parent, OP_NONE, 0, attrs, 0, 0);
}
/* Used so we can gain access to some protected members of the {@link WindowToken} class */
private class TestWindowToken extends WindowToken {
TestWindowToken() {
- super(mWm, null, 0, false, mWm.getDefaultDisplayContentLocked());
+ super(mWm, mock(IBinder.class), 0, false, mWm.getDefaultDisplayContentLocked());
}
int getWindowsCount() {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 610f9bc..dd7b5a8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -27,10 +27,11 @@
import android.text.TextUtils;
import android.util.Slog;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
import java.util.UUID;
-import java.util.List;
-import java.util.ArrayList;
/**
* Helper to manage the database of the sound models that have been registered on the device.
@@ -123,7 +124,7 @@
db.execSQL("DROP TABLE IF EXISTS " + SoundModelContract.TABLE);
onCreate(db);
for (SoundModelRecord record : old_records) {
- if (!record.violatesV6PrimaryKeyConstraint(old_records)) {
+ if (record.ifViolatesV6PrimaryKeyIsFirstOfAnyDuplicates(old_records)) {
try {
long return_value = record.writeToDatabase(6, db);
if (return_value == -1) {
@@ -351,19 +352,39 @@
users = c.getString(c.getColumnIndex(SoundModelContract.KEY_USERS));
}
- // Check to see if this record conflicts with some other record in the list of records.
- public boolean violatesV6PrimaryKeyConstraint(List<SoundModelRecord> records) {
+ private boolean V6PrimaryKeyMatches(SoundModelRecord record) {
+ return keyphraseId == record.keyphraseId && stringComparisonHelper(locale, record.locale)
+ && stringComparisonHelper(users, record.users);
+ }
+
+ // Returns true if this record is a) the only record with the same V6 primary key, or b) the
+ // first record in the list of all records that have the same primary key and equal data.
+ // It will return false if a) there are any records that have the same primary key and
+ // different data, or b) there is a previous record in the list that has the same primary
+ // key and data.
+ // Note that 'this' object must be inside the list.
+ public boolean ifViolatesV6PrimaryKeyIsFirstOfAnyDuplicates(
+ List<SoundModelRecord> records) {
+ // First pass - check to see if all the records that have the same primary key have
+ // duplicated data.
for (SoundModelRecord record : records) {
if (this == record) {
continue;
}
- if (keyphraseId == record.keyphraseId
- && stringComparisonHelper(locale, record.locale)
- && stringComparisonHelper(users, record.users)) {
- return true;
+ // If we have different/missing data with the same primary key, then we should drop
+ // everything.
+ if (this.V6PrimaryKeyMatches(record) && !Arrays.equals(data, record.data)) {
+ return false;
}
}
- return false;
+
+ // We only want to return true for the first duplicated model.
+ for (SoundModelRecord record : records) {
+ if (this.V6PrimaryKeyMatches(record)) {
+ return this == record;
+ }
+ }
+ return true;
}
public long writeToDatabase(int version, SQLiteDatabase db) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index a04034e..e8976a7 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1100,7 +1100,7 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump PowerManager from from pid="
+ pw.println("Permission Denial: can't dump voiceinteraction from from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index c3075b3..8268b40 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -254,8 +254,7 @@
&& structureEnabled) {
mAssistData.clear();
final int count = activityToken != null ? 1 : topActivities.size();
- // Temp workaround for bug: 28348867 Revert after DP3
- for (int i = 0; i < count && i < 1; i++) {
+ for (int i = 0; i < count; i++) {
IBinder topActivity = count == 1 ? activityToken : topActivities.get(i);
try {
MetricsLogger.count(mContext, "assist_with_context", 1);
@@ -570,7 +569,9 @@
@Override
public void onServiceDisconnected(ComponentName name) {
mCallback.sessionConnectionGone(this);
- mService = null;
+ synchronized (mLock) {
+ mService = null;
+ }
}
public void dump(String prefix, PrintWriter pw) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index c006185..8f9c758 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -420,6 +420,31 @@
"android.telecom.extra.DISABLE_ADD_CALL";
/**
+ * String connection extra key on a {@link Connection} or {@link Conference} which contains the
+ * original Connection ID associated with the connection. Used in
+ * {@link RemoteConnectionService} to track the Connection ID which was originally assigned to a
+ * connection/conference added via
+ * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection)} and
+ * {@link ConnectionService#addConference(Conference)} APIs. This is important to pass to
+ * Telecom for when it deals with RemoteConnections. When the ConnectionManager wraps the
+ * {@link RemoteConnection} and {@link RemoteConference} and adds it to Telecom, there needs to
+ * be a way to ensure that we don't add the connection again as a duplicate.
+ * <p>
+ * For example, the TelephonyCS calls addExistingConnection for a Connection with ID
+ * {@code TelephonyCS@1}. The ConnectionManager learns of this via
+ * {@link ConnectionService#onRemoteExistingConnectionAdded(RemoteConnection)}, and wraps this
+ * in a new {@link Connection} which it adds to Telecom via
+ * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection)}. As part of
+ * this process, the wrapped RemoteConnection gets assigned a new ID (e.g. {@code ConnMan@1}).
+ * The TelephonyCS will ALSO try to add the existing connection to Telecom, except with the
+ * ID it originally referred to the connection as. Thus Telecom needs to know that the
+ * Connection with ID {@code ConnMan@1} is really the same as {@code TelephonyCS@1}.
+ * @hide
+ */
+ public static final String EXTRA_ORIGINAL_CONNECTION_ID =
+ "android.telecom.extra.ORIGINAL_CONNECTION_ID";
+
+ /**
* Connection event used to inform Telecom that it should play the on hold tone. This is used
* to play a tone when the peer puts the current call on hold. Sent to Telecom via
* {@link #sendConnectionEvent(String, Bundle)}.
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 7b68a4c..19388e9 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1347,7 +1347,13 @@
*/
private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) {
String id;
- if (handle == null) {
+
+ if (connection.getExtras() != null && connection.getExtras()
+ .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
+ id = connection.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
+ Log.d(this, "addExistingConnectionInternal - conn %s reusing original id %s",
+ connection.getTelecomCallId(), id);
+ } else if (handle == null) {
// If no phone account handle was provided, we cannot be sure the call ID is unique,
// so just use a random UUID.
id = UUID.randomUUID().toString();
@@ -1381,13 +1387,21 @@
}
private String addConferenceInternal(Conference conference) {
+ String originalId = null;
+ if (conference.getExtras() != null && conference.getExtras()
+ .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
+ originalId = conference.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
+ Log.d(this, "addConferenceInternal: conf %s reusing original id %s",
+ conference.getTelecomCallId(),
+ originalId);
+ }
if (mIdByConference.containsKey(conference)) {
Log.w(this, "Re-adding an existing conference: %s.", conference);
} else if (conference != null) {
// Conferences do not (yet) have a PhoneAccountHandle associated with them, so we
// cannot determine a ConnectionService class name to associate with the ID, so use
// a unique UUID (for now).
- String id = UUID.randomUUID().toString();
+ String id = originalId == null ? UUID.randomUUID().toString() : originalId;
mConferenceById.put(id, conference);
mIdByConference.put(conference, id);
conference.addListener(mConferenceListener);
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index ecda3cd..1e5769f 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.telecom.Logging.EventManager;
@@ -175,6 +176,10 @@
* loggers.
*/
+ public static void setSessionContext(Context context) {
+ getSessionManager().setContext(context);
+ }
+
public static void startSession(String shortMethodName) {
getSessionManager().startSession(shortMethodName, null);
}
@@ -183,6 +188,15 @@
getSessionManager().startSession(shortMethodName, callerIdentification);
}
+ public static void startSession(Session.Info info, String shortMethodName,
+ String callerIdentification) {
+ getSessionManager().startSession(info, shortMethodName, callerIdentification);
+ }
+
+ public static void startExternalSession(Session.Info sessionInfo, String shortMethodName) {
+ getSessionManager().startExternalSession(sessionInfo, shortMethodName);
+ }
+
public static Session createSubsession() {
return getSessionManager().createSubsession();
}
@@ -199,6 +213,10 @@
getSessionManager().endSession();
}
+ public static void registerSessionListener(SessionManager.ISessionListener l) {
+ getSessionManager().registerSessionListener(l);
+ }
+
public static String getSessionId() {
// If the Session logger has not been initialized, then there have been no sessions logged.
// Don't load it now!
@@ -300,7 +318,7 @@
private static MessageDigest sMessageDigest;
- static void initMd5Sum() {
+ public static void initMd5Sum() {
new AsyncTask<Void, Void, Void>() {
@Override
public Void doInBackground(Void... args) {
@@ -426,7 +444,7 @@
msg = (args == null || args.length == 0) ? format
: String.format(Locale.US, format, args);
} catch (IllegalFormatException ife) {
- e("Log", ife, "IllegalFormatException: formatString='%s' numArgs=%d", format,
+ e(TAG, ife, "Log: IllegalFormatException: formatString='%s' numArgs=%d", format,
args.length);
msg = format + " (An error occurred while formatting the message.)";
}
diff --git a/telecomm/java/android/telecom/Logging/EventManager.java b/telecomm/java/android/telecom/Logging/EventManager.java
index 8e7a393..2cd1b96 100644
--- a/telecomm/java/android/telecom/Logging/EventManager.java
+++ b/telecomm/java/android/telecom/Logging/EventManager.java
@@ -20,6 +20,7 @@
import android.telecom.Log;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import java.text.DateFormat;
@@ -46,6 +47,7 @@
public class EventManager {
public static final String TAG = "Logging.Events";
+ @VisibleForTesting
public static final int DEFAULT_EVENTS_TO_CACHE = 10; // Arbitrarily chosen.
public interface Loggable {
@@ -79,7 +81,7 @@
* Maps from request events to a list of possible response events. Used to track
* end-to-end timing for critical user-facing operations in Telecom.
*/
- public final Map<String, List<TimedEventPair>> requestResponsePairs = new HashMap<>();
+ private final Map<String, List<TimedEventPair>> requestResponsePairs = new HashMap<>();
private static final Object mSync = new Object();
@@ -289,7 +291,7 @@
msg = (args == null || args.length == 0) ? format
: String.format(Locale.US, format, args);
} catch (IllegalFormatException ife) {
- Log.e("Log", ife, "IllegalFormatException: formatString='%s' numArgs=%d", format,
+ Log.e(this, ife, "IllegalFormatException: formatString='%s' numArgs=%d", format,
args.length);
msg = format + " (An error occurred while formatting the message.)";
}
@@ -340,6 +342,16 @@
}
}
+ @VisibleForTesting
+ public LinkedBlockingQueue<EventRecord> getEventRecords() {
+ return mEventRecords;
+ }
+
+ @VisibleForTesting
+ public Map<Loggable, EventRecord> getCallEventRecordMap() {
+ return mCallEventRecordMap;
+ }
+
private void addEventRecord(EventRecord newRecord) {
Loggable recordEntry = newRecord.getRecordEntry();
@@ -354,8 +366,6 @@
// Now add a new entry
mEventRecords.add(newRecord);
mCallEventRecordMap.put(recordEntry, newRecord);
-
- // TODO: Add Implementation of this in Telecom for Analytics
synchronized (mSync) {
for (EventListener l : mEventListeners) {
l.eventRecordAdded(newRecord);
diff --git a/telecomm/java/android/telecom/Logging/Runnable.java b/telecomm/java/android/telecom/Logging/Runnable.java
new file mode 100644
index 0000000..b2cf3a3
--- /dev/null
+++ b/telecomm/java/android/telecom/Logging/Runnable.java
@@ -0,0 +1,99 @@
+/*
+ * 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.telecom.Logging;
+
+import android.telecom.Log;
+
+/**
+ * Encapsulates session logging in a Runnable to reduce code duplication when continuing subsessions
+ * in a handler/thread.
+ * @hide
+ */
+public abstract class Runnable {
+
+ private Session mSubsession;
+ private final String mSubsessionName;
+ private final Object mLock;
+ private final java.lang.Runnable mRunnable = new java.lang.Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ try {
+ Log.continueSession(mSubsession, mSubsessionName);
+ loggedRun();
+ } finally {
+ if (mSubsession != null) {
+ Log.endSession();
+ mSubsession = null;
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Creates a new Telecom Runnable that incorporates Session Logging into it. Useful for carrying
+ * Logging Sessions through different threads as well as through handlers.
+ * @param subsessionName The name that will be used in the Logs to mark this Session
+ * @param lock The synchronization lock that will be used to lock loggedRun().
+ */
+ public Runnable(String subsessionName, Object lock) {
+ if (lock == null) {
+ mLock = new Object();
+ } else {
+ mLock = lock;
+ }
+ mSubsessionName = subsessionName;
+ }
+
+ /**
+ * Return the runnable that will be canceled in the handler queue.
+ * @return Runnable object to cancel.
+ */
+ public final java.lang.Runnable getRunnableToCancel() {
+ return mRunnable;
+ }
+
+ /**
+ * Creates a Runnable and a logging subsession that can be used in a handler/thread. Be sure to
+ * call cancel() if this session is never going to be run (removed from a handler queue, for
+ * for example).
+ * @return A Java Runnable that can be used in a handler queue or thread.
+ */
+ public java.lang.Runnable prepare() {
+ cancel();
+ mSubsession = Log.createSubsession();
+ return mRunnable;
+ }
+
+ /**
+ * This method is used to clean up the active session if the Runnable gets removed from a
+ * handler and is never run.
+ */
+ public void cancel() {
+ synchronized (mLock) {
+ Log.cancelSubsession(mSubsession);
+ mSubsession = null;
+ }
+ }
+
+ /**
+ * The method that will be run in the handler/thread.
+ */
+ abstract public void loggedRun();
+
+}
diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java
index 14f4a0f..093c0f9 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -17,6 +17,11 @@
package android.telecom.Logging;
import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
@@ -33,8 +38,58 @@
public static final String END_SUBSESSION = "END_SUBSESSION";
public static final String END_SESSION = "END_SESSION";
+ public static final String SUBSESSION_SEPARATION_CHAR = "->";
+ public static final String EXTERNAL_INDICATOR = "E-";
+
+ /**
+ * Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()}
+ * if the Session is canceled.
+ */
public static final int UNDEFINED = -1;
+ public static class Info implements Parcelable {
+ public final String sessionId;
+ public final String shortMethodName;
+
+ private Info(String id, String methodName) {
+ sessionId = id;
+ shortMethodName = methodName;
+ }
+
+ public static Info getInfo (Session s) {
+ return new Info(s.getFullSessionId(), s.getShortMethodName());
+ }
+
+ /** Responsible for creating Info objects for deserialized Parcels. */
+ public static final Parcelable.Creator<Info> CREATOR =
+ new Parcelable.Creator<Info> () {
+ @Override
+ public Info createFromParcel(Parcel source) {
+ String id = source.readString();
+ String methodName = source.readString();
+ return new Info(id, methodName);
+ }
+
+ @Override
+ public Info[] newArray(int size) {
+ return new Info[size];
+ }
+ };
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Writes Info object into a Parcel. */
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeString(sessionId);
+ destination.writeString(shortMethodName);
+ }
+ }
+
private String mSessionId;
private String mShortMethodName;
private long mExecutionStartTimeMs;
@@ -42,6 +97,7 @@
private Session mParentSession;
private ArrayList<Session> mChildSessions;
private boolean mIsCompleted = false;
+ private boolean mIsExternal = false;
private int mChildCounter = 0;
// True if this is a subsession that has been started from the same thread as the parent
// session. This can happen if Log.startSession(...) is called multiple times on the same
@@ -52,8 +108,11 @@
// Optionally provided info about the method/class/component that started the session in order
// to make Logging easier. This info will be provided in parentheses along with the session.
private String mOwnerInfo;
+ // Cache Full Method path so that recursive population of the full method path only needs to
+ // be calculated once.
+ private String mFullMethodPathCache;
- public Session(String sessionId, String shortMethodName, long startTimeMs, long threadID,
+ public Session(String sessionId, String shortMethodName, long startTimeMs,
boolean isStartedFromActiveSession, String ownerInfo) {
setSessionId(sessionId);
setShortMethodName(shortMethodName);
@@ -82,6 +141,14 @@
mShortMethodName = shortMethodName;
}
+ public void setIsExternal(boolean isExternal) {
+ mIsExternal = isExternal;
+ }
+
+ public boolean isExternal() {
+ return mIsExternal;
+ }
+
public void setParentSession(Session parentSession) {
mParentSession = parentSession;
}
@@ -122,6 +189,15 @@
return mIsStartedFromActiveSession;
}
+ public Info getInfo() {
+ return Info.getInfo(this);
+ }
+
+ @VisibleForTesting
+ public String getSessionId() {
+ return mSessionId;
+ }
+
// Mark this session complete. This will be deleted by Log when all subsessions are complete
// as well.
public void markSessionCompleted(long executionEndTimeMs) {
@@ -140,27 +216,6 @@
return String.valueOf(mChildCounter++);
}
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof Session)) {
- return false;
- }
- if (obj == this) {
- return true;
- }
- Session otherSession = (Session) obj;
- return (mSessionId.equals(otherSession.mSessionId)) &&
- (mShortMethodName.equals(otherSession.mShortMethodName)) &&
- mExecutionStartTimeMs == otherSession.mExecutionStartTimeMs &&
- mParentSession == otherSession.mParentSession &&
- mChildSessions.equals(otherSession.mChildSessions) &&
- mIsCompleted == otherSession.mIsCompleted &&
- mExecutionEndTimeMs == otherSession.mExecutionEndTimeMs &&
- mChildCounter == otherSession.mChildCounter &&
- mIsStartedFromActiveSession == otherSession.mIsStartedFromActiveSession &&
- mOwnerInfo == otherSession.mOwnerInfo;
- }
-
// Builds full session id recursively
private String getFullSessionId() {
// Cache mParentSession locally to prevent a concurrency problem where
@@ -203,6 +258,82 @@
}
}
+ // Recursively concatenate mShortMethodName with the parent Sessions to create full method
+ // path. Caches this string so that multiple calls for the path will be quick.
+ public String getFullMethodPath() {
+ StringBuilder sb = new StringBuilder();
+ getFullMethodPath(sb);
+ return sb.toString();
+ }
+
+ private synchronized void getFullMethodPath(StringBuilder sb) {
+ // Don't calculate if we have already figured it out!
+ if (!TextUtils.isEmpty(mFullMethodPathCache)) {
+ sb.append(mFullMethodPathCache);
+ return;
+ }
+ Session parentSession = getParentSession();
+ boolean isSessionStarted = false;
+ if (parentSession != null) {
+ // Check to see if the session has been renamed yet. If it has not, then the session
+ // has not been continued.
+ isSessionStarted = !mShortMethodName.equals(parentSession.mShortMethodName);
+ parentSession.getFullMethodPath(sb);
+ sb.append(SUBSESSION_SEPARATION_CHAR);
+ }
+ sb.append(mShortMethodName);
+
+ if(isSessionStarted) {
+ // Cache this value so that we do not have to do this work next time!
+ // We do not cache the value if the session being evaluated hasn't been continued yet.
+ mFullMethodPathCache = sb.toString();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mSessionId != null ? mSessionId.hashCode() : 0;
+ result = 31 * result + (mShortMethodName != null ? mShortMethodName.hashCode() : 0);
+ result = 31 * result + (int) (mExecutionStartTimeMs ^ (mExecutionStartTimeMs >>> 32));
+ result = 31 * result + (int) (mExecutionEndTimeMs ^ (mExecutionEndTimeMs >>> 32));
+ result = 31 * result + (mParentSession != null ? mParentSession.hashCode() : 0);
+ result = 31 * result + (mChildSessions != null ? mChildSessions.hashCode() : 0);
+ result = 31 * result + (mIsCompleted ? 1 : 0);
+ result = 31 * result + mChildCounter;
+ result = 31 * result + (mIsStartedFromActiveSession ? 1 : 0);
+ result = 31 * result + (mOwnerInfo != null ? mOwnerInfo.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Session session = (Session) o;
+
+ if (mExecutionStartTimeMs != session.mExecutionStartTimeMs) return false;
+ if (mExecutionEndTimeMs != session.mExecutionEndTimeMs) return false;
+ if (mIsCompleted != session.mIsCompleted) return false;
+ if (mChildCounter != session.mChildCounter) return false;
+ if (mIsStartedFromActiveSession != session.mIsStartedFromActiveSession) return false;
+ if (mSessionId != null ?
+ !mSessionId.equals(session.mSessionId) : session.mSessionId != null)
+ return false;
+ if (mShortMethodName != null ? !mShortMethodName.equals(session.mShortMethodName)
+ : session.mShortMethodName != null)
+ return false;
+ if (mParentSession != null ? !mParentSession.equals(session.mParentSession)
+ : session.mParentSession != null)
+ return false;
+ if (mChildSessions != null ? !mChildSessions.equals(session.mChildSessions)
+ : session.mChildSessions != null)
+ return false;
+ return mOwnerInfo != null ? mOwnerInfo.equals(session.mOwnerInfo)
+ : session.mOwnerInfo == null;
+
+ }
+
@Override
public String toString() {
if (mParentSession != null && mIsStartedFromActiveSession) {
@@ -211,7 +342,7 @@
return mParentSession.toString();
} else {
StringBuilder methodName = new StringBuilder();
- methodName.append(mShortMethodName);
+ methodName.append(getFullMethodPath());
if (mOwnerInfo != null && !mOwnerInfo.isEmpty()) {
methodName.append("(InCall package: ");
methodName.append(mOwnerInfo);
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index a4e8977..173e7ee 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -19,7 +19,9 @@
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
import android.provider.Settings;
+import android.telecom.Log;
import android.util.Base64;
import com.android.internal.annotations.VisibleForTesting;
@@ -40,12 +42,9 @@
// Currently using 3 letters, So don't exceed 64^3
private static final long SESSION_ID_ROLLOVER_THRESHOLD = 262144;
-
// This parameter can be overridden in Telecom's Timeouts class.
- public static final long DEFAULT_SESSION_TIMEOUT_MS = 30000L; // 30 seconds
-
- private static String LOGGING_TAG = "Logging";
-
+ private static final long DEFAULT_SESSION_TIMEOUT_MS = 30000L; // 30 seconds
+ private static final String LOGGING_TAG = "Logging";
private static final String TIMEOUTS_PREFIX = "telecom.";
// Synchronized in all method calls
@@ -53,20 +52,26 @@
private Context mContext;
@VisibleForTesting
- public ConcurrentHashMap<Integer, Session> sSessionMapper = new ConcurrentHashMap<>(100);
+ public ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(100);
@VisibleForTesting
- public Handler sSessionCleanupHandler = new Handler(Looper.getMainLooper());
- @VisibleForTesting
- public java.lang.Runnable sCleanStaleSessions = () ->
+ public java.lang.Runnable mCleanStaleSessions = () ->
cleanupStaleSessions(getSessionCleanupTimeoutMs());
+ private Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper());
// Overridden in LogTest to skip query to ContentProvider
private interface ISessionCleanupTimeoutMs {
long get();
}
+ // Overridden in tests to provide test Thread IDs
+ public interface ICurrentThreadId {
+ int get();
+ }
+
@VisibleForTesting
- public ISessionCleanupTimeoutMs sSessionCleanupTimeoutMs = () -> {
+ public ICurrentThreadId mCurrentThreadId = Process::myTid;
+
+ private ISessionCleanupTimeoutMs mSessionCleanupTimeoutMs = () -> {
// mContext may be null in some cases, such as testing. For these cases, use the
// default value.
if (mContext == null) {
@@ -99,14 +104,29 @@
}
private long getSessionCleanupTimeoutMs() {
- return sSessionCleanupTimeoutMs.get();
+ return mSessionCleanupTimeoutMs.get();
}
private synchronized void resetStaleSessionTimer() {
- sSessionCleanupHandler.removeCallbacksAndMessages(null);
+ mSessionCleanupHandler.removeCallbacksAndMessages(null);
// Will be null in Log Testing
- if (sCleanStaleSessions != null) {
- sSessionCleanupHandler.postDelayed(sCleanStaleSessions, getSessionCleanupTimeoutMs());
+ if (mCleanStaleSessions != null) {
+ mSessionCleanupHandler.postDelayed(mCleanStaleSessions, getSessionCleanupTimeoutMs());
+ }
+ }
+
+ /**
+ * Determines whether or not to start a new session or continue an existing session based on
+ * the {@link Session.Info} info passed into startSession. If info is null, a new Session is
+ * created. This code must be accompanied by endSession() at the end of the Session.
+ */
+ public synchronized void startSession(Session.Info info, String shortMethodName,
+ String callerIdentification) {
+ // Start a new session normally if the
+ if(info == null) {
+ startSession(shortMethodName, callerIdentification);
+ } else {
+ startExternalSession(info, shortMethodName);
}
}
@@ -118,21 +138,61 @@
String callerIdentification) {
resetStaleSessionTimer();
int threadId = getCallingThreadId();
- Session activeSession = sSessionMapper.get(threadId);
+ Session activeSession = mSessionMapper.get(threadId);
// We have called startSession within an active session that has not ended... Register this
// session as a subsession.
if (activeSession != null) {
Session childSession = createSubsession(true);
continueSession(childSession, shortMethodName);
return;
+ } else {
+ // Only Log that we are starting the parent session.
+ Log.d(LOGGING_TAG, Session.START_SESSION);
}
Session newSession = new Session(getNextSessionID(), shortMethodName,
- System.currentTimeMillis(), threadId, false, callerIdentification);
- sSessionMapper.put(threadId, newSession);
-
- android.telecom.Log.v(LOGGING_TAG, Session.START_SESSION);
+ System.currentTimeMillis(), false, callerIdentification);
+ mSessionMapper.put(threadId, newSession);
}
+ /**
+ * Registers an external Session with the Manager using that external Session's sessionInfo.
+ * Log.endSession will still need to be called at the end of the session.
+ * @param sessionInfo Describes the external Session's information.
+ * @param shortMethodName The method name of the new session that is being started.
+ */
+ public synchronized void startExternalSession(Session.Info sessionInfo,
+ String shortMethodName) {
+ if(sessionInfo == null) {
+ return;
+ }
+
+ int threadId = getCallingThreadId();
+ Session threadSession = mSessionMapper.get(threadId);
+ if (threadSession != null) {
+ // We should never get into a situation where there is already an active session AND
+ // an external session is added. We are just using that active session.
+ Log.w(LOGGING_TAG, "trying to start an external session with a session " +
+ "already active.");
+ return;
+ }
+
+ // Create Session from Info and add to the sessionMapper under this ID.
+ Session externalSession = new Session(Session.EXTERNAL_INDICATOR + sessionInfo.sessionId,
+ sessionInfo.shortMethodName, System.currentTimeMillis(),
+ false /*isStartedFromActiveSession*/, null);
+ externalSession.setIsExternal(true);
+ // Mark the external session as already completed, since we have no way of knowing when
+ // the external session actually has completed.
+ externalSession.markSessionCompleted(Session.UNDEFINED);
+ // Track the external session with the SessionMapper so that we can create and continue
+ // an active subsession based on it.
+ mSessionMapper.put(threadId, externalSession);
+ // Create a subsession from this external Session parent node
+ Session childSession = createSubsession();
+ continueSession(childSession, shortMethodName);
+
+ Log.d(LOGGING_TAG, Session.START_SESSION);
+ }
/**
* Notifies the logging system that a subsession will be run at a later point and
@@ -145,24 +205,24 @@
private synchronized Session createSubsession(boolean isStartedFromActiveSession) {
int threadId = getCallingThreadId();
- Session threadSession = sSessionMapper.get(threadId);
+ Session threadSession = mSessionMapper.get(threadId);
if (threadSession == null) {
- android.telecom.Log.d(LOGGING_TAG, "Log.createSubsession was called with no session " +
+ Log.d(LOGGING_TAG, "Log.createSubsession was called with no session " +
"active.");
return null;
}
// Start execution time of the session will be overwritten in continueSession(...).
Session newSubsession = new Session(threadSession.getNextChildId(),
- threadSession.getShortMethodName(), System.currentTimeMillis(), threadId,
+ threadSession.getShortMethodName(), System.currentTimeMillis(),
isStartedFromActiveSession, null);
threadSession.addChild(newSubsession);
newSubsession.setParentSession(threadSession);
if (!isStartedFromActiveSession) {
- android.telecom.Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION + " " +
+ Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION + " " +
newSubsession.toString());
} else {
- android.telecom.Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION +
+ Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION +
" (Invisible subsession)");
}
return newSubsession;
@@ -171,14 +231,14 @@
/**
* Cancels a subsession that had Log.createSubsession() called on it, but will never have
* Log.continueSession(...) called on it due to an error. Allows the subsession to be cleaned
- * gracefully instead of being removed by the sSessionCleanupHandler forcefully later.
+ * gracefully instead of being removed by the mSessionCleanupHandler forcefully later.
*/
public synchronized void cancelSubsession(Session subsession) {
if (subsession == null) {
return;
}
- subsession.markSessionCompleted(0);
+ subsession.markSessionCompleted(Session.UNDEFINED);
endParentSessions(subsession);
}
@@ -192,21 +252,20 @@
return;
}
resetStaleSessionTimer();
- String callingMethodName = subsession.getShortMethodName();
- subsession.setShortMethodName(callingMethodName + "->" + shortMethodName);
+ subsession.setShortMethodName(shortMethodName);
subsession.setExecutionStartTimeMs(System.currentTimeMillis());
Session parentSession = subsession.getParentSession();
if (parentSession == null) {
- android.telecom.Log.d(LOGGING_TAG, "Log.continueSession was called with no session " +
- "active for method %s.", shortMethodName);
+ Log.i(LOGGING_TAG, "Log.continueSession was called with no session " +
+ "active for method " + shortMethodName);
return;
}
- sSessionMapper.put(getCallingThreadId(), subsession);
+ mSessionMapper.put(getCallingThreadId(), subsession);
if (!subsession.isStartedFromActiveSession()) {
- android.telecom.Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION);
+ Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION);
} else {
- android.telecom.Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION +
+ Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION +
" (Invisible Subsession) with Method " + shortMethodName);
}
}
@@ -217,30 +276,30 @@
*/
public synchronized void endSession() {
int threadId = getCallingThreadId();
- Session completedSession = sSessionMapper.get(threadId);
+ Session completedSession = mSessionMapper.get(threadId);
if (completedSession == null) {
- android.telecom.Log.w(LOGGING_TAG, "Log.endSession was called with no session active.");
+ Log.w(LOGGING_TAG, "Log.endSession was called with no session active.");
return;
}
completedSession.markSessionCompleted(System.currentTimeMillis());
if (!completedSession.isStartedFromActiveSession()) {
- android.telecom.Log.v(LOGGING_TAG, Session.END_SUBSESSION + " (dur: " +
+ Log.v(LOGGING_TAG, Session.END_SUBSESSION + " (dur: " +
completedSession.getLocalExecutionTime() + " mS)");
} else {
- android.telecom.Log.v(LOGGING_TAG, Session.END_SUBSESSION +
+ Log.v(LOGGING_TAG, Session.END_SUBSESSION +
" (Invisible Subsession) (dur: " + completedSession.getLocalExecutionTime() +
" ms)");
}
// Remove after completed so that reference still exists for logging the end events
Session parentSession = completedSession.getParentSession();
- sSessionMapper.remove(threadId);
+ mSessionMapper.remove(threadId);
endParentSessions(completedSession);
// If this subsession was started from a parent session using Log.startSession, return the
// ThreadID back to the parent after completion.
if (parentSession != null && !parentSession.isSessionCompleted() &&
completedSession.isStartedFromActiveSession()) {
- sSessionMapper.put(threadId, parentSession);
+ mSessionMapper.put(threadId, parentSession);
}
}
@@ -251,28 +310,39 @@
if (!subsession.isSessionCompleted() || subsession.getChildSessions().size() != 0) {
return;
}
-
Session parentSession = subsession.getParentSession();
if (parentSession != null) {
subsession.setParentSession(null);
parentSession.removeChild(subsession);
+ // Report the child session of the external session as being complete to the listeners,
+ // not the external session itself.
+ if (parentSession.isExternal()) {
+ long fullSessionTimeMs =
+ System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds();
+ notifySessionCompleteListeners(subsession.getShortMethodName(), fullSessionTimeMs);
+ }
endParentSessions(parentSession);
} else {
// All of the subsessions have been completed and it is time to report on the full
// running time of the session.
long fullSessionTimeMs =
System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds();
- android.telecom.Log.v(LOGGING_TAG, Session.END_SESSION + " (dur: " + fullSessionTimeMs
+ Log.d(LOGGING_TAG, Session.END_SESSION + " (dur: " + fullSessionTimeMs
+ " ms): " + subsession.toString());
- // TODO: Add analytics hook
- for (ISessionListener l : mSessionListeners) {
- l.sessionComplete(subsession.getShortMethodName(), fullSessionTimeMs);
+ if (!subsession.isExternal()) {
+ notifySessionCompleteListeners(subsession.getShortMethodName(), fullSessionTimeMs);
}
}
}
+ private void notifySessionCompleteListeners(String methodName, long sessionTimeMs) {
+ for (ISessionListener l : mSessionListeners) {
+ l.sessionComplete(methodName, sessionTimeMs);
+ }
+ }
+
public String getSessionId() {
- Session currentSession = sSessionMapper.get(getCallingThreadId());
+ Session currentSession = mSessionMapper.get(getCallingThreadId());
return currentSession != null ? currentSession.toString() : "";
}
@@ -291,24 +361,22 @@
return getBase64Encoding(nextId);
}
- @VisibleForTesting
- public synchronized void restartSessionCounter() {
+ private synchronized void restartSessionCounter() {
sCodeEntryCounter = 0;
}
- @VisibleForTesting
- public String getBase64Encoding(int number) {
+ private String getBase64Encoding(int number) {
byte[] idByteArray = ByteBuffer.allocate(4).putInt(number).array();
idByteArray = Arrays.copyOfRange(idByteArray, 2, 4);
return Base64.encodeToString(idByteArray, Base64.NO_WRAP | Base64.NO_PADDING);
}
- public int getCallingThreadId() {
- return android.os.Process.myTid();
+ private int getCallingThreadId() {
+ return mCurrentThreadId.get();
}
@VisibleForTesting
- private synchronized void cleanupStaleSessions(long timeoutMs) {
+ public synchronized void cleanupStaleSessions(long timeoutMs) {
String logMessage = "Stale Sessions Cleaned:\n";
boolean isSessionsStale = false;
long currentTimeMs = System.currentTimeMillis();
@@ -317,7 +385,7 @@
// If this occurs, then there is most likely a Session active that never had
// Log.endSession called on it.
for (Iterator<ConcurrentHashMap.Entry<Integer, Session>> it =
- sSessionMapper.entrySet().iterator(); it.hasNext(); ) {
+ mSessionMapper.entrySet().iterator(); it.hasNext(); ) {
ConcurrentHashMap.Entry<Integer, Session> entry = it.next();
Session session = entry.getValue();
if (currentTimeMs - session.getExecutionStartTimeMilliseconds() > timeoutMs) {
@@ -327,9 +395,9 @@
}
}
if (isSessionsStale) {
- android.telecom.Log.w(LOGGING_TAG, logMessage);
+ Log.w(LOGGING_TAG, logMessage);
} else {
- android.telecom.Log.v(LOGGING_TAG, "No stale logging sessions needed to be cleaned...");
+ Log.v(LOGGING_TAG, "No stale logging sessions needed to be cleaned...");
}
}
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index 943da6d..0ef9ec1 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -311,6 +311,9 @@
/** @hide */
void putExtras(final Bundle extras) {
+ if (extras == null) {
+ return;
+ }
if (mExtras == null) {
mExtras = new Bundle();
}
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index f030115..37fa374 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -651,6 +651,14 @@
mCallerDisplayName = connection.getCallerDisplayName();
mCallerDisplayNamePresentation = connection.getCallerDisplayNamePresentation();
mConference = null;
+ putExtras(connection.getExtras());
+
+ // Stash the original connection ID as it exists in the source ConnectionService.
+ // Telecom will use this to avoid adding duplicates later.
+ // See comments on Connection.EXTRA_ORIGINAL_CONNECTION_ID for more information.
+ Bundle newExtras = new Bundle();
+ newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
+ putExtras(newExtras);
}
/**
@@ -1348,6 +1356,9 @@
/** @hide */
void putExtras(final Bundle extras) {
+ if (extras == null) {
+ return;
+ }
if (mExtras == null) {
mExtras = new Bundle();
}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index c4739ff..1577a0f 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -214,18 +214,27 @@
conference.addConnection(c);
}
}
-
if (conference.getConnections().size() == 0) {
// A conference was created, but none of its connections are ones that have been
// created by, and therefore being tracked by, this remote connection service. It
// is of no interest to us.
+ Log.d(this, "addConferenceCall - skipping");
return;
}
conference.setState(parcel.getState());
conference.setConnectionCapabilities(parcel.getConnectionCapabilities());
conference.setConnectionProperties(parcel.getConnectionProperties());
+ conference.putExtras(parcel.getExtras());
mConferenceById.put(callId, conference);
+
+ // Stash the original connection ID as it exists in the source ConnectionService.
+ // Telecom will use this to avoid adding duplicates later.
+ // See comments on Connection.EXTRA_ORIGINAL_CONNECTION_ID for more information.
+ Bundle newExtras = new Bundle();
+ newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
+ conference.putExtras(newExtras);
+
conference.registerCallback(new RemoteConference.Callback() {
@Override
public void onDestroyed(RemoteConference c) {
@@ -331,12 +340,18 @@
}
@Override
- public void addExistingConnection(String callId, ParcelableConnection connection) {
- // TODO: add contents of this method
- RemoteConnection remoteConnction = new RemoteConnection(callId,
+ public void addExistingConnection(final String callId, ParcelableConnection connection) {
+ RemoteConnection remoteConnection = new RemoteConnection(callId,
mOutgoingConnectionServiceRpc, connection);
-
- mOurConnectionServiceImpl.addRemoteExistingConnection(remoteConnction);
+ mConnectionById.put(callId, remoteConnection);
+ remoteConnection.registerCallback(new RemoteConnection.Callback() {
+ @Override
+ public void onDestroyed(RemoteConnection connection) {
+ mConnectionById.remove(callId);
+ maybeDisconnectAdapter();
+ }
+ });
+ mOurConnectionServiceImpl.addRemoteExistingConnection(remoteConnection);
}
@Override
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 31ea1d1..7506b10 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -123,6 +123,13 @@
public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
/**
+ * Determines if the carrier requires converting the destination number before sending out an
+ * SMS. Certain networks and numbering plans require different formats.
+ */
+ public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL=
+ "sms_requires_destination_number_conversion_bool";
+
+ /**
* If true, show an onscreen "Dial" button in the dialer. In practice this is used on all
* platforms, even the ones with hard SEND/END keys, but for maximum flexibility it's controlled
* by a flag here (which can be overridden on a per-product basis.)
@@ -1065,6 +1072,18 @@
public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL =
"editable_wfc_roaming_mode_bool";
+ /**
+ * Determine whether current lpp_mode used for E-911 needs to be kept persistently.
+ * {@code false} - not keeping the lpp_mode means using default configuration of gps.conf
+ * when sim is not presented.
+ * {@code true} - current lpp_profile of carrier will be kepted persistently
+ * even after sim is removed.
+ *
+ * @hide
+ */
+ public static final String KEY_PERSIST_LPP_MODE_BOOL = "persist_lpp_mode_bool";
+
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -1113,6 +1132,7 @@
sDefaults.putBoolean(KEY_PREFER_2G_BOOL, true);
sDefaults.putBoolean(KEY_SHOW_APN_SETTING_CDMA_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_CDMA_CHOICES_BOOL, false);
+ sDefaults.putBoolean(KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL, true);
sDefaults.putBoolean(KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL, false);
@@ -1258,6 +1278,7 @@
sDefaults.putStringArray(FILTERED_CNAP_NAMES_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
sDefaults.putBoolean(KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL, false);
+ sDefaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5b63199..fe9d41a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3161,7 +3161,10 @@
* methods may return true.
*
* <p>This method returns valid data for registered cells on devices with
- * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}.
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}. In cases where only
+ * partial information is available for a particular CellInfo entry, unavailable fields
+ * will be reported as Integer.MAX_VALUE. All reported cells will include at least a
+ * valid set of technology-specific identification info and a power level measurement.
*
*<p>
* This method is preferred over using {@link
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 8424344..f737b24 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -25,6 +25,7 @@
import junit.framework.TestCase;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.view.Display.DEFAULT_DISPLAY;
/**
* TODO: Remove this. This is only a placeholder, need to implement this.
@@ -113,7 +114,8 @@
}
try {
- mWm.updateOrientationFromAppTokens(new Configuration(), null);
+ mWm.updateOrientationFromAppTokens(new Configuration(),
+ null /* freezeThisOneIfNeeded */, DEFAULT_DISPLAY);
fail("IWindowManager.updateOrientationFromAppTokens did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
diff --git a/tools/aapt2/.clang-format b/tools/aapt2/.clang-format
new file mode 100644
index 0000000..545366a
--- /dev/null
+++ b/tools/aapt2/.clang-format
@@ -0,0 +1,2 @@
+BasedOnStyle: Google
+
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 6bfedf3..197884dc 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -83,7 +83,7 @@
sources += Format.proto
-sourcesJni :=
+sourcesJni := jni/aapt2_jni.cpp
testSources := \
compile/IdAssigner_test.cpp \
@@ -158,7 +158,7 @@
hostLdLibs_linux := -lz
hostLdLibs_darwin := -lz
-cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
+cFlags := -Wall -Werror -Wno-unused-parameter
cFlags_darwin := -D_DARWIN_UNLIMITED_STREAMS
cFlags_windows := -Wno-maybe-uninitialized # Incorrectly marking use of Maybe.value() as error.
cppFlags := -Wno-missing-field-initializers -fno-exceptions -fno-rtti
@@ -194,15 +194,18 @@
include $(CLEAR_VARS)
LOCAL_MODULE := libaapt2_jni
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_MODULE_HOST_OS := darwin linux
LOCAL_CFLAGS := $(cFlags)
LOCAL_CFLAGS_darwin := $(cFlags_darwin)
LOCAL_CFLAGS_windows := $(cFlags_windows)
LOCAL_CPPFLAGS := $(cppFlags)
LOCAL_C_INCLUDES := $(protoIncludes)
-LOCAL_SRC_FILES := $(sourcesJni)
-LOCAL_STATIC_LIBRARIES := libaapt2 $(hostStaticLibs)
+LOCAL_SRC_FILES := $(toolSources) $(sourcesJni)
+LOCAL_STATIC_LIBRARIES := libaapt2 libnativehelper $(hostStaticLibs)
LOCAL_STATIC_LIBRARIES_windows := $(hostStaticLibs_windows)
+LOCAL_LDLIBS := $(hostLdLibs)
+LOCAL_LDLIBS_darwin := $(hostLdLibs_darwin)
+LOCAL_LDLIBS_linux := $(hostLdLibs_linux)
include $(BUILD_HOST_SHARED_LIBRARY)
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index a9794a4..1e488f7 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -17,10 +17,10 @@
#ifndef AAPT_APP_INFO_H
#define AAPT_APP_INFO_H
-#include "util/Maybe.h"
-
#include <string>
+#include "util/Maybe.h"
+
namespace aapt {
/**
@@ -28,27 +28,27 @@
* will come from the app's AndroidManifest.
*/
struct AppInfo {
- /**
- * App's package name.
- */
- std::string package;
+ /**
+ * App's package name.
+ */
+ std::string package;
- /**
- * The App's minimum SDK version.
- */
- Maybe<std::string> minSdkVersion;
+ /**
+ * The App's minimum SDK version.
+ */
+ Maybe<std::string> min_sdk_version;
- /**
- * The Version code of the app.
- */
- Maybe<uint32_t> versionCode;
+ /**
+ * The Version code of the app.
+ */
+ Maybe<uint32_t> version_code;
- /**
- * The revision code of the app.
- */
- Maybe<uint32_t> revisionCode;
+ /**
+ * The revision code of the app.
+ */
+ Maybe<uint32_t> revision_code;
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_APP_INFO_H
+#endif // AAPT_APP_INFO_H
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index 1812d96..289919a3 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -15,870 +15,887 @@
*/
#include "ConfigDescription.h"
+
+#include <string>
+#include <vector>
+
+#include "androidfw/ResourceTypes.h"
+
#include "Locale.h"
#include "SdkConstants.h"
#include "util/StringPiece.h"
#include "util/Util.h"
-#include <androidfw/ResourceTypes.h>
-#include <string>
-#include <vector>
-
namespace aapt {
using android::ResTable_config;
static const char* kWildcardName = "any";
-const ConfigDescription& ConfigDescription::defaultConfig() {
- static ConfigDescription config = {};
- return config;
+const ConfigDescription& ConfigDescription::DefaultConfig() {
+ static ConfigDescription config = {};
+ return config;
}
static bool parseMcc(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->mcc = 0;
- return true;
- }
- const char* c = name;
- if (tolower(*c) != 'm') return false;
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->mcc = 0;
+ return true;
+ }
+ const char* c = name;
+ if (tolower(*c) != 'm') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+
+ const char* val = c;
+
+ while (*c >= '0' && *c <= '9') {
c++;
- if (tolower(*c) != 'c') return false;
- c++;
- if (tolower(*c) != 'c') return false;
- c++;
+ }
+ if (*c != 0) return false;
+ if (c - val != 3) return false;
- const char* val = c;
+ int d = atoi(val);
+ if (d != 0) {
+ if (out) out->mcc = d;
+ return true;
+ }
- while (*c >= '0' && *c <= '9') {
- c++;
- }
- if (*c != 0) return false;
- if (c-val != 3) return false;
-
- int d = atoi(val);
- if (d != 0) {
- if (out) out->mcc = d;
- return true;
- }
-
- return false;
+ return false;
}
static bool parseMnc(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->mcc = 0;
- return true;
- }
- const char* c = name;
- if (tolower(*c) != 'm') return false;
- c++;
- if (tolower(*c) != 'n') return false;
- c++;
- if (tolower(*c) != 'c') return false;
- c++;
-
- const char* val = c;
-
- while (*c >= '0' && *c <= '9') {
- c++;
- }
- if (*c != 0) return false;
- if (c-val == 0 || c-val > 3) return false;
-
- if (out) {
- out->mnc = atoi(val);
- if (out->mnc == 0) {
- out->mnc = ACONFIGURATION_MNC_ZERO;
- }
- }
-
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->mcc = 0;
return true;
+ }
+ const char* c = name;
+ if (tolower(*c) != 'm') return false;
+ c++;
+ if (tolower(*c) != 'n') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+
+ const char* val = c;
+
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
+ if (*c != 0) return false;
+ if (c - val == 0 || c - val > 3) return false;
+
+ if (out) {
+ out->mnc = atoi(val);
+ if (out->mnc == 0) {
+ out->mnc = ACONFIGURATION_MNC_ZERO;
+ }
+ }
+
+ return true;
}
static bool parseLayoutDirection(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_ANY;
- return true;
- } else if (strcmp(name, "ldltr") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_LTR;
- return true;
- } else if (strcmp(name, "ldrtl") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_RTL;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) |
+ ResTable_config::LAYOUTDIR_ANY;
+ return true;
+ } else if (strcmp(name, "ldltr") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) |
+ ResTable_config::LAYOUTDIR_LTR;
+ return true;
+ } else if (strcmp(name, "ldrtl") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) |
+ ResTable_config::LAYOUTDIR_RTL;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_ANY;
- return true;
- } else if (strcmp(name, "small") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_SMALL;
- return true;
- } else if (strcmp(name, "normal") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_NORMAL;
- return true;
- } else if (strcmp(name, "large") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_LARGE;
- return true;
- } else if (strcmp(name, "xlarge") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_XLARGE;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
+ ResTable_config::SCREENSIZE_ANY;
+ return true;
+ } else if (strcmp(name, "small") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
+ ResTable_config::SCREENSIZE_SMALL;
+ return true;
+ } else if (strcmp(name, "normal") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
+ ResTable_config::SCREENSIZE_NORMAL;
+ return true;
+ } else if (strcmp(name, "large") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
+ ResTable_config::SCREENSIZE_LARGE;
+ return true;
+ } else if (strcmp(name, "xlarge") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
+ ResTable_config::SCREENSIZE_XLARGE;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_ANY;
- return true;
- } else if (strcmp(name, "long") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_YES;
- return true;
- } else if (strcmp(name, "notlong") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_NO;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) |
+ ResTable_config::SCREENLONG_ANY;
+ return true;
+ } else if (strcmp(name, "long") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) |
+ ResTable_config::SCREENLONG_YES;
+ return true;
+ } else if (strcmp(name, "notlong") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) |
+ ResTable_config::SCREENLONG_NO;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseScreenRound(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout2 =
- (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
- | ResTable_config::SCREENROUND_ANY;
- return true;
- } else if (strcmp(name, "round") == 0) {
- if (out) out->screenLayout2 =
- (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
- | ResTable_config::SCREENROUND_YES;
- return true;
- } else if (strcmp(name, "notround") == 0) {
- if (out) out->screenLayout2 =
- (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
- | ResTable_config::SCREENROUND_NO;
- return true;
- }
- return false;
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->screenLayout2 =
+ (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) |
+ ResTable_config::SCREENROUND_ANY;
+ return true;
+ } else if (strcmp(name, "round") == 0) {
+ if (out)
+ out->screenLayout2 =
+ (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) |
+ ResTable_config::SCREENROUND_YES;
+ return true;
+ } else if (strcmp(name, "notround") == 0) {
+ if (out)
+ out->screenLayout2 =
+ (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) |
+ ResTable_config::SCREENROUND_NO;
+ return true;
+ }
+ return false;
}
static bool parseOrientation(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->orientation = out->ORIENTATION_ANY;
- return true;
- } else if (strcmp(name, "port") == 0) {
- if (out) out->orientation = out->ORIENTATION_PORT;
- return true;
- } else if (strcmp(name, "land") == 0) {
- if (out) out->orientation = out->ORIENTATION_LAND;
- return true;
- } else if (strcmp(name, "square") == 0) {
- if (out) out->orientation = out->ORIENTATION_SQUARE;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->orientation = out->ORIENTATION_ANY;
+ return true;
+ } else if (strcmp(name, "port") == 0) {
+ if (out) out->orientation = out->ORIENTATION_PORT;
+ return true;
+ } else if (strcmp(name, "land") == 0) {
+ if (out) out->orientation = out->ORIENTATION_LAND;
+ return true;
+ } else if (strcmp(name, "square") == 0) {
+ if (out) out->orientation = out->ORIENTATION_SQUARE;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseUiModeType(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_ANY;
- return true;
- } else if (strcmp(name, "desk") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_DESK;
- return true;
- } else if (strcmp(name, "car") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_CAR;
- return true;
- } else if (strcmp(name, "television") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_TELEVISION;
- return true;
- } else if (strcmp(name, "appliance") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_APPLIANCE;
- return true;
- } else if (strcmp(name, "watch") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_WATCH;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_ANY;
+ return true;
+ } else if (strcmp(name, "desk") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_DESK;
+ return true;
+ } else if (strcmp(name, "car") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_CAR;
+ return true;
+ } else if (strcmp(name, "television") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_TELEVISION;
+ return true;
+ } else if (strcmp(name, "appliance") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_APPLIANCE;
+ return true;
+ } else if (strcmp(name, "watch") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_WATCH;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseUiModeNight(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_ANY;
- return true;
- } else if (strcmp(name, "night") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_YES;
- return true;
- } else if (strcmp(name, "notnight") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_NO;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
+ ResTable_config::UI_MODE_NIGHT_ANY;
+ return true;
+ } else if (strcmp(name, "night") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
+ ResTable_config::UI_MODE_NIGHT_YES;
+ return true;
+ } else if (strcmp(name, "notnight") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
+ ResTable_config::UI_MODE_NIGHT_NO;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseDensity(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->density = ResTable_config::DENSITY_DEFAULT;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->density = ResTable_config::DENSITY_DEFAULT;
+ return true;
+ }
- if (strcmp(name, "anydpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_ANY;
- return true;
- }
+ if (strcmp(name, "anydpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_ANY;
+ return true;
+ }
- if (strcmp(name, "nodpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_NONE;
- return true;
- }
+ if (strcmp(name, "nodpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_NONE;
+ return true;
+ }
- if (strcmp(name, "ldpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_LOW;
- return true;
- }
+ if (strcmp(name, "ldpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_LOW;
+ return true;
+ }
- if (strcmp(name, "mdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_MEDIUM;
- return true;
- }
+ if (strcmp(name, "mdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_MEDIUM;
+ return true;
+ }
- if (strcmp(name, "tvdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_TV;
- return true;
- }
+ if (strcmp(name, "tvdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_TV;
+ return true;
+ }
- if (strcmp(name, "hdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_HIGH;
- return true;
- }
+ if (strcmp(name, "hdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_HIGH;
+ return true;
+ }
- if (strcmp(name, "xhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XHIGH;
- return true;
- }
+ if (strcmp(name, "xhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XHIGH;
+ return true;
+ }
- if (strcmp(name, "xxhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XXHIGH;
- return true;
- }
+ if (strcmp(name, "xxhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XXHIGH;
+ return true;
+ }
- if (strcmp(name, "xxxhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
- return true;
- }
+ if (strcmp(name, "xxxhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
+ return true;
+ }
- char* c = (char*)name;
- while (*c >= '0' && *c <= '9') {
- c++;
- }
+ char* c = (char*)name;
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
- // check that we have 'dpi' after the last digit.
- if (toupper(c[0]) != 'D' ||
- toupper(c[1]) != 'P' ||
- toupper(c[2]) != 'I' ||
- c[3] != 0) {
- return false;
- }
-
- // temporarily replace the first letter with \0 to
- // use atoi.
- char tmp = c[0];
- c[0] = '\0';
-
- int d = atoi(name);
- c[0] = tmp;
-
- if (d != 0) {
- if (out) out->density = d;
- return true;
- }
-
+ // check that we have 'dpi' after the last digit.
+ if (toupper(c[0]) != 'D' || toupper(c[1]) != 'P' || toupper(c[2]) != 'I' ||
+ c[3] != 0) {
return false;
+ }
+
+ // temporarily replace the first letter with \0 to
+ // use atoi.
+ char tmp = c[0];
+ c[0] = '\0';
+
+ int d = atoi(name);
+ c[0] = tmp;
+
+ if (d != 0) {
+ if (out) out->density = d;
+ return true;
+ }
+
+ return false;
}
static bool parseTouchscreen(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
- return true;
- } else if (strcmp(name, "notouch") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
- return true;
- } else if (strcmp(name, "stylus") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
- return true;
- } else if (strcmp(name, "finger") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
+ return true;
+ } else if (strcmp(name, "notouch") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
+ return true;
+ } else if (strcmp(name, "stylus") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
+ return true;
+ } else if (strcmp(name, "finger") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseKeysHidden(const char* name, ResTable_config* out) {
- uint8_t mask = 0;
- uint8_t value = 0;
- if (strcmp(name, kWildcardName) == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_ANY;
- } else if (strcmp(name, "keysexposed") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_NO;
- } else if (strcmp(name, "keyshidden") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_YES;
- } else if (strcmp(name, "keyssoft") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_SOFT;
- }
+ uint8_t mask = 0;
+ uint8_t value = 0;
+ if (strcmp(name, kWildcardName) == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_ANY;
+ } else if (strcmp(name, "keysexposed") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_NO;
+ } else if (strcmp(name, "keyshidden") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_YES;
+ } else if (strcmp(name, "keyssoft") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_SOFT;
+ }
- if (mask != 0) {
- if (out) out->inputFlags = (out->inputFlags&~mask) | value;
- return true;
- }
+ if (mask != 0) {
+ if (out) out->inputFlags = (out->inputFlags & ~mask) | value;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseKeyboard(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->keyboard = out->KEYBOARD_ANY;
- return true;
- } else if (strcmp(name, "nokeys") == 0) {
- if (out) out->keyboard = out->KEYBOARD_NOKEYS;
- return true;
- } else if (strcmp(name, "qwerty") == 0) {
- if (out) out->keyboard = out->KEYBOARD_QWERTY;
- return true;
- } else if (strcmp(name, "12key") == 0) {
- if (out) out->keyboard = out->KEYBOARD_12KEY;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->keyboard = out->KEYBOARD_ANY;
+ return true;
+ } else if (strcmp(name, "nokeys") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_NOKEYS;
+ return true;
+ } else if (strcmp(name, "qwerty") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_QWERTY;
+ return true;
+ } else if (strcmp(name, "12key") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_12KEY;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseNavHidden(const char* name, ResTable_config* out) {
- uint8_t mask = 0;
- uint8_t value = 0;
- if (strcmp(name, kWildcardName) == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_ANY;
- } else if (strcmp(name, "navexposed") == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_NO;
- } else if (strcmp(name, "navhidden") == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_YES;
- }
+ uint8_t mask = 0;
+ uint8_t value = 0;
+ if (strcmp(name, kWildcardName) == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_ANY;
+ } else if (strcmp(name, "navexposed") == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_NO;
+ } else if (strcmp(name, "navhidden") == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_YES;
+ }
- if (mask != 0) {
- if (out) out->inputFlags = (out->inputFlags&~mask) | value;
- return true;
- }
+ if (mask != 0) {
+ if (out) out->inputFlags = (out->inputFlags & ~mask) | value;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseNavigation(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->navigation = out->NAVIGATION_ANY;
- return true;
- } else if (strcmp(name, "nonav") == 0) {
- if (out) out->navigation = out->NAVIGATION_NONAV;
- return true;
- } else if (strcmp(name, "dpad") == 0) {
- if (out) out->navigation = out->NAVIGATION_DPAD;
- return true;
- } else if (strcmp(name, "trackball") == 0) {
- if (out) out->navigation = out->NAVIGATION_TRACKBALL;
- return true;
- } else if (strcmp(name, "wheel") == 0) {
- if (out) out->navigation = out->NAVIGATION_WHEEL;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->navigation = out->NAVIGATION_ANY;
+ return true;
+ } else if (strcmp(name, "nonav") == 0) {
+ if (out) out->navigation = out->NAVIGATION_NONAV;
+ return true;
+ } else if (strcmp(name, "dpad") == 0) {
+ if (out) out->navigation = out->NAVIGATION_DPAD;
+ return true;
+ } else if (strcmp(name, "trackball") == 0) {
+ if (out) out->navigation = out->NAVIGATION_TRACKBALL;
+ return true;
+ } else if (strcmp(name, "wheel") == 0) {
+ if (out) out->navigation = out->NAVIGATION_WHEEL;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseScreenSize(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenWidth = out->SCREENWIDTH_ANY;
- out->screenHeight = out->SCREENHEIGHT_ANY;
- }
- return true;
- }
-
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || *x != 'x') return false;
- std::string xName(name, x-name);
- x++;
-
- const char* y = x;
- while (*y >= '0' && *y <= '9') y++;
- if (y == name || *y != 0) return false;
- std::string yName(x, y-x);
-
- uint16_t w = (uint16_t)atoi(xName.c_str());
- uint16_t h = (uint16_t)atoi(yName.c_str());
- if (w < h) {
- return false;
- }
-
+ if (strcmp(name, kWildcardName) == 0) {
if (out) {
- out->screenWidth = w;
- out->screenHeight = h;
+ out->screenWidth = out->SCREENWIDTH_ANY;
+ out->screenHeight = out->SCREENHEIGHT_ANY;
}
-
return true;
+ }
+
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || *x != 'x') return false;
+ std::string xName(name, x - name);
+ x++;
+
+ const char* y = x;
+ while (*y >= '0' && *y <= '9') y++;
+ if (y == name || *y != 0) return false;
+ std::string yName(x, y - x);
+
+ uint16_t w = (uint16_t)atoi(xName.c_str());
+ uint16_t h = (uint16_t)atoi(yName.c_str());
+ if (w < h) {
+ return false;
+ }
+
+ if (out) {
+ out->screenWidth = w;
+ out->screenHeight = h;
+ }
+
+ return true;
}
static bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 's') return false;
- name++;
- if (*name != 'w') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- std::string xName(name, x-name);
-
+ if (strcmp(name, kWildcardName) == 0) {
if (out) {
- out->smallestScreenWidthDp = (uint16_t)atoi(xName.c_str());
+ out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
}
-
return true;
+ }
+
+ if (*name != 's') return false;
+ name++;
+ if (*name != 'w') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ std::string xName(name, x - name);
+
+ if (out) {
+ out->smallestScreenWidthDp = (uint16_t)atoi(xName.c_str());
+ }
+
+ return true;
}
static bool parseScreenWidthDp(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenWidthDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 'w') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- std::string xName(name, x-name);
-
+ if (strcmp(name, kWildcardName) == 0) {
if (out) {
- out->screenWidthDp = (uint16_t)atoi(xName.c_str());
+ out->screenWidthDp = out->SCREENWIDTH_ANY;
}
-
return true;
+ }
+
+ if (*name != 'w') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ std::string xName(name, x - name);
+
+ if (out) {
+ out->screenWidthDp = (uint16_t)atoi(xName.c_str());
+ }
+
+ return true;
}
static bool parseScreenHeightDp(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenHeightDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 'h') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- std::string xName(name, x-name);
-
+ if (strcmp(name, kWildcardName) == 0) {
if (out) {
- out->screenHeightDp = (uint16_t)atoi(xName.c_str());
+ out->screenHeightDp = out->SCREENWIDTH_ANY;
}
-
return true;
+ }
+
+ if (*name != 'h') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ std::string xName(name, x - name);
+
+ if (out) {
+ out->screenHeightDp = (uint16_t)atoi(xName.c_str());
+ }
+
+ return true;
}
static bool parseVersion(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->sdkVersion = out->SDKVERSION_ANY;
- out->minorVersion = out->MINORVERSION_ANY;
- }
- return true;
- }
-
- if (*name != 'v') {
- return false;
- }
-
- name++;
- const char* s = name;
- while (*s >= '0' && *s <= '9') s++;
- if (s == name || *s != 0) return false;
- std::string sdkName(name, s-name);
-
+ if (strcmp(name, kWildcardName) == 0) {
if (out) {
- out->sdkVersion = (uint16_t)atoi(sdkName.c_str());
- out->minorVersion = 0;
+ out->sdkVersion = out->SDKVERSION_ANY;
+ out->minorVersion = out->MINORVERSION_ANY;
}
-
return true;
+ }
+
+ if (*name != 'v') {
+ return false;
+ }
+
+ name++;
+ const char* s = name;
+ while (*s >= '0' && *s <= '9') s++;
+ if (s == name || *s != 0) return false;
+ std::string sdkName(name, s - name);
+
+ if (out) {
+ out->sdkVersion = (uint16_t)atoi(sdkName.c_str());
+ out->minorVersion = 0;
+ }
+
+ return true;
}
-bool ConfigDescription::parse(const StringPiece& str, ConfigDescription* out) {
- std::vector<std::string> parts = util::splitAndLowercase(str, '-');
+bool ConfigDescription::Parse(const StringPiece& str, ConfigDescription* out) {
+ std::vector<std::string> parts = util::SplitAndLowercase(str, '-');
- ConfigDescription config;
- ssize_t partsConsumed = 0;
- LocaleValue locale;
+ ConfigDescription config;
+ ssize_t parts_consumed = 0;
+ LocaleValue locale;
- const auto partsEnd = parts.end();
- auto partIter = parts.begin();
+ const auto parts_end = parts.end();
+ auto part_iter = parts.begin();
- if (str.size() == 0) {
- goto success;
+ if (str.size() == 0) {
+ goto success;
+ }
+
+ if (parseMcc(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
}
+ }
- if (parseMcc(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
+ if (parseMnc(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
}
+ }
- if (parseMnc(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- // Locale spans a few '-' separators, so we let it
- // control the index.
- partsConsumed = locale.initFromParts(partIter, partsEnd);
- if (partsConsumed < 0) {
- return false;
- } else {
- locale.writeTo(&config);
- partIter += partsConsumed;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseLayoutDirection(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseSmallestScreenWidthDp(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenWidthDp(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenHeightDp(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenLayoutSize(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenLayoutLong(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenRound(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseOrientation(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseUiModeType(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseUiModeNight(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseDensity(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseTouchscreen(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseKeysHidden(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseKeyboard(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseNavHidden(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseNavigation(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenSize(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseVersion(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- // Unrecognized.
+ // Locale spans a few '-' separators, so we let it
+ // control the index.
+ parts_consumed = locale.InitFromParts(part_iter, parts_end);
+ if (parts_consumed < 0) {
return false;
+ } else {
+ locale.WriteTo(&config);
+ part_iter += parts_consumed;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseLayoutDirection(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseSmallestScreenWidthDp(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseScreenWidthDp(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseScreenHeightDp(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseScreenLayoutSize(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseScreenLayoutLong(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseScreenRound(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseOrientation(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseUiModeType(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseUiModeNight(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseDensity(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseTouchscreen(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseKeysHidden(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseKeyboard(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseNavHidden(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseNavigation(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseScreenSize(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ if (parseVersion(part_iter->c_str(), &config)) {
+ ++part_iter;
+ if (part_iter == parts_end) {
+ goto success;
+ }
+ }
+
+ // Unrecognized.
+ return false;
success:
- if (out != NULL) {
- applyVersionForCompatibility(&config);
- *out = config;
- }
+ if (out != NULL) {
+ ApplyVersionForCompatibility(&config);
+ *out = config;
+ }
+ return true;
+}
+
+void ConfigDescription::ApplyVersionForCompatibility(
+ ConfigDescription* config) {
+ uint16_t min_sdk = 0;
+ if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
+ min_sdk = SDK_MARSHMALLOW;
+ } else if (config->density == ResTable_config::DENSITY_ANY) {
+ min_sdk = SDK_LOLLIPOP;
+ } else if (config->smallestScreenWidthDp !=
+ ResTable_config::SCREENWIDTH_ANY ||
+ config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY ||
+ config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
+ min_sdk = SDK_HONEYCOMB_MR2;
+ } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE) !=
+ ResTable_config::UI_MODE_TYPE_ANY ||
+ (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT) !=
+ ResTable_config::UI_MODE_NIGHT_ANY) {
+ min_sdk = SDK_FROYO;
+ } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE) !=
+ ResTable_config::SCREENSIZE_ANY ||
+ (config->screenLayout & ResTable_config::MASK_SCREENLONG) !=
+ ResTable_config::SCREENLONG_ANY ||
+ config->density != ResTable_config::DENSITY_DEFAULT) {
+ min_sdk = SDK_DONUT;
+ }
+
+ if (min_sdk > config->sdkVersion) {
+ config->sdkVersion = min_sdk;
+ }
+}
+
+ConfigDescription ConfigDescription::CopyWithoutSdkVersion() const {
+ ConfigDescription copy = *this;
+ copy.sdkVersion = 0;
+ return copy;
+}
+
+bool ConfigDescription::Dominates(const ConfigDescription& o) const {
+ if (*this == DefaultConfig() || *this == o) {
return true;
+ }
+ return MatchWithDensity(o) && !o.MatchWithDensity(*this) &&
+ !isMoreSpecificThan(o) && !o.HasHigherPrecedenceThan(*this);
}
-void ConfigDescription::applyVersionForCompatibility(ConfigDescription* config) {
- uint16_t minSdk = 0;
- if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
- minSdk = SDK_MARSHMALLOW;
- } else if (config->density == ResTable_config::DENSITY_ANY) {
- minSdk = SDK_LOLLIPOP;
- } else if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
- || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
- || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
- minSdk = SDK_HONEYCOMB_MR2;
- } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
- != ResTable_config::UI_MODE_TYPE_ANY
- || (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT)
- != ResTable_config::UI_MODE_NIGHT_ANY) {
- minSdk = SDK_FROYO;
- } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE)
- != ResTable_config::SCREENSIZE_ANY
- || (config->screenLayout & ResTable_config::MASK_SCREENLONG)
- != ResTable_config::SCREENLONG_ANY
- || config->density != ResTable_config::DENSITY_DEFAULT) {
- minSdk = SDK_DONUT;
- }
-
- if (minSdk > config->sdkVersion) {
- config->sdkVersion = minSdk;
- }
+bool ConfigDescription::HasHigherPrecedenceThan(
+ const ConfigDescription& o) const {
+ // The order of the following tests defines the importance of one
+ // configuration parameter over another. Those tests first are more
+ // important, trumping any values in those following them.
+ // The ordering should be the same as ResTable_config#isBetterThan.
+ if (mcc || o.mcc) return (!o.mcc);
+ if (mnc || o.mnc) return (!o.mnc);
+ if (language[0] || o.language[0]) return (!o.language[0]);
+ if (country[0] || o.country[0]) return (!o.country[0]);
+ // Script and variant require either a language or country, both of which
+ // have higher precedence.
+ if ((screenLayout | o.screenLayout) & MASK_LAYOUTDIR) {
+ return !(o.screenLayout & MASK_LAYOUTDIR);
+ }
+ if (smallestScreenWidthDp || o.smallestScreenWidthDp)
+ return (!o.smallestScreenWidthDp);
+ if (screenWidthDp || o.screenWidthDp) return (!o.screenWidthDp);
+ if (screenHeightDp || o.screenHeightDp) return (!o.screenHeightDp);
+ if ((screenLayout | o.screenLayout) & MASK_SCREENSIZE) {
+ return !(o.screenLayout & MASK_SCREENSIZE);
+ }
+ if ((screenLayout | o.screenLayout) & MASK_SCREENLONG) {
+ return !(o.screenLayout & MASK_SCREENLONG);
+ }
+ if ((screenLayout2 | o.screenLayout2) & MASK_SCREENROUND) {
+ return !(o.screenLayout2 & MASK_SCREENROUND);
+ }
+ if (orientation || o.orientation) return (!o.orientation);
+ if ((uiMode | o.uiMode) & MASK_UI_MODE_TYPE) {
+ return !(o.uiMode & MASK_UI_MODE_TYPE);
+ }
+ if ((uiMode | o.uiMode) & MASK_UI_MODE_NIGHT) {
+ return !(o.uiMode & MASK_UI_MODE_NIGHT);
+ }
+ if (density || o.density) return (!o.density);
+ if (touchscreen || o.touchscreen) return (!o.touchscreen);
+ if ((inputFlags | o.inputFlags) & MASK_KEYSHIDDEN) {
+ return !(o.inputFlags & MASK_KEYSHIDDEN);
+ }
+ if ((inputFlags | o.inputFlags) & MASK_NAVHIDDEN) {
+ return !(o.inputFlags & MASK_NAVHIDDEN);
+ }
+ if (keyboard || o.keyboard) return (!o.keyboard);
+ if (navigation || o.navigation) return (!o.navigation);
+ if (screenWidth || o.screenWidth) return (!o.screenWidth);
+ if (screenHeight || o.screenHeight) return (!o.screenHeight);
+ if (sdkVersion || o.sdkVersion) return (!o.sdkVersion);
+ if (minorVersion || o.minorVersion) return (!o.minorVersion);
+ // Both configurations have nothing defined except some possible future
+ // value. Returning the comparison of the two configurations is a
+ // "best effort" at this point to protect against incorrect dominations.
+ return *this != o;
}
-ConfigDescription ConfigDescription::copyWithoutSdkVersion() const {
- ConfigDescription copy = *this;
- copy.sdkVersion = 0;
- return copy;
+bool ConfigDescription::ConflictsWith(const ConfigDescription& o) const {
+ // This method should be updated as new configuration parameters are
+ // introduced (e.g. screenConfig2).
+ auto pred = [](const uint32_t a, const uint32_t b) -> bool {
+ return a == 0 || b == 0 || a == b;
+ };
+ // The values here can be found in ResTable_config#match. Density and range
+ // values can't lead to conflicts, and are ignored.
+ return !pred(mcc, o.mcc) || !pred(mnc, o.mnc) || !pred(locale, o.locale) ||
+ !pred(screenLayout & MASK_LAYOUTDIR,
+ o.screenLayout & MASK_LAYOUTDIR) ||
+ !pred(screenLayout & MASK_SCREENLONG,
+ o.screenLayout & MASK_SCREENLONG) ||
+ !pred(screenLayout & MASK_UI_MODE_TYPE,
+ o.screenLayout & MASK_UI_MODE_TYPE) ||
+ !pred(uiMode & MASK_UI_MODE_TYPE, o.uiMode & MASK_UI_MODE_TYPE) ||
+ !pred(uiMode & MASK_UI_MODE_NIGHT, o.uiMode & MASK_UI_MODE_NIGHT) ||
+ !pred(screenLayout2 & MASK_SCREENROUND,
+ o.screenLayout2 & MASK_SCREENROUND) ||
+ !pred(orientation, o.orientation) ||
+ !pred(touchscreen, o.touchscreen) ||
+ !pred(inputFlags & MASK_KEYSHIDDEN, o.inputFlags & MASK_KEYSHIDDEN) ||
+ !pred(inputFlags & MASK_NAVHIDDEN, o.inputFlags & MASK_NAVHIDDEN) ||
+ !pred(keyboard, o.keyboard) || !pred(navigation, o.navigation);
}
-bool ConfigDescription::dominates(const ConfigDescription& o) const {
- if (*this == defaultConfig() || *this == o) {
- return true;
- }
- return matchWithDensity(o)
- && !o.matchWithDensity(*this)
- && !isMoreSpecificThan(o)
- && !o.hasHigherPrecedenceThan(*this);
+bool ConfigDescription::IsCompatibleWith(const ConfigDescription& o) const {
+ return !ConflictsWith(o) && !Dominates(o) && !o.Dominates(*this);
}
-bool ConfigDescription::hasHigherPrecedenceThan(const ConfigDescription& o) const {
- // The order of the following tests defines the importance of one
- // configuration parameter over another. Those tests first are more
- // important, trumping any values in those following them.
- // The ordering should be the same as ResTable_config#isBetterThan.
- if (mcc || o.mcc) return (!o.mcc);
- if (mnc || o.mnc) return (!o.mnc);
- if (language[0] || o.language[0]) return (!o.language[0]);
- if (country[0] || o.country[0]) return (!o.country[0]);
- // Script and variant require either a language or country, both of which
- // have higher precedence.
- if ((screenLayout | o.screenLayout) & MASK_LAYOUTDIR) {
- return !(o.screenLayout & MASK_LAYOUTDIR);
- }
- if (smallestScreenWidthDp || o.smallestScreenWidthDp) return (!o.smallestScreenWidthDp);
- if (screenWidthDp || o.screenWidthDp) return (!o.screenWidthDp);
- if (screenHeightDp || o.screenHeightDp) return (!o.screenHeightDp);
- if ((screenLayout | o.screenLayout) & MASK_SCREENSIZE) {
- return !(o.screenLayout & MASK_SCREENSIZE);
- }
- if ((screenLayout | o.screenLayout) & MASK_SCREENLONG) {
- return !(o.screenLayout & MASK_SCREENLONG);
- }
- if ((screenLayout2 | o.screenLayout2) & MASK_SCREENROUND) {
- return !(o.screenLayout2 & MASK_SCREENROUND);
- }
- if (orientation || o.orientation) return (!o.orientation);
- if ((uiMode | o.uiMode) & MASK_UI_MODE_TYPE) {
- return !(o.uiMode & MASK_UI_MODE_TYPE);
- }
- if ((uiMode | o.uiMode) & MASK_UI_MODE_NIGHT) {
- return !(o.uiMode & MASK_UI_MODE_NIGHT);
- }
- if (density || o.density) return (!o.density);
- if (touchscreen || o.touchscreen) return (!o.touchscreen);
- if ((inputFlags | o.inputFlags) & MASK_KEYSHIDDEN) {
- return !(o.inputFlags & MASK_KEYSHIDDEN);
- }
- if ((inputFlags | o.inputFlags) & MASK_NAVHIDDEN) {
- return !(o.inputFlags & MASK_NAVHIDDEN);
- }
- if (keyboard || o.keyboard) return (!o.keyboard);
- if (navigation || o.navigation) return (!o.navigation);
- if (screenWidth || o.screenWidth) return (!o.screenWidth);
- if (screenHeight || o.screenHeight) return (!o.screenHeight);
- if (sdkVersion || o.sdkVersion) return (!o.sdkVersion);
- if (minorVersion || o.minorVersion) return (!o.minorVersion);
- // Both configurations have nothing defined except some possible future
- // value. Returning the comparison of the two configurations is a
- // "best effort" at this point to protect against incorrect dominations.
- return *this != o;
-}
-
-bool ConfigDescription::conflictsWith(const ConfigDescription& o) const {
- // This method should be updated as new configuration parameters are
- // introduced (e.g. screenConfig2).
- auto pred = [](const uint32_t a, const uint32_t b) -> bool {
- return a == 0 || b == 0 || a == b;
- };
- // The values here can be found in ResTable_config#match. Density and range
- // values can't lead to conflicts, and are ignored.
- return !pred(mcc, o.mcc)
- || !pred(mnc, o.mnc)
- || !pred(locale, o.locale)
- || !pred(screenLayout & MASK_LAYOUTDIR, o.screenLayout & MASK_LAYOUTDIR)
- || !pred(screenLayout & MASK_SCREENLONG, o.screenLayout & MASK_SCREENLONG)
- || !pred(screenLayout & MASK_UI_MODE_TYPE, o.screenLayout & MASK_UI_MODE_TYPE)
- || !pred(uiMode & MASK_UI_MODE_TYPE, o.uiMode & MASK_UI_MODE_TYPE)
- || !pred(uiMode & MASK_UI_MODE_NIGHT, o.uiMode & MASK_UI_MODE_NIGHT)
- || !pred(screenLayout2 & MASK_SCREENROUND, o.screenLayout2 & MASK_SCREENROUND)
- || !pred(orientation, o.orientation)
- || !pred(touchscreen, o.touchscreen)
- || !pred(inputFlags & MASK_KEYSHIDDEN, o.inputFlags & MASK_KEYSHIDDEN)
- || !pred(inputFlags & MASK_NAVHIDDEN, o.inputFlags & MASK_NAVHIDDEN)
- || !pred(keyboard, o.keyboard)
- || !pred(navigation, o.navigation);
-}
-
-bool ConfigDescription::isCompatibleWith(const ConfigDescription& o) const {
- return !conflictsWith(o) && !dominates(o) && !o.dominates(*this);
-}
-
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index d801621..97d0f38 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -17,11 +17,12 @@
#ifndef AAPT_CONFIG_DESCRIPTION_H
#define AAPT_CONFIG_DESCRIPTION_H
-#include "util/StringPiece.h"
-
-#include <androidfw/ResourceTypes.h>
#include <ostream>
+#include "androidfw/ResourceTypes.h"
+
+#include "util/StringPiece.h"
+
namespace aapt {
/*
@@ -29,148 +30,152 @@
* initialization and comparison methods.
*/
struct ConfigDescription : public android::ResTable_config {
- /**
- * Returns an immutable default config.
- */
- static const ConfigDescription& defaultConfig();
+ /**
+ * Returns an immutable default config.
+ */
+ static const ConfigDescription& DefaultConfig();
- /*
- * Parse a string of the form 'fr-sw600dp-land' and fill in the
- * given ResTable_config with resulting configuration parameters.
- *
- * The resulting configuration has the appropriate sdkVersion defined
- * for backwards compatibility.
- */
- static bool parse(const StringPiece& str, ConfigDescription* out = nullptr);
+ /*
+ * Parse a string of the form 'fr-sw600dp-land' and fill in the
+ * given ResTable_config with resulting configuration parameters.
+ *
+ * The resulting configuration has the appropriate sdkVersion defined
+ * for backwards compatibility.
+ */
+ static bool Parse(const StringPiece& str, ConfigDescription* out = nullptr);
- /**
- * If the configuration uses an axis that was added after
- * the original Android release, make sure the SDK version
- * is set accordingly.
- */
- static void applyVersionForCompatibility(ConfigDescription* config);
+ /**
+ * If the configuration uses an axis that was added after
+ * the original Android release, make sure the SDK version
+ * is set accordingly.
+ */
+ static void ApplyVersionForCompatibility(ConfigDescription* config);
- ConfigDescription();
- ConfigDescription(const android::ResTable_config& o); // NOLINT(implicit)
- ConfigDescription(const ConfigDescription& o);
- ConfigDescription(ConfigDescription&& o);
+ ConfigDescription();
+ ConfigDescription(const android::ResTable_config& o); // NOLINT(implicit)
+ ConfigDescription(const ConfigDescription& o);
+ ConfigDescription(ConfigDescription&& o);
- ConfigDescription& operator=(const android::ResTable_config& o);
- ConfigDescription& operator=(const ConfigDescription& o);
- ConfigDescription& operator=(ConfigDescription&& o);
+ ConfigDescription& operator=(const android::ResTable_config& o);
+ ConfigDescription& operator=(const ConfigDescription& o);
+ ConfigDescription& operator=(ConfigDescription&& o);
- ConfigDescription copyWithoutSdkVersion() const;
+ ConfigDescription CopyWithoutSdkVersion() const;
- /**
- * A configuration X dominates another configuration Y, if X has at least the
- * precedence of Y and X is strictly more general than Y: for any type defined
- * by X, the same type is defined by Y with a value equal to or, in the case
- * of ranges, more specific than that of X.
- *
- * For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
- * does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
- */
- bool dominates(const ConfigDescription& o) const;
+ /**
+ * A configuration X dominates another configuration Y, if X has at least the
+ * precedence of Y and X is strictly more general than Y: for any type defined
+ * by X, the same type is defined by Y with a value equal to or, in the case
+ * of ranges, more specific than that of X.
+ *
+ * For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
+ * does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
+ */
+ bool Dominates(const ConfigDescription& o) const;
- /**
- * Returns true if this configuration defines a more important configuration
- * parameter than o. For example, "en" has higher precedence than "v23",
- * whereas "en" has the same precedence as "en-v23".
- */
- bool hasHigherPrecedenceThan(const ConfigDescription& o) const;
+ /**
+ * Returns true if this configuration defines a more important configuration
+ * parameter than o. For example, "en" has higher precedence than "v23",
+ * whereas "en" has the same precedence as "en-v23".
+ */
+ bool HasHigherPrecedenceThan(const ConfigDescription& o) const;
- /**
- * A configuration conflicts with another configuration if both
- * configurations define an incompatible configuration parameter. An
- * incompatible configuration parameter is a non-range, non-density parameter
- * that is defined in both configurations as a different, non-default value.
- */
- bool conflictsWith(const ConfigDescription& o) const;
+ /**
+ * A configuration conflicts with another configuration if both
+ * configurations define an incompatible configuration parameter. An
+ * incompatible configuration parameter is a non-range, non-density parameter
+ * that is defined in both configurations as a different, non-default value.
+ */
+ bool ConflictsWith(const ConfigDescription& o) const;
- /**
- * A configuration is compatible with another configuration if both
- * configurations can match a common concrete device configuration and are
- * unrelated by domination. For example, land-v11 conflicts with port-v21
- * but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
- */
- bool isCompatibleWith(const ConfigDescription& o) const;
+ /**
+ * A configuration is compatible with another configuration if both
+ * configurations can match a common concrete device configuration and are
+ * unrelated by domination. For example, land-v11 conflicts with port-v21
+ * but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
+ */
+ bool IsCompatibleWith(const ConfigDescription& o) const;
- bool matchWithDensity(const ConfigDescription& o) const;
+ bool MatchWithDensity(const ConfigDescription& o) const;
- bool operator<(const ConfigDescription& o) const;
- bool operator<=(const ConfigDescription& o) const;
- bool operator==(const ConfigDescription& o) const;
- bool operator!=(const ConfigDescription& o) const;
- bool operator>=(const ConfigDescription& o) const;
- bool operator>(const ConfigDescription& o) const;
+ bool operator<(const ConfigDescription& o) const;
+ bool operator<=(const ConfigDescription& o) const;
+ bool operator==(const ConfigDescription& o) const;
+ bool operator!=(const ConfigDescription& o) const;
+ bool operator>=(const ConfigDescription& o) const;
+ bool operator>(const ConfigDescription& o) const;
};
inline ConfigDescription::ConfigDescription() {
- memset(this, 0, sizeof(*this));
- size = sizeof(android::ResTable_config);
+ memset(this, 0, sizeof(*this));
+ size = sizeof(android::ResTable_config);
}
inline ConfigDescription::ConfigDescription(const android::ResTable_config& o) {
- *static_cast<android::ResTable_config*>(this) = o;
- size = sizeof(android::ResTable_config);
+ *static_cast<android::ResTable_config*>(this) = o;
+ size = sizeof(android::ResTable_config);
}
inline ConfigDescription::ConfigDescription(const ConfigDescription& o) {
- *static_cast<android::ResTable_config*>(this) = o;
+ *static_cast<android::ResTable_config*>(this) = o;
}
inline ConfigDescription::ConfigDescription(ConfigDescription&& o) {
- *this = o;
+ *this = o;
}
-inline ConfigDescription& ConfigDescription::operator=(const android::ResTable_config& o) {
- *static_cast<android::ResTable_config*>(this) = o;
- size = sizeof(android::ResTable_config);
- return *this;
+inline ConfigDescription& ConfigDescription::operator=(
+ const android::ResTable_config& o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ size = sizeof(android::ResTable_config);
+ return *this;
}
-inline ConfigDescription& ConfigDescription::operator=(const ConfigDescription& o) {
- *static_cast<android::ResTable_config*>(this) = o;
- return *this;
+inline ConfigDescription& ConfigDescription::operator=(
+ const ConfigDescription& o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ return *this;
}
inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) {
- *this = o;
- return *this;
+ *this = o;
+ return *this;
}
-inline bool ConfigDescription::matchWithDensity(const ConfigDescription& o) const {
- return match(o) && (density == 0 || density == o.density);
+inline bool ConfigDescription::MatchWithDensity(
+ const ConfigDescription& o) const {
+ return match(o) && (density == 0 || density == o.density);
}
inline bool ConfigDescription::operator<(const ConfigDescription& o) const {
- return compare(o) < 0;
+ return compare(o) < 0;
}
inline bool ConfigDescription::operator<=(const ConfigDescription& o) const {
- return compare(o) <= 0;
+ return compare(o) <= 0;
}
inline bool ConfigDescription::operator==(const ConfigDescription& o) const {
- return compare(o) == 0;
+ return compare(o) == 0;
}
inline bool ConfigDescription::operator!=(const ConfigDescription& o) const {
- return compare(o) != 0;
+ return compare(o) != 0;
}
inline bool ConfigDescription::operator>=(const ConfigDescription& o) const {
- return compare(o) >= 0;
+ return compare(o) >= 0;
}
inline bool ConfigDescription::operator>(const ConfigDescription& o) const {
- return compare(o) > 0;
+ return compare(o) > 0;
}
-inline ::std::ostream& operator<<(::std::ostream& out, const ConfigDescription& o) {
- return out << o.toString().string();
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ConfigDescription& o) {
+ return out << o.toString().string();
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_CONFIG_DESCRIPTION_H
+#endif // AAPT_CONFIG_DESCRIPTION_H
diff --git a/tools/aapt2/ConfigDescription_test.cpp b/tools/aapt2/ConfigDescription_test.cpp
index 455a57f..c331dc0 100644
--- a/tools/aapt2/ConfigDescription_test.cpp
+++ b/tools/aapt2/ConfigDescription_test.cpp
@@ -15,84 +15,88 @@
*/
#include "ConfigDescription.h"
+
+#include <string>
+
#include "SdkConstants.h"
#include "test/Test.h"
#include "util/StringPiece.h"
-#include <string>
-
namespace aapt {
-static ::testing::AssertionResult TestParse(const StringPiece& input,
- ConfigDescription* config = nullptr) {
- if (ConfigDescription::parse(input, config)) {
- return ::testing::AssertionSuccess() << input << " was successfully parsed";
- }
- return ::testing::AssertionFailure() << input << " could not be parsed";
+static ::testing::AssertionResult TestParse(
+ const StringPiece& input, ConfigDescription* config = nullptr) {
+ if (ConfigDescription::Parse(input, config)) {
+ return ::testing::AssertionSuccess() << input << " was successfully parsed";
+ }
+ return ::testing::AssertionFailure() << input << " could not be parsed";
}
TEST(ConfigDescriptionTest, ParseFailWhenQualifiersAreOutOfOrder) {
- EXPECT_FALSE(TestParse("en-sw600dp-ldrtl"));
- EXPECT_FALSE(TestParse("land-en"));
- EXPECT_FALSE(TestParse("hdpi-320dpi"));
+ EXPECT_FALSE(TestParse("en-sw600dp-ldrtl"));
+ EXPECT_FALSE(TestParse("land-en"));
+ EXPECT_FALSE(TestParse("hdpi-320dpi"));
}
TEST(ConfigDescriptionTest, ParseFailWhenQualifiersAreNotMatched) {
- EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL"));
+ EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL"));
}
TEST(ConfigDescriptionTest, ParseFailWhenQualifiersHaveTrailingDash) {
- EXPECT_FALSE(TestParse("en-sw600dp-land-"));
+ EXPECT_FALSE(TestParse("en-sw600dp-land-"));
}
TEST(ConfigDescriptionTest, ParseBasicQualifiers) {
- ConfigDescription config;
- EXPECT_TRUE(TestParse("", &config));
- EXPECT_EQ(std::string(""), config.toString().string());
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("", &config));
+ EXPECT_EQ(std::string(""), config.toString().string());
- EXPECT_TRUE(TestParse("fr-land", &config));
- EXPECT_EQ(std::string("fr-land"), config.toString().string());
+ EXPECT_TRUE(TestParse("fr-land", &config));
+ EXPECT_EQ(std::string("fr-land"), config.toString().string());
- EXPECT_TRUE(TestParse("mcc310-pl-sw720dp-normal-long-port-night-"
- "xhdpi-keyssoft-qwerty-navexposed-nonav", &config));
- EXPECT_EQ(std::string("mcc310-pl-sw720dp-normal-long-port-night-"
- "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"), config.toString().string());
+ EXPECT_TRUE(
+ TestParse("mcc310-pl-sw720dp-normal-long-port-night-"
+ "xhdpi-keyssoft-qwerty-navexposed-nonav",
+ &config));
+ EXPECT_EQ(std::string("mcc310-pl-sw720dp-normal-long-port-night-"
+ "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"),
+ config.toString().string());
}
TEST(ConfigDescriptionTest, ParseLocales) {
- ConfigDescription config;
- EXPECT_TRUE(TestParse("en-rUS", &config));
- EXPECT_EQ(std::string("en-rUS"), config.toString().string());
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("en-rUS", &config));
+ EXPECT_EQ(std::string("en-rUS"), config.toString().string());
}
TEST(ConfigDescriptionTest, ParseQualifierAddedInApi13) {
- ConfigDescription config;
- EXPECT_TRUE(TestParse("sw600dp", &config));
- EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("sw600dp", &config));
+ EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
- EXPECT_TRUE(TestParse("sw600dp-v8", &config));
- EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
+ EXPECT_TRUE(TestParse("sw600dp-v8", &config));
+ EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
}
TEST(ConfigDescriptionTest, ParseCarAttribute) {
- ConfigDescription config;
- EXPECT_TRUE(TestParse("car", &config));
- EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_CAR, config.uiMode);
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("car", &config));
+ EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_CAR, config.uiMode);
}
TEST(ConfigDescriptionTest, TestParsingRoundQualifier) {
- ConfigDescription config;
- EXPECT_TRUE(TestParse("round", &config));
- EXPECT_EQ(android::ResTable_config::SCREENROUND_YES,
- config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
- EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
- EXPECT_EQ(std::string("round-v23"), config.toString().string());
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("round", &config));
+ EXPECT_EQ(android::ResTable_config::SCREENROUND_YES,
+ config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
+ EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
+ EXPECT_EQ(std::string("round-v23"), config.toString().string());
- EXPECT_TRUE(TestParse("notround", &config));
- EXPECT_EQ(android::ResTable_config::SCREENROUND_NO,
- config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
- EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
- EXPECT_EQ(std::string("notround-v23"), config.toString().string());
+ EXPECT_TRUE(TestParse("notround", &config));
+ EXPECT_EQ(android::ResTable_config::SCREENROUND_NO,
+ config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
+ EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
+ EXPECT_EQ(std::string("notround-v23"), config.toString().string());
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 304e571..60b01e3 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -15,10 +15,6 @@
*/
#include "Debug.h"
-#include "ResourceTable.h"
-#include "ResourceValues.h"
-#include "util/Util.h"
-#include "ValueVisitor.h"
#include <algorithm>
#include <iostream>
@@ -28,278 +24,290 @@
#include <set>
#include <vector>
+#include "android-base/logging.h"
+
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
+#include "util/Util.h"
+
namespace aapt {
class PrintVisitor : public ValueVisitor {
-public:
- using ValueVisitor::visit;
+ public:
+ using ValueVisitor::Visit;
- void visit(Attribute* attr) override {
- std::cout << "(attr) type=";
- attr->printMask(&std::cout);
- static constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM |
- android::ResTable_map::TYPE_FLAGS;
- if (attr->typeMask & kMask) {
- for (const auto& symbol : attr->symbols) {
- std::cout << "\n " << symbol.symbol.name.value().entry;
- if (symbol.symbol.id) {
- std::cout << " (" << symbol.symbol.id.value() << ")";
- }
- std::cout << " = " << symbol.value;
- }
+ void Visit(Attribute* attr) override {
+ std::cout << "(attr) type=";
+ attr->PrintMask(&std::cout);
+ static constexpr uint32_t kMask =
+ android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_FLAGS;
+ if (attr->type_mask & kMask) {
+ for (const auto& symbol : attr->symbols) {
+ std::cout << "\n " << symbol.symbol.name.value().entry;
+ if (symbol.symbol.id) {
+ std::cout << " (" << symbol.symbol.id.value() << ")";
}
+ std::cout << " = " << symbol.value;
+ }
}
+ }
- void visit(Style* style) override {
- std::cout << "(style)";
- if (style->parent) {
- const Reference& parentRef = style->parent.value();
- std::cout << " parent=";
- if (parentRef.name) {
- if (parentRef.privateReference) {
- std::cout << "*";
- }
- std::cout << parentRef.name.value() << " ";
- }
-
- if (parentRef.id) {
- std::cout << parentRef.id.value();
- }
+ void Visit(Style* style) override {
+ std::cout << "(style)";
+ if (style->parent) {
+ const Reference& parent_ref = style->parent.value();
+ std::cout << " parent=";
+ if (parent_ref.name) {
+ if (parent_ref.private_reference) {
+ std::cout << "*";
}
+ std::cout << parent_ref.name.value() << " ";
+ }
- for (const auto& entry : style->entries) {
- std::cout << "\n ";
- if (entry.key.name) {
- const ResourceName& name = entry.key.name.value();
- if (!name.package.empty()) {
- std::cout << name.package << ":";
- }
- std::cout << name.entry;
- }
+ if (parent_ref.id) {
+ std::cout << parent_ref.id.value();
+ }
+ }
- if (entry.key.id) {
- std::cout << "(" << entry.key.id.value() << ")";
- }
-
- std::cout << "=" << *entry.value;
+ for (const auto& entry : style->entries) {
+ std::cout << "\n ";
+ if (entry.key.name) {
+ const ResourceName& name = entry.key.name.value();
+ if (!name.package.empty()) {
+ std::cout << name.package << ":";
}
+ std::cout << name.entry;
+ }
+
+ if (entry.key.id) {
+ std::cout << "(" << entry.key.id.value() << ")";
+ }
+
+ std::cout << "=" << *entry.value;
}
+ }
- void visit(Array* array) override {
- array->print(&std::cout);
- }
+ void Visit(Array* array) override { array->Print(&std::cout); }
- void visit(Plural* plural) override {
- plural->print(&std::cout);
- }
+ void Visit(Plural* plural) override { plural->Print(&std::cout); }
- void visit(Styleable* styleable) override {
- std::cout << "(styleable)";
- for (const auto& attr : styleable->entries) {
- std::cout << "\n ";
- if (attr.name) {
- const ResourceName& name = attr.name.value();
- if (!name.package.empty()) {
- std::cout << name.package << ":";
- }
- std::cout << name.entry;
- }
-
- if (attr.id) {
- std::cout << "(" << attr.id.value() << ")";
- }
+ void Visit(Styleable* styleable) override {
+ std::cout << "(styleable)";
+ for (const auto& attr : styleable->entries) {
+ std::cout << "\n ";
+ if (attr.name) {
+ const ResourceName& name = attr.name.value();
+ if (!name.package.empty()) {
+ std::cout << name.package << ":";
}
- }
+ std::cout << name.entry;
+ }
- void visitItem(Item* item) override {
- item->print(&std::cout);
+ if (attr.id) {
+ std::cout << "(" << attr.id.value() << ")";
+ }
}
+ }
+
+ void VisitItem(Item* item) override { item->Print(&std::cout); }
};
-void Debug::printTable(ResourceTable* table, const DebugPrintTableOptions& options) {
- PrintVisitor visitor;
+void Debug::PrintTable(ResourceTable* table,
+ const DebugPrintTableOptions& options) {
+ PrintVisitor visitor;
- for (auto& package : table->packages) {
- std::cout << "Package name=" << package->name;
- if (package->id) {
- std::cout << " id=" << std::hex << (int) package->id.value() << std::dec;
+ for (auto& package : table->packages) {
+ std::cout << "Package name=" << package->name;
+ if (package->id) {
+ std::cout << " id=" << std::hex << (int)package->id.value() << std::dec;
+ }
+ std::cout << std::endl;
+
+ for (const auto& type : package->types) {
+ std::cout << "\n type " << type->type;
+ if (type->id) {
+ std::cout << " id=" << std::hex << (int)type->id.value() << std::dec;
+ }
+ std::cout << " entryCount=" << type->entries.size() << std::endl;
+
+ std::vector<const ResourceEntry*> sorted_entries;
+ for (const auto& entry : type->entries) {
+ auto iter = std::lower_bound(
+ sorted_entries.begin(), sorted_entries.end(), entry.get(),
+ [](const ResourceEntry* a, const ResourceEntry* b) -> bool {
+ if (a->id && b->id) {
+ return a->id.value() < b->id.value();
+ } else if (a->id) {
+ return true;
+ } else {
+ return false;
+ }
+ });
+ sorted_entries.insert(iter, entry.get());
+ }
+
+ for (const ResourceEntry* entry : sorted_entries) {
+ ResourceId id(package->id ? package->id.value() : uint8_t(0),
+ type->id ? type->id.value() : uint8_t(0),
+ entry->id ? entry->id.value() : uint16_t(0));
+ ResourceName name(package->name, type->type, entry->name);
+
+ std::cout << " spec resource " << id << " " << name;
+ switch (entry->symbol_status.state) {
+ case SymbolState::kPublic:
+ std::cout << " PUBLIC";
+ break;
+ case SymbolState::kPrivate:
+ std::cout << " _PRIVATE_";
+ break;
+ default:
+ break;
}
+
std::cout << std::endl;
- for (const auto& type : package->types) {
- std::cout << "\n type " << type->type;
- if (type->id) {
- std::cout << " id=" << std::hex << (int) type->id.value() << std::dec;
- }
- std::cout << " entryCount=" << type->entries.size() << std::endl;
-
- std::vector<const ResourceEntry*> sortedEntries;
- for (const auto& entry : type->entries) {
- auto iter = std::lower_bound(sortedEntries.begin(), sortedEntries.end(), entry.get(),
- [](const ResourceEntry* a, const ResourceEntry* b) -> bool {
- if (a->id && b->id) {
- return a->id.value() < b->id.value();
- } else if (a->id) {
- return true;
- } else {
- return false;
- }
- });
- sortedEntries.insert(iter, entry.get());
- }
-
- for (const ResourceEntry* entry : sortedEntries) {
- ResourceId id(package->id ? package->id.value() : uint8_t(0),
- type->id ? type->id.value() : uint8_t(0),
- entry->id ? entry->id.value() : uint16_t(0));
- ResourceName name(package->name, type->type, entry->name);
-
- std::cout << " spec resource " << id << " " << name;
- switch (entry->symbolStatus.state) {
- case SymbolState::kPublic: std::cout << " PUBLIC"; break;
- case SymbolState::kPrivate: std::cout << " _PRIVATE_"; break;
- default: break;
- }
-
- std::cout << std::endl;
-
- for (const auto& value : entry->values) {
- std::cout << " (" << value->config << ") ";
- value->value->accept(&visitor);
- if (options.showSources && !value->value->getSource().path.empty()) {
- std::cout << " src=" << value->value->getSource();
- }
- std::cout << std::endl;
- }
- }
+ for (const auto& value : entry->values) {
+ std::cout << " (" << value->config << ") ";
+ value->value->Accept(&visitor);
+ if (options.show_sources && !value->value->GetSource().path.empty()) {
+ std::cout << " src=" << value->value->GetSource();
+ }
+ std::cout << std::endl;
}
+ }
}
+ }
}
-static size_t getNodeIndex(const std::vector<ResourceName>& names, const ResourceName& name) {
- auto iter = std::lower_bound(names.begin(), names.end(), name);
- assert(iter != names.end() && *iter == name);
- return std::distance(names.begin(), iter);
+static size_t GetNodeIndex(const std::vector<ResourceName>& names,
+ const ResourceName& name) {
+ auto iter = std::lower_bound(names.begin(), names.end(), name);
+ CHECK(iter != names.end());
+ CHECK(*iter == name);
+ return std::distance(names.begin(), iter);
}
-void Debug::printStyleGraph(ResourceTable* table, const ResourceName& targetStyle) {
- std::map<ResourceName, std::set<ResourceName>> graph;
+void Debug::PrintStyleGraph(ResourceTable* table,
+ const ResourceName& target_style) {
+ std::map<ResourceName, std::set<ResourceName>> graph;
- std::queue<ResourceName> stylesToVisit;
- stylesToVisit.push(targetStyle);
- for (; !stylesToVisit.empty(); stylesToVisit.pop()) {
- const ResourceName& styleName = stylesToVisit.front();
- std::set<ResourceName>& parents = graph[styleName];
- if (!parents.empty()) {
- // We've already visited this style.
- continue;
+ std::queue<ResourceName> styles_to_visit;
+ styles_to_visit.push(target_style);
+ for (; !styles_to_visit.empty(); styles_to_visit.pop()) {
+ const ResourceName& style_name = styles_to_visit.front();
+ std::set<ResourceName>& parents = graph[style_name];
+ if (!parents.empty()) {
+ // We've already visited this style.
+ continue;
+ }
+
+ Maybe<ResourceTable::SearchResult> result = table->FindResource(style_name);
+ if (result) {
+ ResourceEntry* entry = result.value().entry;
+ for (const auto& value : entry->values) {
+ if (Style* style = ValueCast<Style>(value->value.get())) {
+ if (style->parent && style->parent.value().name) {
+ parents.insert(style->parent.value().name.value());
+ styles_to_visit.push(style->parent.value().name.value());
+ }
}
-
- Maybe<ResourceTable::SearchResult> result = table->findResource(styleName);
- if (result) {
- ResourceEntry* entry = result.value().entry;
- for (const auto& value : entry->values) {
- if (Style* style = valueCast<Style>(value->value.get())) {
- if (style->parent && style->parent.value().name) {
- parents.insert(style->parent.value().name.value());
- stylesToVisit.push(style->parent.value().name.value());
- }
- }
- }
- }
+ }
}
+ }
- std::vector<ResourceName> names;
- for (const auto& entry : graph) {
- names.push_back(entry.first);
+ std::vector<ResourceName> names;
+ for (const auto& entry : graph) {
+ names.push_back(entry.first);
+ }
+
+ std::cout << "digraph styles {\n";
+ for (const auto& name : names) {
+ std::cout << " node_" << GetNodeIndex(names, name) << " [label=\"" << name
+ << "\"];\n";
+ }
+
+ for (const auto& entry : graph) {
+ const ResourceName& style_name = entry.first;
+ size_t style_node_index = GetNodeIndex(names, style_name);
+
+ for (const auto& parent_name : entry.second) {
+ std::cout << " node_" << style_node_index << " -> "
+ << "node_" << GetNodeIndex(names, parent_name) << ";\n";
}
+ }
- std::cout << "digraph styles {\n";
- for (const auto& name : names) {
- std::cout << " node_" << getNodeIndex(names, name)
- << " [label=\"" << name << "\"];\n";
- }
-
- for (const auto& entry : graph) {
- const ResourceName& styleName = entry.first;
- size_t styleNodeIndex = getNodeIndex(names, styleName);
-
- for (const auto& parentName : entry.second) {
- std::cout << " node_" << styleNodeIndex << " -> "
- << "node_" << getNodeIndex(names, parentName) << ";\n";
- }
- }
-
- std::cout << "}" << std::endl;
+ std::cout << "}" << std::endl;
}
-void Debug::dumpHex(const void* data, size_t len) {
- const uint8_t* d = (const uint8_t*) data;
- for (size_t i = 0; i < len; i++) {
- std::cerr << std::hex << std::setfill('0') << std::setw(2) << (uint32_t) d[i] << " ";
- if (i % 8 == 7) {
- std::cerr << "\n";
- }
+void Debug::DumpHex(const void* data, size_t len) {
+ const uint8_t* d = (const uint8_t*)data;
+ for (size_t i = 0; i < len; i++) {
+ std::cerr << std::hex << std::setfill('0') << std::setw(2) << (uint32_t)d[i]
+ << " ";
+ if (i % 8 == 7) {
+ std::cerr << "\n";
}
+ }
- if (len - 1 % 8 != 7) {
- std::cerr << std::endl;
- }
+ if (len - 1 % 8 != 7) {
+ std::cerr << std::endl;
+ }
}
namespace {
class XmlPrinter : public xml::Visitor {
-public:
- using xml::Visitor::visit;
+ public:
+ using xml::Visitor::Visit;
- void visit(xml::Element* el) override {
- std::cerr << mPrefix;
- std::cerr << "E: ";
- if (!el->namespaceUri.empty()) {
- std::cerr << el->namespaceUri << ":";
- }
- std::cerr << el->name << " (line=" << el->lineNumber << ")\n";
+ void Visit(xml::Element* el) override {
+ std::cerr << prefix_;
+ std::cerr << "E: ";
+ if (!el->namespace_uri.empty()) {
+ std::cerr << el->namespace_uri << ":";
+ }
+ std::cerr << el->name << " (line=" << el->line_number << ")\n";
- for (const xml::Attribute& attr : el->attributes) {
- std::cerr << mPrefix << " A: ";
- if (!attr.namespaceUri.empty()) {
- std::cerr << attr.namespaceUri << ":";
- }
- std::cerr << attr.name << "=" << attr.value << "\n";
- }
-
- const size_t previousSize = mPrefix.size();
- mPrefix += " ";
- xml::Visitor::visit(el);
- mPrefix.resize(previousSize);
+ for (const xml::Attribute& attr : el->attributes) {
+ std::cerr << prefix_ << " A: ";
+ if (!attr.namespace_uri.empty()) {
+ std::cerr << attr.namespace_uri << ":";
+ }
+ std::cerr << attr.name << "=" << attr.value << "\n";
}
- void visit(xml::Namespace* ns) override {
- std::cerr << mPrefix;
- std::cerr << "N: " << ns->namespacePrefix << "=" << ns->namespaceUri
- << " (line=" << ns->lineNumber << ")\n";
+ const size_t previous_size = prefix_.size();
+ prefix_ += " ";
+ xml::Visitor::Visit(el);
+ prefix_.resize(previous_size);
+ }
- const size_t previousSize = mPrefix.size();
- mPrefix += " ";
- xml::Visitor::visit(ns);
- mPrefix.resize(previousSize);
- }
+ void Visit(xml::Namespace* ns) override {
+ std::cerr << prefix_;
+ std::cerr << "N: " << ns->namespace_prefix << "=" << ns->namespace_uri
+ << " (line=" << ns->line_number << ")\n";
- void visit(xml::Text* text) override {
- std::cerr << mPrefix;
- std::cerr << "T: '" << text->text << "'\n";
- }
+ const size_t previous_size = prefix_.size();
+ prefix_ += " ";
+ xml::Visitor::Visit(ns);
+ prefix_.resize(previous_size);
+ }
-private:
- std::string mPrefix;
+ void Visit(xml::Text* text) override {
+ std::cerr << prefix_;
+ std::cerr << "T: '" << text->text << "'\n";
+ }
+
+ private:
+ std::string prefix_;
};
-} // namespace
+} // namespace
-void Debug::dumpXml(xml::XmlResource* doc) {
- XmlPrinter printer;
- doc->root->accept(&printer);
+void Debug::DumpXml(xml::XmlResource* doc) {
+ XmlPrinter printer;
+ doc->root->Accept(&printer);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index c0fcbf1..56e2e95 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -17,27 +17,28 @@
#ifndef AAPT_DEBUG_H
#define AAPT_DEBUG_H
+// Include for printf-like debugging.
+#include <iostream>
+
#include "Resource.h"
#include "ResourceTable.h"
#include "xml/XmlDom.h"
-// Include for printf-like debugging.
-#include <iostream>
-
namespace aapt {
struct DebugPrintTableOptions {
- bool showSources = false;
+ bool show_sources = false;
};
struct Debug {
- static void printTable(ResourceTable* table, const DebugPrintTableOptions& options = {});
- static void printStyleGraph(ResourceTable* table,
- const ResourceName& targetStyle);
- static void dumpHex(const void* data, size_t len);
- static void dumpXml(xml::XmlResource* doc);
+ static void PrintTable(ResourceTable* table,
+ const DebugPrintTableOptions& options = {});
+ static void PrintStyleGraph(ResourceTable* table,
+ const ResourceName& target_style);
+ static void DumpHex(const void* data, size_t len);
+ static void DumpXml(xml::XmlResource* doc);
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_DEBUG_H
+#endif // AAPT_DEBUG_H
diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h
index 725027c..5bc86a9 100644
--- a/tools/aapt2/Diagnostics.h
+++ b/tools/aapt2/Diagnostics.h
@@ -17,131 +17,125 @@
#ifndef AAPT_DIAGNOSTICS_H
#define AAPT_DIAGNOSTICS_H
-#include "Source.h"
-#include "util/StringPiece.h"
-#include "util/Util.h"
-
-#include <android-base/macros.h>
#include <iostream>
#include <sstream>
#include <string>
+#include "android-base/macros.h"
+
+#include "Source.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
namespace aapt {
struct DiagMessageActual {
- Source source;
- std::string message;
+ Source source;
+ std::string message;
};
struct DiagMessage {
-private:
- Source mSource;
- std::stringstream mMessage;
+ public:
+ DiagMessage() = default;
-public:
- DiagMessage() = default;
+ explicit DiagMessage(const StringPiece& src) : source_(src) {}
- explicit DiagMessage(const StringPiece& src) : mSource(src) {
- }
+ explicit DiagMessage(const Source& src) : source_(src) {}
- explicit DiagMessage(const Source& src) : mSource(src) {
- }
+ explicit DiagMessage(size_t line) : source_(Source().WithLine(line)) {}
- explicit DiagMessage(size_t line) : mSource(Source().withLine(line)) {
- }
+ template <typename T>
+ DiagMessage& operator<<(const T& value) {
+ message_ << value;
+ return *this;
+ }
- template <typename T>
- DiagMessage& operator<<(const T& value) {
- mMessage << value;
- return *this;
- }
+ DiagMessageActual Build() const {
+ return DiagMessageActual{source_, message_.str()};
+ }
- DiagMessageActual build() const {
- return DiagMessageActual{ mSource, mMessage.str() };
- }
+ private:
+ Source source_;
+ std::stringstream message_;
};
struct IDiagnostics {
- virtual ~IDiagnostics() = default;
+ virtual ~IDiagnostics() = default;
- enum class Level {
- Note,
- Warn,
- Error
- };
+ enum class Level { Note, Warn, Error };
- virtual void log(Level level, DiagMessageActual& actualMsg) = 0;
+ virtual void Log(Level level, DiagMessageActual& actualMsg) = 0;
- virtual void error(const DiagMessage& message) {
- DiagMessageActual actual = message.build();
- log(Level::Error, actual);
- }
+ virtual void Error(const DiagMessage& message) {
+ DiagMessageActual actual = message.Build();
+ Log(Level::Error, actual);
+ }
- virtual void warn(const DiagMessage& message) {
- DiagMessageActual actual = message.build();
- log(Level::Warn, actual);
- }
+ virtual void Warn(const DiagMessage& message) {
+ DiagMessageActual actual = message.Build();
+ Log(Level::Warn, actual);
+ }
- virtual void note(const DiagMessage& message) {
- DiagMessageActual actual = message.build();
- log(Level::Note, actual);
- }
+ virtual void Note(const DiagMessage& message) {
+ DiagMessageActual actual = message.Build();
+ Log(Level::Note, actual);
+ }
};
class StdErrDiagnostics : public IDiagnostics {
-public:
- StdErrDiagnostics() = default;
+ public:
+ StdErrDiagnostics() = default;
- void log(Level level, DiagMessageActual& actualMsg) override {
- const char* tag;
+ void Log(Level level, DiagMessageActual& actual_msg) override {
+ const char* tag;
- switch (level) {
- case Level::Error:
- mNumErrors++;
- if (mNumErrors > 20) {
- return;
- }
- tag = "error";
- break;
-
- case Level::Warn:
- tag = "warn";
- break;
-
- case Level::Note:
- tag = "note";
- break;
+ switch (level) {
+ case Level::Error:
+ num_errors_++;
+ if (num_errors_ > 20) {
+ return;
}
+ tag = "error";
+ break;
- if (!actualMsg.source.path.empty()) {
- std::cerr << actualMsg.source << ": ";
- }
- std::cerr << tag << ": " << actualMsg.message << "." << std::endl;
+ case Level::Warn:
+ tag = "warn";
+ break;
+
+ case Level::Note:
+ tag = "note";
+ break;
}
-private:
- size_t mNumErrors = 0;
+ if (!actual_msg.source.path.empty()) {
+ std::cerr << actual_msg.source << ": ";
+ }
+ std::cerr << tag << ": " << actual_msg.message << "." << std::endl;
+ }
- DISALLOW_COPY_AND_ASSIGN(StdErrDiagnostics);
+ private:
+ size_t num_errors_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(StdErrDiagnostics);
};
class SourcePathDiagnostics : public IDiagnostics {
-public:
- SourcePathDiagnostics(const Source& src, IDiagnostics* diag) : mSource(src), mDiag(diag) {
- }
+ public:
+ SourcePathDiagnostics(const Source& src, IDiagnostics* diag)
+ : source_(src), diag_(diag) {}
- void log(Level level, DiagMessageActual& actualMsg) override {
- actualMsg.source.path = mSource.path;
- mDiag->log(level, actualMsg);
- }
+ void Log(Level level, DiagMessageActual& actual_msg) override {
+ actual_msg.source.path = source_.path;
+ diag_->Log(level, actual_msg);
+ }
-private:
- Source mSource;
- IDiagnostics* mDiag;
+ private:
+ Source source_;
+ IDiagnostics* diag_;
- DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics);
+ DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_DIAGNOSTICS_H */
diff --git a/tools/aapt2/DominatorTree.cpp b/tools/aapt2/DominatorTree.cpp
index 29587a8..118a385 100644
--- a/tools/aapt2/DominatorTree.cpp
+++ b/tools/aapt2/DominatorTree.cpp
@@ -14,76 +14,81 @@
* limitations under the License.
*/
-#include "ConfigDescription.h"
#include "DominatorTree.h"
#include <algorithm>
+#include "android-base/logging.h"
+
+#include "ConfigDescription.h"
+
namespace aapt {
DominatorTree::DominatorTree(
- const std::vector<std::unique_ptr<ResourceConfigValue>>& configs) {
- for (const auto& config : configs) {
- mProductRoots[config->product].tryAddChild(
- util::make_unique<Node>(config.get(), nullptr));
- }
+ const std::vector<std::unique_ptr<ResourceConfigValue>>& configs) {
+ for (const auto& config : configs) {
+ product_roots_[config->product].TryAddChild(
+ util::make_unique<Node>(config.get(), nullptr));
+ }
}
-void DominatorTree::accept(Visitor* visitor) {
- for (auto& entry : mProductRoots) {
- visitor->visitTree(entry.first, &entry.second);
- }
+void DominatorTree::Accept(Visitor* visitor) {
+ for (auto& entry : product_roots_) {
+ visitor->VisitTree(entry.first, &entry.second);
+ }
}
-bool DominatorTree::Node::tryAddChild(std::unique_ptr<Node> newChild) {
- assert(newChild->mValue && "cannot add a root or empty node as a child");
- if (mValue && !dominates(newChild.get())) {
- // This is not the root and the child dominates us.
- return false;
- }
- return addChild(std::move(newChild));
+bool DominatorTree::Node::TryAddChild(std::unique_ptr<Node> new_child) {
+ CHECK(new_child->value_) << "cannot add a root or empty node as a child";
+ if (value_ && !Dominates(new_child.get())) {
+ // This is not the root and the child dominates us.
+ return false;
+ }
+ return AddChild(std::move(new_child));
}
-bool DominatorTree::Node::addChild(std::unique_ptr<Node> newChild) {
- bool hasDominatedChildren = false;
- // Demote children dominated by the new config.
- for (auto& child : mChildren) {
- if (newChild->dominates(child.get())) {
- child->mParent = newChild.get();
- newChild->mChildren.push_back(std::move(child));
- child = {};
- hasDominatedChildren = true;
- }
+bool DominatorTree::Node::AddChild(std::unique_ptr<Node> new_child) {
+ bool has_dominated_children = false;
+ // Demote children dominated by the new config.
+ for (auto& child : children_) {
+ if (new_child->Dominates(child.get())) {
+ child->parent_ = new_child.get();
+ new_child->children_.push_back(std::move(child));
+ child = {};
+ has_dominated_children = true;
}
- // Remove dominated children.
- if (hasDominatedChildren) {
- mChildren.erase(std::remove_if(mChildren.begin(), mChildren.end(),
- [](const std::unique_ptr<Node>& child) -> bool {
- return child == nullptr;
- }), mChildren.end());
+ }
+ // Remove dominated children.
+ if (has_dominated_children) {
+ children_.erase(
+ std::remove_if(children_.begin(), children_.end(),
+ [](const std::unique_ptr<Node>& child) -> bool {
+ return child == nullptr;
+ }),
+ children_.end());
+ }
+ // Add the new config to a child if a child dominates the new config.
+ for (auto& child : children_) {
+ if (child->Dominates(new_child.get())) {
+ child->AddChild(std::move(new_child));
+ return true;
}
- // Add the new config to a child if a child dominates the new config.
- for (auto& child : mChildren) {
- if (child->dominates(newChild.get())) {
- child->addChild(std::move(newChild));
- return true;
- }
- }
- // The new config is not dominated by a child, so add it here.
- newChild->mParent = this;
- mChildren.push_back(std::move(newChild));
+ }
+ // The new config is not dominated by a child, so add it here.
+ new_child->parent_ = this;
+ children_.push_back(std::move(new_child));
+ return true;
+}
+
+bool DominatorTree::Node::Dominates(const Node* other) const {
+ // Check root node dominations.
+ if (other->is_root_node()) {
+ return is_root_node();
+ } else if (is_root_node()) {
return true;
+ }
+ // Neither node is a root node; compare the configurations.
+ return value_->config.Dominates(other->value_->config);
}
-bool DominatorTree::Node::dominates(const Node* other) const {
- // Check root node dominations.
- if (other->isRootNode()) {
- return isRootNode();
- } else if (isRootNode()) {
- return true;
- }
- // Neither node is a root node; compare the configurations.
- return mValue->config.dominates(other->mValue->config);
-}
-
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/DominatorTree.h b/tools/aapt2/DominatorTree.h
index ad2df0e..7d50935 100644
--- a/tools/aapt2/DominatorTree.h
+++ b/tools/aapt2/DominatorTree.h
@@ -17,13 +17,13 @@
#ifndef AAPT_DOMINATOR_TREE_H
#define AAPT_DOMINATOR_TREE_H
-#include "ResourceTable.h"
-
#include <map>
#include <memory>
#include <string>
#include <vector>
+#include "ResourceTable.h"
+
namespace aapt {
/**
@@ -46,82 +46,76 @@
* will exhibit undefined behavior.
*/
class DominatorTree {
-public:
- explicit DominatorTree(const std::vector<std::unique_ptr<ResourceConfigValue>>& configs);
+ public:
+ explicit DominatorTree(
+ const std::vector<std::unique_ptr<ResourceConfigValue>>& configs);
- class Node {
- public:
- explicit Node(ResourceConfigValue* value = nullptr, Node* parent = nullptr) :
- mValue(value), mParent(parent) {
- }
+ class Node {
+ public:
+ explicit Node(ResourceConfigValue* value = nullptr, Node* parent = nullptr)
+ : value_(value), parent_(parent) {}
- inline ResourceConfigValue* value() const {
- return mValue;
- }
+ inline ResourceConfigValue* value() const { return value_; }
- inline Node* parent() const {
- return mParent;
- }
+ inline Node* parent() const { return parent_; }
- inline bool isRootNode() const {
- return !mValue;
- }
+ inline bool is_root_node() const { return !value_; }
- inline const std::vector<std::unique_ptr<Node>>& children() const {
- return mChildren;
- }
-
- bool tryAddChild(std::unique_ptr<Node> newChild);
-
- private:
- bool addChild(std::unique_ptr<Node> newChild);
- bool dominates(const Node* other) const;
-
- ResourceConfigValue* mValue;
- Node* mParent;
- std::vector<std::unique_ptr<Node>> mChildren;
-
- DISALLOW_COPY_AND_ASSIGN(Node);
- };
-
- struct Visitor {
- virtual ~Visitor() = default;
- virtual void visitTree(const std::string& product, Node* root) = 0;
- };
-
- class BottomUpVisitor : public Visitor {
- public:
- virtual ~BottomUpVisitor() = default;
-
- void visitTree(const std::string& product, Node* root) override {
- for (auto& child : root->children()) {
- visitNode(child.get());
- }
- }
-
- virtual void visitConfig(Node* node) = 0;
-
- private:
- void visitNode(Node* node) {
- for (auto& child : node->children()) {
- visitNode(child.get());
- }
- visitConfig(node);
- }
- };
-
- void accept(Visitor* visitor);
-
- inline const std::map<std::string, Node>& getProductRoots() const {
- return mProductRoots;
+ inline const std::vector<std::unique_ptr<Node>>& children() const {
+ return children_;
}
-private:
- DISALLOW_COPY_AND_ASSIGN(DominatorTree);
+ bool TryAddChild(std::unique_ptr<Node> new_child);
- std::map<std::string, Node> mProductRoots;
+ private:
+ bool AddChild(std::unique_ptr<Node> new_child);
+ bool Dominates(const Node* other) const;
+
+ ResourceConfigValue* value_;
+ Node* parent_;
+ std::vector<std::unique_ptr<Node>> children_;
+
+ DISALLOW_COPY_AND_ASSIGN(Node);
+ };
+
+ struct Visitor {
+ virtual ~Visitor() = default;
+ virtual void VisitTree(const std::string& product, Node* root) = 0;
+ };
+
+ class BottomUpVisitor : public Visitor {
+ public:
+ virtual ~BottomUpVisitor() = default;
+
+ void VisitTree(const std::string& product, Node* root) override {
+ for (auto& child : root->children()) {
+ VisitNode(child.get());
+ }
+ }
+
+ virtual void VisitConfig(Node* node) = 0;
+
+ private:
+ void VisitNode(Node* node) {
+ for (auto& child : node->children()) {
+ VisitNode(child.get());
+ }
+ VisitConfig(node);
+ }
+ };
+
+ void Accept(Visitor* visitor);
+
+ inline const std::map<std::string, Node>& product_roots() const {
+ return product_roots_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DominatorTree);
+
+ std::map<std::string, Node> product_roots_;
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_DOMINATOR_TREE_H
+#endif // AAPT_DOMINATOR_TREE_H
diff --git a/tools/aapt2/DominatorTree_test.cpp b/tools/aapt2/DominatorTree_test.cpp
index fb850e4..e89c6be 100644
--- a/tools/aapt2/DominatorTree_test.cpp
+++ b/tools/aapt2/DominatorTree_test.cpp
@@ -15,137 +15,149 @@
*/
#include "DominatorTree.h"
-#include "test/Test.h"
-#include "util/Util.h"
#include <sstream>
#include <string>
#include <vector>
+#include "test/Test.h"
+#include "util/Util.h"
+
namespace aapt {
namespace {
class PrettyPrinter : public DominatorTree::Visitor {
-public:
- explicit PrettyPrinter(const int indent = 2) : mIndent(indent) {
- }
+ public:
+ explicit PrettyPrinter(const int indent = 2) : indent_(indent) {}
- void visitTree(const std::string& product, DominatorTree::Node* root) override {
- for (auto& child : root->children()) {
- visitNode(child.get(), 0);
- }
+ void VisitTree(const std::string& product,
+ DominatorTree::Node* root) override {
+ for (auto& child : root->children()) {
+ VisitNode(child.get(), 0);
}
+ }
- std::string toString(DominatorTree* tree) {
- mBuffer.str("");
- mBuffer.clear();
- tree->accept(this);
- return mBuffer.str();
+ std::string ToString(DominatorTree* tree) {
+ buffer_.str("");
+ buffer_.clear();
+ tree->Accept(this);
+ return buffer_.str();
+ }
+
+ private:
+ void VisitConfig(const DominatorTree::Node* node, const int indent) {
+ auto config_string = node->value()->config.toString();
+ buffer_ << std::string(indent, ' ')
+ << (config_string.isEmpty() ? "<default>" : config_string)
+ << std::endl;
+ }
+
+ void VisitNode(const DominatorTree::Node* node, const int indent) {
+ VisitConfig(node, indent);
+ for (const auto& child : node->children()) {
+ VisitNode(child.get(), indent + indent_);
}
+ }
-private:
- void visitConfig(const DominatorTree::Node* node, const int indent) {
- auto configString = node->value()->config.toString();
- mBuffer << std::string(indent, ' ')
- << (configString.isEmpty() ? "<default>" : configString)
- << std::endl;
- }
-
- void visitNode(const DominatorTree::Node* node, const int indent) {
- visitConfig(node, indent);
- for (const auto& child : node->children()) {
- visitNode(child.get(), indent + mIndent);
- }
- }
-
- std::stringstream mBuffer;
- const int mIndent = 2;
+ std::stringstream buffer_;
+ const int indent_ = 2;
};
-} // namespace
+} // namespace
TEST(DominatorTreeTest, DefaultDominatesEverything) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription landConfig = test::parseConfigOrDie("land");
- const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land-v13");
+ const ConfigDescription default_config = {};
+ const ConfigDescription land_config = test::ParseConfigOrDie("land");
+ const ConfigDescription sw600dp_land_config =
+ test::ParseConfigOrDie("sw600dp-land-v13");
- std::vector<std::unique_ptr<ResourceConfigValue>> configs;
- configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
+ std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+ configs.push_back(util::make_unique<ResourceConfigValue>(default_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(land_config, ""));
+ configs.push_back(
+ util::make_unique<ResourceConfigValue>(sw600dp_land_config, ""));
- DominatorTree tree(configs);
- PrettyPrinter printer;
+ DominatorTree tree(configs);
+ PrettyPrinter printer;
- std::string expected =
- "<default>\n"
- " land\n"
- " sw600dp-land-v13\n";
- EXPECT_EQ(expected, printer.toString(&tree));
+ std::string expected =
+ "<default>\n"
+ " land\n"
+ " sw600dp-land-v13\n";
+ EXPECT_EQ(expected, printer.ToString(&tree));
}
TEST(DominatorTreeTest, ProductsAreDominatedSeparately) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription landConfig = test::parseConfigOrDie("land");
- const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land-v13");
+ const ConfigDescription default_config = {};
+ const ConfigDescription land_config = test::ParseConfigOrDie("land");
+ const ConfigDescription sw600dp_land_config =
+ test::ParseConfigOrDie("sw600dp-land-v13");
- std::vector<std::unique_ptr<ResourceConfigValue>> configs;
- configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, "phablet"));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, "phablet"));
+ std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+ configs.push_back(util::make_unique<ResourceConfigValue>(default_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(land_config, ""));
+ configs.push_back(
+ util::make_unique<ResourceConfigValue>(default_config, "phablet"));
+ configs.push_back(
+ util::make_unique<ResourceConfigValue>(sw600dp_land_config, "phablet"));
- DominatorTree tree(configs);
- PrettyPrinter printer;
+ DominatorTree tree(configs);
+ PrettyPrinter printer;
- std::string expected =
- "<default>\n"
- " land\n"
- "<default>\n"
- " sw600dp-land-v13\n";
- EXPECT_EQ(expected, printer.toString(&tree));
+ std::string expected =
+ "<default>\n"
+ " land\n"
+ "<default>\n"
+ " sw600dp-land-v13\n";
+ EXPECT_EQ(expected, printer.ToString(&tree));
}
TEST(DominatorTreeTest, MoreSpecificConfigurationsAreDominated) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription enConfig = test::parseConfigOrDie("en");
- const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
- const ConfigDescription ldrtlConfig = test::parseConfigOrDie("ldrtl-v4");
- const ConfigDescription ldrtlXhdpiConfig = test::parseConfigOrDie("ldrtl-xhdpi-v4");
- const ConfigDescription sw300dpConfig = test::parseConfigOrDie("sw300dp-v13");
- const ConfigDescription sw540dpConfig = test::parseConfigOrDie("sw540dp-v14");
- const ConfigDescription sw600dpConfig = test::parseConfigOrDie("sw600dp-v14");
- const ConfigDescription sw720dpConfig = test::parseConfigOrDie("sw720dp-v13");
- const ConfigDescription v20Config = test::parseConfigOrDie("v20");
+ const ConfigDescription default_config = {};
+ const ConfigDescription en_config = test::ParseConfigOrDie("en");
+ const ConfigDescription en_v21_config = test::ParseConfigOrDie("en-v21");
+ const ConfigDescription ldrtl_config = test::ParseConfigOrDie("ldrtl-v4");
+ const ConfigDescription ldrtl_xhdpi_config =
+ test::ParseConfigOrDie("ldrtl-xhdpi-v4");
+ const ConfigDescription sw300dp_config =
+ test::ParseConfigOrDie("sw300dp-v13");
+ const ConfigDescription sw540dp_config =
+ test::ParseConfigOrDie("sw540dp-v14");
+ const ConfigDescription sw600dp_config =
+ test::ParseConfigOrDie("sw600dp-v14");
+ const ConfigDescription sw720dp_config =
+ test::ParseConfigOrDie("sw720dp-v13");
+ const ConfigDescription v20_config = test::ParseConfigOrDie("v20");
- std::vector<std::unique_ptr<ResourceConfigValue>> configs;
- configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(enConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(enV21Config, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(ldrtlConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(ldrtlXhdpiConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw300dpConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw540dpConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw720dpConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(v20Config, ""));
+ std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+ configs.push_back(util::make_unique<ResourceConfigValue>(default_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(en_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(en_v21_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(ldrtl_config, ""));
+ configs.push_back(
+ util::make_unique<ResourceConfigValue>(ldrtl_xhdpi_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw300dp_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw540dp_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw600dp_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw720dp_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(v20_config, ""));
- DominatorTree tree(configs);
- PrettyPrinter printer;
+ DominatorTree tree(configs);
+ PrettyPrinter printer;
- std::string expected =
- "<default>\n"
- " en\n"
- " en-v21\n"
- " ldrtl-v4\n"
- " ldrtl-xhdpi-v4\n"
- " sw300dp-v13\n"
- " sw540dp-v14\n"
- " sw600dp-v14\n"
- " sw720dp-v13\n"
- " v20\n";
- EXPECT_EQ(expected, printer.toString(&tree));
+ std::string expected =
+ "<default>\n"
+ " en\n"
+ " en-v21\n"
+ " ldrtl-v4\n"
+ " ldrtl-xhdpi-v4\n"
+ " sw300dp-v13\n"
+ " sw540dp-v14\n"
+ " sw600dp-v14\n"
+ " sw720dp-v13\n"
+ " v20\n";
+ EXPECT_EQ(expected, printer.ToString(&tree));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp
index 3731ac7..c98cd37 100644
--- a/tools/aapt2/Flags.cpp
+++ b/tools/aapt2/Flags.cpp
@@ -15,165 +15,178 @@
*/
#include "Flags.h"
-#include "util/StringPiece.h"
-#include "util/Util.h"
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
namespace aapt {
-Flags& Flags::requiredFlag(const StringPiece& name, const StringPiece& description,
- std::string* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- *value = arg.toString();
- return true;
- };
-
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, true, 1, false});
- return *this;
-}
-
-Flags& Flags::requiredFlagList(const StringPiece& name, const StringPiece& description,
- std::vector<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- value->push_back(arg.toString());
- return true;
- };
-
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, true, 1, false });
- return *this;
-}
-
-Flags& Flags::optionalFlag(const StringPiece& name, const StringPiece& description,
- Maybe<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- *value = arg.toString();
- return true;
- };
-
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 1, false });
- return *this;
-}
-
-Flags& Flags::optionalFlagList(const StringPiece& name, const StringPiece& description,
- std::vector<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- value->push_back(arg.toString());
- return true;
- };
-
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 1, false });
- return *this;
-}
-
-Flags& Flags::optionalFlagList(const StringPiece& name, const StringPiece& description,
- std::unordered_set<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- value->insert(arg.toString());
- return true;
- };
-
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 1, false });
- return *this;
-}
-
-Flags& Flags::optionalSwitch(const StringPiece& name, const StringPiece& description,
- bool* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- *value = true;
- return true;
- };
-
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 0, false });
- return *this;
-}
-
-void Flags::usage(const StringPiece& command, std::ostream* out) {
- constexpr size_t kWidth = 50;
-
- *out << command << " [options]";
- for (const Flag& flag : mFlags) {
- if (flag.required) {
- *out << " " << flag.name << " arg";
- }
- }
-
- *out << " files...\n\nOptions:\n";
-
- for (const Flag& flag : mFlags) {
- std::string argLine = flag.name;
- if (flag.numArgs > 0) {
- argLine += " arg";
- }
-
- // Split the description by newlines and write out the argument (which is empty after
- // the first line) followed by the description line. This will make sure that multiline
- // descriptions are still right justified and aligned.
- for (StringPiece line : util::tokenize(flag.description, '\n')) {
- *out << " " << std::setw(kWidth) << std::left << argLine << line << "\n";
- argLine = " ";
- }
- }
- *out << " " << std::setw(kWidth) << std::left << "-h" << "Displays this help menu\n";
- out->flush();
-}
-
-bool Flags::parse(const StringPiece& command, const std::vector<StringPiece>& args,
- std::ostream* outError) {
- for (size_t i = 0; i < args.size(); i++) {
- StringPiece arg = args[i];
- if (*(arg.data()) != '-') {
- mArgs.push_back(arg.toString());
- continue;
- }
-
- if (arg == "-h" || arg == "--help") {
- usage(command, outError);
- return false;
- }
-
- bool match = false;
- for (Flag& flag : mFlags) {
- if (arg == flag.name) {
- if (flag.numArgs > 0) {
- i++;
- if (i >= args.size()) {
- *outError << flag.name << " missing argument.\n\n";
- usage(command, outError);
- return false;
- }
- flag.action(args[i]);
- } else {
- flag.action({});
- }
- flag.parsed = true;
- match = true;
- break;
- }
- }
-
- if (!match) {
- *outError << "unknown option '" << arg << "'.\n\n";
- usage(command, outError);
- return false;
- }
- }
-
- for (const Flag& flag : mFlags) {
- if (flag.required && !flag.parsed) {
- *outError << "missing required flag " << flag.name << "\n\n";
- usage(command, outError);
- return false;
- }
- }
+Flags& Flags::RequiredFlag(const StringPiece& name,
+ const StringPiece& description, std::string* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = arg.ToString();
return true;
+ };
+
+ flags_.push_back(
+ Flag{name.ToString(), description.ToString(), func, true, 1, false});
+ return *this;
}
-const std::vector<std::string>& Flags::getArgs() {
- return mArgs;
+Flags& Flags::RequiredFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::vector<std::string>* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ value->push_back(arg.ToString());
+ return true;
+ };
+
+ flags_.push_back(
+ Flag{name.ToString(), description.ToString(), func, true, 1, false});
+ return *this;
}
-} // namespace aapt
+Flags& Flags::OptionalFlag(const StringPiece& name,
+ const StringPiece& description,
+ Maybe<std::string>* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = arg.ToString();
+ return true;
+ };
+
+ flags_.push_back(
+ Flag{name.ToString(), description.ToString(), func, false, 1, false});
+ return *this;
+}
+
+Flags& Flags::OptionalFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::vector<std::string>* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ value->push_back(arg.ToString());
+ return true;
+ };
+
+ flags_.push_back(
+ Flag{name.ToString(), description.ToString(), func, false, 1, false});
+ return *this;
+}
+
+Flags& Flags::OptionalFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::unordered_set<std::string>* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ value->insert(arg.ToString());
+ return true;
+ };
+
+ flags_.push_back(
+ Flag{name.ToString(), description.ToString(), func, false, 1, false});
+ return *this;
+}
+
+Flags& Flags::OptionalSwitch(const StringPiece& name,
+ const StringPiece& description, bool* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = true;
+ return true;
+ };
+
+ flags_.push_back(
+ Flag{name.ToString(), description.ToString(), func, false, 0, false});
+ return *this;
+}
+
+void Flags::Usage(const StringPiece& command, std::ostream* out) {
+ constexpr size_t kWidth = 50;
+
+ *out << command << " [options]";
+ for (const Flag& flag : flags_) {
+ if (flag.required) {
+ *out << " " << flag.name << " arg";
+ }
+ }
+
+ *out << " files...\n\nOptions:\n";
+
+ for (const Flag& flag : flags_) {
+ std::string argline = flag.name;
+ if (flag.num_args > 0) {
+ argline += " arg";
+ }
+
+ // Split the description by newlines and write out the argument (which is
+ // empty after
+ // the first line) followed by the description line. This will make sure
+ // that multiline
+ // descriptions are still right justified and aligned.
+ for (StringPiece line : util::Tokenize(flag.description, '\n')) {
+ *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
+ argline = " ";
+ }
+ }
+ *out << " " << std::setw(kWidth) << std::left << "-h"
+ << "Displays this help menu\n";
+ out->flush();
+}
+
+bool Flags::Parse(const StringPiece& command,
+ const std::vector<StringPiece>& args,
+ std::ostream* out_error) {
+ for (size_t i = 0; i < args.size(); i++) {
+ StringPiece arg = args[i];
+ if (*(arg.data()) != '-') {
+ args_.push_back(arg.ToString());
+ continue;
+ }
+
+ if (arg == "-h" || arg == "--help") {
+ Usage(command, out_error);
+ return false;
+ }
+
+ bool match = false;
+ for (Flag& flag : flags_) {
+ if (arg == flag.name) {
+ if (flag.num_args > 0) {
+ i++;
+ if (i >= args.size()) {
+ *out_error << flag.name << " missing argument.\n\n";
+ Usage(command, out_error);
+ return false;
+ }
+ flag.action(args[i]);
+ } else {
+ flag.action({});
+ }
+ flag.parsed = true;
+ match = true;
+ break;
+ }
+ }
+
+ if (!match) {
+ *out_error << "unknown option '" << arg << "'.\n\n";
+ Usage(command, out_error);
+ return false;
+ }
+ }
+
+ for (const Flag& flag : flags_) {
+ if (flag.required && !flag.parsed) {
+ *out_error << "missing required flag " << flag.name << "\n\n";
+ Usage(command, out_error);
+ return false;
+ }
+ }
+ return true;
+}
+
+const std::vector<std::string>& Flags::GetArgs() { return args_; }
+
+} // namespace aapt
diff --git a/tools/aapt2/Flags.h b/tools/aapt2/Flags.h
index b092855..9feff6b 100644
--- a/tools/aapt2/Flags.h
+++ b/tools/aapt2/Flags.h
@@ -17,54 +17,57 @@
#ifndef AAPT_FLAGS_H
#define AAPT_FLAGS_H
-#include "util/Maybe.h"
-#include "util/StringPiece.h"
-
#include <functional>
#include <ostream>
#include <string>
#include <unordered_set>
#include <vector>
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+
namespace aapt {
class Flags {
-public:
- Flags& requiredFlag(const StringPiece& name, const StringPiece& description,
- std::string* value);
- Flags& requiredFlagList(const StringPiece& name, const StringPiece& description,
- std::vector<std::string>* value);
- Flags& optionalFlag(const StringPiece& name, const StringPiece& description,
- Maybe<std::string>* value);
- Flags& optionalFlagList(const StringPiece& name, const StringPiece& description,
- std::vector<std::string>* value);
- Flags& optionalFlagList(const StringPiece& name, const StringPiece& description,
- std::unordered_set<std::string>* value);
- Flags& optionalSwitch(const StringPiece& name, const StringPiece& description,
- bool* value);
+ public:
+ Flags& RequiredFlag(const StringPiece& name, const StringPiece& description,
+ std::string* value);
+ Flags& RequiredFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::vector<std::string>* value);
+ Flags& OptionalFlag(const StringPiece& name, const StringPiece& description,
+ Maybe<std::string>* value);
+ Flags& OptionalFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::vector<std::string>* value);
+ Flags& OptionalFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::unordered_set<std::string>* value);
+ Flags& OptionalSwitch(const StringPiece& name, const StringPiece& description,
+ bool* value);
- void usage(const StringPiece& command, std::ostream* out);
+ void Usage(const StringPiece& command, std::ostream* out);
- bool parse(const StringPiece& command, const std::vector<StringPiece>& args,
- std::ostream* outError);
+ bool Parse(const StringPiece& command, const std::vector<StringPiece>& args,
+ std::ostream* outError);
- const std::vector<std::string>& getArgs();
+ const std::vector<std::string>& GetArgs();
-private:
- struct Flag {
- std::string name;
- std::string description;
- std::function<bool(const StringPiece& value)> action;
- bool required;
- size_t numArgs;
+ private:
+ struct Flag {
+ std::string name;
+ std::string description;
+ std::function<bool(const StringPiece& value)> action;
+ bool required;
+ size_t num_args;
- bool parsed;
- };
+ bool parsed;
+ };
- std::vector<Flag> mFlags;
- std::vector<std::string> mArgs;
+ std::vector<Flag> flags_;
+ std::vector<std::string> args_;
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_FLAGS_H
+#endif // AAPT_FLAGS_H
diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp
index f7956c0..78f56c7a 100644
--- a/tools/aapt2/Locale.cpp
+++ b/tools/aapt2/Locale.cpp
@@ -15,237 +15,240 @@
*/
#include "Locale.h"
-#include "util/Util.h"
+
+#include <ctype.h>
#include <algorithm>
-#include <ctype.h>
#include <string>
#include <vector>
+#include "util/Util.h"
+
namespace aapt {
using android::ResTable_config;
-void LocaleValue::setLanguage(const char* languageChars) {
- size_t i = 0;
- while ((*languageChars) != '\0') {
- language[i++] = ::tolower(*languageChars);
- languageChars++;
- }
+void LocaleValue::set_language(const char* language_chars) {
+ size_t i = 0;
+ while ((*language_chars) != '\0') {
+ language[i++] = ::tolower(*language_chars);
+ language_chars++;
+ }
}
-void LocaleValue::setRegion(const char* regionChars) {
- size_t i = 0;
- while ((*regionChars) != '\0') {
- region[i++] = ::toupper(*regionChars);
- regionChars++;
- }
+void LocaleValue::set_region(const char* region_chars) {
+ size_t i = 0;
+ while ((*region_chars) != '\0') {
+ region[i++] = ::toupper(*region_chars);
+ region_chars++;
+ }
}
-void LocaleValue::setScript(const char* scriptChars) {
- size_t i = 0;
- while ((*scriptChars) != '\0') {
- if (i == 0) {
- script[i++] = ::toupper(*scriptChars);
- } else {
- script[i++] = ::tolower(*scriptChars);
- }
- scriptChars++;
- }
-}
-
-void LocaleValue::setVariant(const char* variantChars) {
- size_t i = 0;
- while ((*variantChars) != '\0') {
- variant[i++] = *variantChars;
- variantChars++;
- }
-}
-
-static inline bool isAlpha(const std::string& str) {
- return std::all_of(std::begin(str), std::end(str), ::isalpha);
-}
-
-static inline bool isNumber(const std::string& str) {
- return std::all_of(std::begin(str), std::end(str), ::isdigit);
-}
-
-bool LocaleValue::initFromFilterString(const StringPiece& str) {
- // A locale (as specified in the filter) is an underscore separated name such
- // as "en_US", "en_Latn_US", or "en_US_POSIX".
- std::vector<std::string> parts = util::splitAndLowercase(str, '_');
-
- const int numTags = parts.size();
- bool valid = false;
- if (numTags >= 1) {
- const std::string& lang = parts[0];
- if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
- setLanguage(lang.c_str());
- valid = true;
- }
- }
-
- if (!valid || numTags == 1) {
- return valid;
- }
-
- // At this point, valid == true && numTags > 1.
- const std::string& part2 = parts[1];
- if ((part2.length() == 2 && isAlpha(part2)) ||
- (part2.length() == 3 && isNumber(part2))) {
- setRegion(part2.c_str());
- } else if (part2.length() == 4 && isAlpha(part2)) {
- setScript(part2.c_str());
- } else if (part2.length() >= 4 && part2.length() <= 8) {
- setVariant(part2.c_str());
- } else {
- valid = false;
- }
-
- if (!valid || numTags == 2) {
- return valid;
- }
-
- // At this point, valid == true && numTags > 1.
- const std::string& part3 = parts[2];
- if (((part3.length() == 2 && isAlpha(part3)) ||
- (part3.length() == 3 && isNumber(part3))) && script[0]) {
- setRegion(part3.c_str());
- } else if (part3.length() >= 4 && part3.length() <= 8) {
- setVariant(part3.c_str());
- } else {
- valid = false;
- }
-
- if (!valid || numTags == 3) {
- return valid;
- }
-
- const std::string& part4 = parts[3];
- if (part4.length() >= 4 && part4.length() <= 8) {
- setVariant(part4.c_str());
- } else {
- valid = false;
- }
-
- if (!valid || numTags > 4) {
- return false;
- }
-
- return true;
-}
-
-ssize_t LocaleValue::initFromParts(std::vector<std::string>::iterator iter,
- std::vector<std::string>::iterator end) {
- const std::vector<std::string>::iterator startIter = iter;
-
- std::string& part = *iter;
- if (part[0] == 'b' && part[1] == '+') {
- // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
- // except that the separator is "+" and not "-".
- std::vector<std::string> subtags = util::splitAndLowercase(part, '+');
- subtags.erase(subtags.begin());
- if (subtags.size() == 1) {
- setLanguage(subtags[0].c_str());
- } else if (subtags.size() == 2) {
- setLanguage(subtags[0].c_str());
-
- // The second tag can either be a region, a variant or a script.
- switch (subtags[1].size()) {
- case 2:
- case 3:
- setRegion(subtags[1].c_str());
- break;
- case 4:
- if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
- // This is a variant: fall through
- } else {
- setScript(subtags[1].c_str());
- break;
- }
- case 5:
- case 6:
- case 7:
- case 8:
- setVariant(subtags[1].c_str());
- break;
- default:
- return -1;
- }
- } else if (subtags.size() == 3) {
- // The language is always the first subtag.
- setLanguage(subtags[0].c_str());
-
- // The second subtag can either be a script or a region code.
- // If its size is 4, it's a script code, else it's a region code.
- if (subtags[1].size() == 4) {
- setScript(subtags[1].c_str());
- } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
- setRegion(subtags[1].c_str());
- } else {
- return -1;
- }
-
- // The third tag can either be a region code (if the second tag was
- // a script), else a variant code.
- if (subtags[2].size() >= 4) {
- setVariant(subtags[2].c_str());
- } else {
- setRegion(subtags[2].c_str());
- }
- } else if (subtags.size() == 4) {
- setLanguage(subtags[0].c_str());
- setScript(subtags[1].c_str());
- setRegion(subtags[2].c_str());
- setVariant(subtags[3].c_str());
- } else {
- return -1;
- }
-
- ++iter;
-
+void LocaleValue::set_script(const char* script_chars) {
+ size_t i = 0;
+ while ((*script_chars) != '\0') {
+ if (i == 0) {
+ script[i++] = ::toupper(*script_chars);
} else {
- if ((part.length() == 2 || part.length() == 3)
- && isAlpha(part) && part != "car") {
- setLanguage(part.c_str());
- ++iter;
+ script[i++] = ::tolower(*script_chars);
+ }
+ script_chars++;
+ }
+}
- if (iter != end) {
- const std::string& regionPart = *iter;
- if (regionPart.c_str()[0] == 'r' && regionPart.length() == 3) {
- setRegion(regionPart.c_str() + 1);
- ++iter;
- }
- }
+void LocaleValue::set_variant(const char* variant_chars) {
+ size_t i = 0;
+ while ((*variant_chars) != '\0') {
+ variant[i++] = *variant_chars;
+ variant_chars++;
+ }
+}
+
+static inline bool is_alpha(const std::string& str) {
+ return std::all_of(std::begin(str), std::end(str), ::isalpha);
+}
+
+static inline bool is_number(const std::string& str) {
+ return std::all_of(std::begin(str), std::end(str), ::isdigit);
+}
+
+bool LocaleValue::InitFromFilterString(const StringPiece& str) {
+ // A locale (as specified in the filter) is an underscore separated name such
+ // as "en_US", "en_Latn_US", or "en_US_POSIX".
+ std::vector<std::string> parts = util::SplitAndLowercase(str, '_');
+
+ const int num_tags = parts.size();
+ bool valid = false;
+ if (num_tags >= 1) {
+ const std::string& lang = parts[0];
+ if (is_alpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
+ set_language(lang.c_str());
+ valid = true;
+ }
+ }
+
+ if (!valid || num_tags == 1) {
+ return valid;
+ }
+
+ // At this point, valid == true && numTags > 1.
+ const std::string& part2 = parts[1];
+ if ((part2.length() == 2 && is_alpha(part2)) ||
+ (part2.length() == 3 && is_number(part2))) {
+ set_region(part2.c_str());
+ } else if (part2.length() == 4 && is_alpha(part2)) {
+ set_script(part2.c_str());
+ } else if (part2.length() >= 4 && part2.length() <= 8) {
+ set_variant(part2.c_str());
+ } else {
+ valid = false;
+ }
+
+ if (!valid || num_tags == 2) {
+ return valid;
+ }
+
+ // At this point, valid == true && numTags > 1.
+ const std::string& part3 = parts[2];
+ if (((part3.length() == 2 && is_alpha(part3)) ||
+ (part3.length() == 3 && is_number(part3))) &&
+ script[0]) {
+ set_region(part3.c_str());
+ } else if (part3.length() >= 4 && part3.length() <= 8) {
+ set_variant(part3.c_str());
+ } else {
+ valid = false;
+ }
+
+ if (!valid || num_tags == 3) {
+ return valid;
+ }
+
+ const std::string& part4 = parts[3];
+ if (part4.length() >= 4 && part4.length() <= 8) {
+ set_variant(part4.c_str());
+ } else {
+ valid = false;
+ }
+
+ if (!valid || num_tags > 4) {
+ return false;
+ }
+
+ return true;
+}
+
+ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter,
+ std::vector<std::string>::iterator end) {
+ const std::vector<std::string>::iterator start_iter = iter;
+
+ std::string& part = *iter;
+ if (part[0] == 'b' && part[1] == '+') {
+ // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
+ // except that the separator is "+" and not "-".
+ std::vector<std::string> subtags = util::SplitAndLowercase(part, '+');
+ subtags.erase(subtags.begin());
+ if (subtags.size() == 1) {
+ set_language(subtags[0].c_str());
+ } else if (subtags.size() == 2) {
+ set_language(subtags[0].c_str());
+
+ // The second tag can either be a region, a variant or a script.
+ switch (subtags[1].size()) {
+ case 2:
+ case 3:
+ set_region(subtags[1].c_str());
+ break;
+ case 4:
+ if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
+ // This is a variant: fall through
+ } else {
+ set_script(subtags[1].c_str());
+ break;
+ }
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ set_variant(subtags[1].c_str());
+ break;
+ default:
+ return -1;
+ }
+ } else if (subtags.size() == 3) {
+ // The language is always the first subtag.
+ set_language(subtags[0].c_str());
+
+ // The second subtag can either be a script or a region code.
+ // If its size is 4, it's a script code, else it's a region code.
+ if (subtags[1].size() == 4) {
+ set_script(subtags[1].c_str());
+ } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
+ set_region(subtags[1].c_str());
+ } else {
+ return -1;
+ }
+
+ // The third tag can either be a region code (if the second tag was
+ // a script), else a variant code.
+ if (subtags[2].size() >= 4) {
+ set_variant(subtags[2].c_str());
+ } else {
+ set_region(subtags[2].c_str());
+ }
+ } else if (subtags.size() == 4) {
+ set_language(subtags[0].c_str());
+ set_script(subtags[1].c_str());
+ set_region(subtags[2].c_str());
+ set_variant(subtags[3].c_str());
+ } else {
+ return -1;
+ }
+
+ ++iter;
+
+ } else {
+ if ((part.length() == 2 || part.length() == 3) && is_alpha(part) &&
+ part != "car") {
+ set_language(part.c_str());
+ ++iter;
+
+ if (iter != end) {
+ const std::string& region_part = *iter;
+ if (region_part.c_str()[0] == 'r' && region_part.length() == 3) {
+ set_region(region_part.c_str() + 1);
+ ++iter;
}
+ }
}
+ }
- return static_cast<ssize_t>(iter - startIter);
+ return static_cast<ssize_t>(iter - start_iter);
}
-void LocaleValue::initFromResTable(const ResTable_config& config) {
- config.unpackLanguage(language);
- config.unpackRegion(region);
- if (config.localeScript[0] && !config.localeScriptWasComputed) {
- memcpy(script, config.localeScript, sizeof(config.localeScript));
- }
+void LocaleValue::InitFromResTable(const ResTable_config& config) {
+ config.unpackLanguage(language);
+ config.unpackRegion(region);
+ if (config.localeScript[0] && !config.localeScriptWasComputed) {
+ memcpy(script, config.localeScript, sizeof(config.localeScript));
+ }
- if (config.localeVariant[0]) {
- memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
- }
+ if (config.localeVariant[0]) {
+ memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
+ }
}
-void LocaleValue::writeTo(ResTable_config* out) const {
- out->packLanguage(language);
- out->packRegion(region);
+void LocaleValue::WriteTo(ResTable_config* out) const {
+ out->packLanguage(language);
+ out->packRegion(region);
- if (script[0]) {
- memcpy(out->localeScript, script, sizeof(out->localeScript));
- }
+ if (script[0]) {
+ memcpy(out->localeScript, script, sizeof(out->localeScript));
+ }
- if (variant[0]) {
- memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
- }
+ if (variant[0]) {
+ memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
+ }
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Locale.h b/tools/aapt2/Locale.h
index 33f80ad..fc6c448 100644
--- a/tools/aapt2/Locale.h
+++ b/tools/aapt2/Locale.h
@@ -17,98 +17,97 @@
#ifndef AAPT_LOCALE_VALUE_H
#define AAPT_LOCALE_VALUE_H
-#include "util/StringPiece.h"
-
-#include <androidfw/ResourceTypes.h>
#include <string>
#include <vector>
+#include "androidfw/ResourceTypes.h"
+
+#include "util/StringPiece.h"
+
namespace aapt {
/**
* A convenience class to build and parse locales.
*/
struct LocaleValue {
- char language[4];
- char region[4];
- char script[4];
- char variant[8];
+ char language[4];
+ char region[4];
+ char script[4];
+ char variant[8];
- inline LocaleValue();
+ inline LocaleValue();
- /**
- * Initialize this LocaleValue from a config string.
- */
- bool initFromFilterString(const StringPiece& config);
+ /**
+ * Initialize this LocaleValue from a config string.
+ */
+ bool InitFromFilterString(const StringPiece& config);
- /**
- * Initialize this LocaleValue from parts of a vector.
- */
- ssize_t initFromParts(std::vector<std::string>::iterator iter,
- std::vector<std::string>::iterator end);
+ /**
+ * Initialize this LocaleValue from parts of a vector.
+ */
+ ssize_t InitFromParts(std::vector<std::string>::iterator iter,
+ std::vector<std::string>::iterator end);
- /**
- * Initialize this LocaleValue from a ResTable_config.
- */
- void initFromResTable(const android::ResTable_config& config);
+ /**
+ * Initialize this LocaleValue from a ResTable_config.
+ */
+ void InitFromResTable(const android::ResTable_config& config);
- /**
- * Set the locale in a ResTable_config from this LocaleValue.
- */
- void writeTo(android::ResTable_config* out) const;
+ /**
+ * Set the locale in a ResTable_config from this LocaleValue.
+ */
+ void WriteTo(android::ResTable_config* out) const;
- inline int compare(const LocaleValue& other) const;
+ inline int compare(const LocaleValue& other) const;
- inline bool operator<(const LocaleValue& o) const;
- inline bool operator<=(const LocaleValue& o) const;
- inline bool operator==(const LocaleValue& o) const;
- inline bool operator!=(const LocaleValue& o) const;
- inline bool operator>=(const LocaleValue& o) const;
- inline bool operator>(const LocaleValue& o) const;
+ inline bool operator<(const LocaleValue& o) const;
+ inline bool operator<=(const LocaleValue& o) const;
+ inline bool operator==(const LocaleValue& o) const;
+ inline bool operator!=(const LocaleValue& o) const;
+ inline bool operator>=(const LocaleValue& o) const;
+ inline bool operator>(const LocaleValue& o) const;
-private:
- void setLanguage(const char* language);
- void setRegion(const char* language);
- void setScript(const char* script);
- void setVariant(const char* variant);
+ private:
+ void set_language(const char* language);
+ void set_region(const char* language);
+ void set_script(const char* script);
+ void set_variant(const char* variant);
};
//
// Implementation
//
-LocaleValue::LocaleValue() {
- memset(this, 0, sizeof(LocaleValue));
-}
+LocaleValue::LocaleValue() { memset(this, 0, sizeof(LocaleValue)); }
int LocaleValue::compare(const LocaleValue& other) const {
- return memcmp(this, &other, sizeof(LocaleValue));
+ return memcmp(this, &other, sizeof(LocaleValue));
}
bool LocaleValue::operator<(const LocaleValue& o) const {
- return compare(o) < 0;
+ return compare(o) < 0;
}
bool LocaleValue::operator<=(const LocaleValue& o) const {
- return compare(o) <= 0;
+ return compare(o) <= 0;
}
bool LocaleValue::operator==(const LocaleValue& o) const {
- return compare(o) == 0;
+ return compare(o) == 0;
}
bool LocaleValue::operator!=(const LocaleValue& o) const {
- return compare(o) != 0;
+ return compare(o) != 0;
}
bool LocaleValue::operator>=(const LocaleValue& o) const {
- return compare(o) >= 0;
+ return compare(o) >= 0;
}
bool LocaleValue::operator>(const LocaleValue& o) const {
- return compare(o) > 0;
+ return compare(o) > 0;
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_LOCALE_VALUE_H
+#endif // AAPT_LOCALE_VALUE_H
diff --git a/tools/aapt2/Locale_test.cpp b/tools/aapt2/Locale_test.cpp
index e4b8ce7..68b4cae 100644
--- a/tools/aapt2/Locale_test.cpp
+++ b/tools/aapt2/Locale_test.cpp
@@ -15,68 +15,82 @@
*/
#include "Locale.h"
-#include "util/Util.h"
-#include <gtest/gtest.h>
#include <string>
+#include "gtest/gtest.h"
+
+#include "util/Util.h"
+
namespace aapt {
-static ::testing::AssertionResult TestLanguage(const char* input, const char* lang) {
- std::vector<std::string> parts = util::splitAndLowercase(input, '-');
- LocaleValue lv;
- ssize_t count = lv.initFromParts(std::begin(parts), std::end(parts));
- if (count < 0) {
- return ::testing::AssertionFailure() << " failed to parse '" << input << "'.";
- }
+static ::testing::AssertionResult TestLanguage(const char* input,
+ const char* lang) {
+ std::vector<std::string> parts = util::SplitAndLowercase(input, '-');
+ LocaleValue lv;
+ ssize_t count = lv.InitFromParts(std::begin(parts), std::end(parts));
+ if (count < 0) {
+ return ::testing::AssertionFailure() << " failed to parse '" << input
+ << "'.";
+ }
- if (count != 1) {
- return ::testing::AssertionFailure() << count
- << " parts were consumed parsing '" << input << "' but expected 1.";
- }
+ if (count != 1) {
+ return ::testing::AssertionFailure()
+ << count << " parts were consumed parsing '" << input
+ << "' but expected 1.";
+ }
- if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) != 0) {
- return ::testing::AssertionFailure() << "expected " << lang << " but got "
- << std::string(lv.language, sizeof(lv.language)) << ".";
- }
+ if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) !=
+ 0) {
+ return ::testing::AssertionFailure()
+ << "expected " << lang << " but got "
+ << std::string(lv.language, sizeof(lv.language)) << ".";
+ }
- return ::testing::AssertionSuccess();
+ return ::testing::AssertionSuccess();
}
-static ::testing::AssertionResult TestLanguageRegion(const char* input, const char* lang,
+static ::testing::AssertionResult TestLanguageRegion(const char* input,
+ const char* lang,
const char* region) {
- std::vector<std::string> parts = util::splitAndLowercase(input, '-');
- LocaleValue lv;
- ssize_t count = lv.initFromParts(std::begin(parts), std::end(parts));
- if (count < 0) {
- return ::testing::AssertionFailure() << " failed to parse '" << input << "'.";
- }
+ std::vector<std::string> parts = util::SplitAndLowercase(input, '-');
+ LocaleValue lv;
+ ssize_t count = lv.InitFromParts(std::begin(parts), std::end(parts));
+ if (count < 0) {
+ return ::testing::AssertionFailure() << " failed to parse '" << input
+ << "'.";
+ }
- if (count != 2) {
- return ::testing::AssertionFailure() << count
- << " parts were consumed parsing '" << input << "' but expected 2.";
- }
+ if (count != 2) {
+ return ::testing::AssertionFailure()
+ << count << " parts were consumed parsing '" << input
+ << "' but expected 2.";
+ }
- if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) != 0) {
- return ::testing::AssertionFailure() << "expected " << input << " but got "
- << std::string(lv.language, sizeof(lv.language)) << ".";
- }
+ if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) !=
+ 0) {
+ return ::testing::AssertionFailure()
+ << "expected " << input << " but got "
+ << std::string(lv.language, sizeof(lv.language)) << ".";
+ }
- if (memcmp(lv.region, region, std::min(strlen(region), sizeof(lv.region))) != 0) {
- return ::testing::AssertionFailure() << "expected " << region << " but got "
- << std::string(lv.region, sizeof(lv.region)) << ".";
- }
+ if (memcmp(lv.region, region, std::min(strlen(region), sizeof(lv.region))) !=
+ 0) {
+ return ::testing::AssertionFailure()
+ << "expected " << region << " but got "
+ << std::string(lv.region, sizeof(lv.region)) << ".";
+ }
- return ::testing::AssertionSuccess();
+ return ::testing::AssertionSuccess();
}
TEST(ConfigDescriptionTest, ParseLanguage) {
- EXPECT_TRUE(TestLanguage("en", "en"));
- EXPECT_TRUE(TestLanguage("fr", "fr"));
- EXPECT_FALSE(TestLanguage("land", ""));
- EXPECT_TRUE(TestLanguage("fr-land", "fr"));
+ EXPECT_TRUE(TestLanguage("en", "en"));
+ EXPECT_TRUE(TestLanguage("fr", "fr"));
+ EXPECT_FALSE(TestLanguage("land", ""));
+ EXPECT_TRUE(TestLanguage("fr-land", "fr"));
- EXPECT_TRUE(TestLanguageRegion("fr-rCA", "fr", "CA"));
+ EXPECT_TRUE(TestLanguageRegion("fr-rCA", "fr", "CA"));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index f3f70d6..8dd176d 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-#include "util/StringPiece.h"
-
#include <iostream>
#include <vector>
+#include "util/StringPiece.h"
+
namespace aapt {
// DO NOT UPDATE, this is more of a marketing version.
@@ -27,46 +27,47 @@
// Update minor version whenever a feature or flag is added.
static const char* sMinorVersion = "2";
-int printVersion() {
- std::cerr << "Android Asset Packaging Tool (aapt) "
- << sMajorVersion << "." << sMinorVersion << std::endl;
- return 0;
+int PrintVersion() {
+ std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
+ << sMinorVersion << std::endl;
+ return 0;
}
-extern int compile(const std::vector<StringPiece>& args);
-extern int link(const std::vector<StringPiece>& args);
-extern int dump(const std::vector<StringPiece>& args);
-extern int diff(const std::vector<StringPiece>& args);
+extern int Compile(const std::vector<StringPiece>& args);
+extern int Link(const std::vector<StringPiece>& args);
+extern int Dump(const std::vector<StringPiece>& args);
+extern int Diff(const std::vector<StringPiece>& args);
-} // namespace aapt
+} // namespace aapt
int main(int argc, char** argv) {
- if (argc >= 2) {
- argv += 1;
- argc -= 1;
+ if (argc >= 2) {
+ argv += 1;
+ argc -= 1;
- std::vector<aapt::StringPiece> args;
- for (int i = 1; i < argc; i++) {
- args.push_back(argv[i]);
- }
-
- aapt::StringPiece command(argv[0]);
- if (command == "compile" || command == "c") {
- return aapt::compile(args);
- } else if (command == "link" || command == "l") {
- return aapt::link(args);
- } else if (command == "dump" || command == "d") {
- return aapt::dump(args);
- } else if (command == "diff") {
- return aapt::diff(args);
- } else if (command == "version") {
- return aapt::printVersion();
- }
- std::cerr << "unknown command '" << command << "'\n";
- } else {
- std::cerr << "no command specified\n";
+ std::vector<aapt::StringPiece> args;
+ for (int i = 1; i < argc; i++) {
+ args.push_back(argv[i]);
}
- std::cerr << "\nusage: aapt2 [compile|link|dump|diff|version] ..." << std::endl;
- return 1;
+ aapt::StringPiece command(argv[0]);
+ if (command == "compile" || command == "c") {
+ return aapt::Compile(args);
+ } else if (command == "link" || command == "l") {
+ return aapt::Link(args);
+ } else if (command == "dump" || command == "d") {
+ return aapt::Dump(args);
+ } else if (command == "diff") {
+ return aapt::Diff(args);
+ } else if (command == "version") {
+ return aapt::PrintVersion();
+ }
+ std::cerr << "unknown command '" << command << "'\n";
+ } else {
+ std::cerr << "no command specified\n";
+ }
+
+ std::cerr << "\nusage: aapt2 [compile|link|dump|diff|version] ..."
+ << std::endl;
+ return 1;
}
diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h
index b6aaa4d..dba2d09 100644
--- a/tools/aapt2/NameMangler.h
+++ b/tools/aapt2/NameMangler.h
@@ -17,78 +17,82 @@
#ifndef AAPT_NAME_MANGLER_H
#define AAPT_NAME_MANGLER_H
-#include "Resource.h"
-#include "util/Maybe.h"
-
#include <set>
#include <string>
+#include "Resource.h"
+#include "util/Maybe.h"
+
namespace aapt {
struct NameManglerPolicy {
- /**
- * Represents the package we are trying to build. References pointing
- * to this package are not mangled, and mangled references inherit this package name.
- */
- std::string targetPackageName;
+ /**
+ * Represents the package we are trying to build. References pointing
+ * to this package are not mangled, and mangled references inherit this
+ * package name.
+ */
+ std::string target_package_name;
- /**
- * We must know which references to mangle, and which to keep (android vs. com.android.support).
- */
- std::set<std::string> packagesToMangle;
+ /**
+ * We must know which references to mangle, and which to keep (android vs.
+ * com.android.support).
+ */
+ std::set<std::string> packages_to_mangle;
};
class NameMangler {
-private:
- NameManglerPolicy mPolicy;
+ public:
+ explicit NameMangler(NameManglerPolicy policy) : policy_(policy) {}
-public:
- explicit NameMangler(NameManglerPolicy policy) : mPolicy(policy) {
+ Maybe<ResourceName> MangleName(const ResourceName& name) {
+ if (policy_.target_package_name == name.package ||
+ policy_.packages_to_mangle.count(name.package) == 0) {
+ return {};
}
- Maybe<ResourceName> mangleName(const ResourceName& name) {
- if (mPolicy.targetPackageName == name.package ||
- mPolicy.packagesToMangle.count(name.package) == 0) {
- return {};
- }
+ std::string mangled_entry_name = MangleEntry(name.package, name.entry);
+ return ResourceName(policy_.target_package_name, name.type,
+ mangled_entry_name);
+ }
- std::string mangledEntryName = mangleEntry(name.package, name.entry);
- return ResourceName(mPolicy.targetPackageName, name.type, mangledEntryName);
+ bool ShouldMangle(const std::string& package) const {
+ if (package.empty() || policy_.target_package_name == package) {
+ return false;
+ }
+ return policy_.packages_to_mangle.count(package) != 0;
+ }
+
+ /**
+ * Returns a mangled name that is a combination of `name` and `package`.
+ * The mangled name should contain symbols that are illegal to define in XML,
+ * so that there will never be name mangling collisions.
+ */
+ static std::string MangleEntry(const std::string& package,
+ const std::string& name) {
+ return package + "$" + name;
+ }
+
+ /**
+ * Unmangles the name in `outName`, storing the correct name back in `outName`
+ * and the package in `outPackage`. Returns true if the name was unmangled or
+ * false if the name was never mangled to begin with.
+ */
+ static bool Unmangle(std::string* out_name, std::string* out_package) {
+ size_t pivot = out_name->find('$');
+ if (pivot == std::string::npos) {
+ return false;
}
- bool shouldMangle(const std::string& package) const {
- if (package.empty() || mPolicy.targetPackageName == package) {
- return false;
- }
- return mPolicy.packagesToMangle.count(package) != 0;
- }
+ out_package->assign(out_name->data(), pivot);
+ out_name->assign(out_name->data() + pivot + 1,
+ out_name->size() - (pivot + 1));
+ return true;
+ }
- /**
- * Returns a mangled name that is a combination of `name` and `package`.
- * The mangled name should contain symbols that are illegal to define in XML,
- * so that there will never be name mangling collisions.
- */
- static std::string mangleEntry(const std::string& package, const std::string& name) {
- return package + "$" + name;
- }
-
- /**
- * Unmangles the name in `outName`, storing the correct name back in `outName`
- * and the package in `outPackage`. Returns true if the name was unmangled or
- * false if the name was never mangled to begin with.
- */
- static bool unmangle(std::string* outName, std::string* outPackage) {
- size_t pivot = outName->find('$');
- if (pivot == std::string::npos) {
- return false;
- }
-
- outPackage->assign(outName->data(), pivot);
- outName->assign(outName->data() + pivot + 1, outName->size() - (pivot + 1));
- return true;
- }
+ private:
+ NameManglerPolicy policy_;
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_NAME_MANGLER_H
+#endif // AAPT_NAME_MANGLER_H
diff --git a/tools/aapt2/NameMangler_test.cpp b/tools/aapt2/NameMangler_test.cpp
index f624df2..bc89b5c 100644
--- a/tools/aapt2/NameMangler_test.cpp
+++ b/tools/aapt2/NameMangler_test.cpp
@@ -15,32 +15,33 @@
*/
#include "NameMangler.h"
-#include "test/Test.h"
#include <string>
+#include "test/Test.h"
+
namespace aapt {
TEST(NameManglerTest, MangleName) {
- std::string package = "android.appcompat";
- std::string name = "Platform.AppCompat";
+ std::string package = "android.appcompat";
+ std::string name = "Platform.AppCompat";
- std::string mangledName = NameMangler::mangleEntry(package, name);
- EXPECT_EQ(mangledName, "android.appcompat$Platform.AppCompat");
+ std::string mangled_name = NameMangler::MangleEntry(package, name);
+ EXPECT_EQ(mangled_name, "android.appcompat$Platform.AppCompat");
- std::string unmangledPackage;
- std::string unmangledName = mangledName;
- ASSERT_TRUE(NameMangler::unmangle(&unmangledName, &unmangledPackage));
- EXPECT_EQ(unmangledName, "Platform.AppCompat");
- EXPECT_EQ(unmangledPackage, "android.appcompat");
+ std::string unmangled_package;
+ std::string unmangled_name = mangled_name;
+ ASSERT_TRUE(NameMangler::Unmangle(&unmangled_name, &unmangled_package));
+ EXPECT_EQ(unmangled_name, "Platform.AppCompat");
+ EXPECT_EQ(unmangled_package, "android.appcompat");
}
TEST(NameManglerTest, IgnoreUnmangledName) {
- std::string package;
- std::string name = "foo_bar";
+ std::string package;
+ std::string name = "foo_bar";
- EXPECT_FALSE(NameMangler::unmangle(&name, &package));
- EXPECT_EQ(name, "foo_bar");
+ EXPECT_FALSE(NameMangler::Unmangle(&name, &package));
+ EXPECT_EQ(name, "foo_bar");
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index b7a091e..1d414743 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -15,82 +15,104 @@
*/
#include "Resource.h"
-#include "util/StringPiece.h"
#include <map>
#include <string>
namespace aapt {
-StringPiece toString(ResourceType type) {
- switch (type) {
- case ResourceType::kAnim: return "anim";
- case ResourceType::kAnimator: return "animator";
- case ResourceType::kArray: return "array";
- case ResourceType::kAttr: return "attr";
- case ResourceType::kAttrPrivate: return "^attr-private";
- case ResourceType::kBool: return "bool";
- case ResourceType::kColor: return "color";
- case ResourceType::kDimen: return "dimen";
- case ResourceType::kDrawable: return "drawable";
- case ResourceType::kFraction: return "fraction";
- case ResourceType::kId: return "id";
- case ResourceType::kInteger: return "integer";
- case ResourceType::kInterpolator: return "interpolator";
- case ResourceType::kLayout: return "layout";
- case ResourceType::kMenu: return "menu";
- case ResourceType::kMipmap: return "mipmap";
- case ResourceType::kPlurals: return "plurals";
- case ResourceType::kRaw: return "raw";
- case ResourceType::kString: return "string";
- case ResourceType::kStyle: return "style";
- case ResourceType::kStyleable: return "styleable";
- case ResourceType::kTransition: return "transition";
- case ResourceType::kXml: return "xml";
- }
- return {};
+StringPiece ToString(ResourceType type) {
+ switch (type) {
+ case ResourceType::kAnim:
+ return "anim";
+ case ResourceType::kAnimator:
+ return "animator";
+ case ResourceType::kArray:
+ return "array";
+ case ResourceType::kAttr:
+ return "attr";
+ case ResourceType::kAttrPrivate:
+ return "^attr-private";
+ case ResourceType::kBool:
+ return "bool";
+ case ResourceType::kColor:
+ return "color";
+ case ResourceType::kDimen:
+ return "dimen";
+ case ResourceType::kDrawable:
+ return "drawable";
+ case ResourceType::kFraction:
+ return "fraction";
+ case ResourceType::kId:
+ return "id";
+ case ResourceType::kInteger:
+ return "integer";
+ case ResourceType::kInterpolator:
+ return "interpolator";
+ case ResourceType::kLayout:
+ return "layout";
+ case ResourceType::kMenu:
+ return "menu";
+ case ResourceType::kMipmap:
+ return "mipmap";
+ case ResourceType::kPlurals:
+ return "plurals";
+ case ResourceType::kRaw:
+ return "raw";
+ case ResourceType::kString:
+ return "string";
+ case ResourceType::kStyle:
+ return "style";
+ case ResourceType::kStyleable:
+ return "styleable";
+ case ResourceType::kTransition:
+ return "transition";
+ case ResourceType::kXml:
+ return "xml";
+ }
+ return {};
}
-static const std::map<StringPiece, ResourceType> sResourceTypeMap {
- { "anim", ResourceType::kAnim },
- { "animator", ResourceType::kAnimator },
- { "array", ResourceType::kArray },
- { "attr", ResourceType::kAttr },
- { "^attr-private", ResourceType::kAttrPrivate },
- { "bool", ResourceType::kBool },
- { "color", ResourceType::kColor },
- { "dimen", ResourceType::kDimen },
- { "drawable", ResourceType::kDrawable },
- { "fraction", ResourceType::kFraction },
- { "id", ResourceType::kId },
- { "integer", ResourceType::kInteger },
- { "interpolator", ResourceType::kInterpolator },
- { "layout", ResourceType::kLayout },
- { "menu", ResourceType::kMenu },
- { "mipmap", ResourceType::kMipmap },
- { "plurals", ResourceType::kPlurals },
- { "raw", ResourceType::kRaw },
- { "string", ResourceType::kString },
- { "style", ResourceType::kStyle },
- { "styleable", ResourceType::kStyleable },
- { "transition", ResourceType::kTransition },
- { "xml", ResourceType::kXml },
+static const std::map<StringPiece, ResourceType> sResourceTypeMap{
+ {"anim", ResourceType::kAnim},
+ {"animator", ResourceType::kAnimator},
+ {"array", ResourceType::kArray},
+ {"attr", ResourceType::kAttr},
+ {"^attr-private", ResourceType::kAttrPrivate},
+ {"bool", ResourceType::kBool},
+ {"color", ResourceType::kColor},
+ {"dimen", ResourceType::kDimen},
+ {"drawable", ResourceType::kDrawable},
+ {"fraction", ResourceType::kFraction},
+ {"id", ResourceType::kId},
+ {"integer", ResourceType::kInteger},
+ {"interpolator", ResourceType::kInterpolator},
+ {"layout", ResourceType::kLayout},
+ {"menu", ResourceType::kMenu},
+ {"mipmap", ResourceType::kMipmap},
+ {"plurals", ResourceType::kPlurals},
+ {"raw", ResourceType::kRaw},
+ {"string", ResourceType::kString},
+ {"style", ResourceType::kStyle},
+ {"styleable", ResourceType::kStyleable},
+ {"transition", ResourceType::kTransition},
+ {"xml", ResourceType::kXml},
};
-const ResourceType* parseResourceType(const StringPiece& str) {
- auto iter = sResourceTypeMap.find(str);
- if (iter == std::end(sResourceTypeMap)) {
- return nullptr;
- }
- return &iter->second;
+const ResourceType* ParseResourceType(const StringPiece& str) {
+ auto iter = sResourceTypeMap.find(str);
+ if (iter == std::end(sResourceTypeMap)) {
+ return nullptr;
+ }
+ return &iter->second;
}
bool operator<(const ResourceKey& a, const ResourceKey& b) {
- return std::tie(a.name, a.config) < std::tie(b.name, b.config);
+ return std::tie(a.name, a.config) < std::tie(b.name, b.config);
}
bool operator<(const ResourceKeyRef& a, const ResourceKeyRef& b) {
- return std::tie(a.name, a.config) < std::tie(b.name, b.config);
+ return std::tie(a.name, a.config) < std::tie(b.name, b.config);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 2969b8c..78acb70 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -17,12 +17,6 @@
#ifndef AAPT_RESOURCE_H
#define AAPT_RESOURCE_H
-#include "ConfigDescription.h"
-#include "Source.h"
-#include "util/StringPiece.h"
-
-#include <utils/JenkinsHash.h>
-
#include <iomanip>
#include <limits>
#include <sstream>
@@ -30,6 +24,12 @@
#include <tuple>
#include <vector>
+#include "utils/JenkinsHash.h"
+
+#include "ConfigDescription.h"
+#include "Source.h"
+#include "util/StringPiece.h"
+
namespace aapt {
/**
@@ -37,55 +37,55 @@
* to the 'type' in package:type/entry.
*/
enum class ResourceType {
- kAnim,
- kAnimator,
- kArray,
- kAttr,
- kAttrPrivate,
- kBool,
- kColor,
- kDimen,
- kDrawable,
- kFraction,
- kId,
- kInteger,
- kInterpolator,
- kLayout,
- kMenu,
- kMipmap,
- kPlurals,
- kRaw,
- kString,
- kStyle,
- kStyleable,
- kTransition,
- kXml,
+ kAnim,
+ kAnimator,
+ kArray,
+ kAttr,
+ kAttrPrivate,
+ kBool,
+ kColor,
+ kDimen,
+ kDrawable,
+ kFraction,
+ kId,
+ kInteger,
+ kInterpolator,
+ kLayout,
+ kMenu,
+ kMipmap,
+ kPlurals,
+ kRaw,
+ kString,
+ kStyle,
+ kStyleable,
+ kTransition,
+ kXml,
};
-StringPiece toString(ResourceType type);
+StringPiece ToString(ResourceType type);
/**
* Returns a pointer to a valid ResourceType, or nullptr if
* the string was invalid.
*/
-const ResourceType* parseResourceType(const StringPiece& str);
+const ResourceType* ParseResourceType(const StringPiece& str);
/**
* A resource's name. This can uniquely identify
* a resource in the ResourceTable.
*/
struct ResourceName {
- std::string package;
- ResourceType type;
- std::string entry;
+ std::string package;
+ ResourceType type = ResourceType::kRaw;
+ std::string entry;
- ResourceName() : type(ResourceType::kRaw) {}
- ResourceName(const StringPiece& p, ResourceType t, const StringPiece& e);
+ ResourceName() = default;
+ ResourceName(const StringPiece& p, ResourceType t, const StringPiece& e);
- int compare(const ResourceName& other) const;
+ int compare(const ResourceName& other) const;
- bool isValid() const;
- std::string toString() const;
+ bool is_valid() const;
+ std::string ToString() const;
};
/**
@@ -95,21 +95,21 @@
* of the original string.
*/
struct ResourceNameRef {
- StringPiece package;
- ResourceType type;
- StringPiece entry;
+ StringPiece package;
+ ResourceType type = ResourceType::kRaw;
+ StringPiece entry;
- ResourceNameRef() = default;
- ResourceNameRef(const ResourceNameRef&) = default;
- ResourceNameRef(ResourceNameRef&&) = default;
- ResourceNameRef(const ResourceName& rhs); // NOLINT(implicit)
- ResourceNameRef(const StringPiece& p, ResourceType t, const StringPiece& e);
- ResourceNameRef& operator=(const ResourceNameRef& rhs) = default;
- ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
- ResourceNameRef& operator=(const ResourceName& rhs);
+ ResourceNameRef() = default;
+ ResourceNameRef(const ResourceNameRef&) = default;
+ ResourceNameRef(ResourceNameRef&&) = default;
+ ResourceNameRef(const ResourceName& rhs); // NOLINT(implicit)
+ ResourceNameRef(const StringPiece& p, ResourceType t, const StringPiece& e);
+ ResourceNameRef& operator=(const ResourceNameRef& rhs) = default;
+ ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
+ ResourceNameRef& operator=(const ResourceName& rhs);
- ResourceName toResourceName() const;
- bool isValid() const;
+ ResourceName ToResourceName() const;
+ bool is_valid() const;
};
/**
@@ -124,64 +124,66 @@
* EEEE: 16 bit entry identifier.
*/
struct ResourceId {
- uint32_t id;
+ uint32_t id;
- ResourceId();
- ResourceId(const ResourceId& rhs);
- ResourceId(uint32_t resId); // NOLINT(implicit)
- ResourceId(uint8_t p, uint8_t t, uint16_t e);
+ ResourceId();
+ ResourceId(const ResourceId& rhs);
+ ResourceId(uint32_t res_id); // NOLINT(implicit)
+ ResourceId(uint8_t p, uint8_t t, uint16_t e);
- bool isValid() const;
- uint8_t packageId() const;
- uint8_t typeId() const;
- uint16_t entryId() const;
+ bool is_valid() const;
+ uint8_t package_id() const;
+ uint8_t type_id() const;
+ uint16_t entry_id() const;
};
struct SourcedResourceName {
- ResourceName name;
- size_t line;
+ ResourceName name;
+ size_t line;
};
struct ResourceFile {
- // Name
- ResourceName name;
+ // Name
+ ResourceName name;
- // Configuration
- ConfigDescription config;
+ // Configuration
+ ConfigDescription config;
- // Source
- Source source;
+ // Source
+ Source source;
- // Exported symbols
- std::vector<SourcedResourceName> exportedSymbols;
+ // Exported symbols
+ std::vector<SourcedResourceName> exported_symbols;
};
/**
- * Useful struct used as a key to represent a unique resource in associative containers.
+ * Useful struct used as a key to represent a unique resource in associative
+ * containers.
*/
struct ResourceKey {
- ResourceName name;
- ConfigDescription config;
+ ResourceName name;
+ ConfigDescription config;
};
bool operator<(const ResourceKey& a, const ResourceKey& b);
/**
- * Useful struct used as a key to represent a unique resource in associative containers.
+ * Useful struct used as a key to represent a unique resource in associative
+ * containers.
* Holds a reference to the name, so that name better live longer than this key!
*/
struct ResourceKeyRef {
- ResourceNameRef name;
- ConfigDescription config;
+ ResourceNameRef name;
+ ConfigDescription config;
- ResourceKeyRef() = default;
- ResourceKeyRef(const ResourceNameRef& n, const ConfigDescription& c) : name(n), config(c) {
- }
+ ResourceKeyRef() = default;
+ ResourceKeyRef(const ResourceNameRef& n, const ConfigDescription& c)
+ : name(n), config(c) {}
- /**
- * Prevent taking a reference to a temporary. This is bad.
- */
- ResourceKeyRef(ResourceName&& n, const ConfigDescription& c) = delete;
+ /**
+ * Prevent taking a reference to a temporary. This is bad.
+ */
+ ResourceKeyRef(ResourceName&& n, const ConfigDescription& c) = delete;
};
bool operator<(const ResourceKeyRef& a, const ResourceKeyRef& b);
@@ -190,193 +192,194 @@
// ResourceId implementation.
//
-inline ResourceId::ResourceId() : id(0) {
+inline ResourceId::ResourceId() : id(0) {}
+
+inline ResourceId::ResourceId(const ResourceId& rhs) : id(rhs.id) {}
+
+inline ResourceId::ResourceId(uint32_t res_id) : id(res_id) {}
+
+inline ResourceId::ResourceId(uint8_t p, uint8_t t, uint16_t e)
+ : id((p << 24) | (t << 16) | e) {}
+
+inline bool ResourceId::is_valid() const {
+ return (id & 0xff000000u) != 0 && (id & 0x00ff0000u) != 0;
}
-inline ResourceId::ResourceId(const ResourceId& rhs) : id(rhs.id) {
+inline uint8_t ResourceId::package_id() const {
+ return static_cast<uint8_t>(id >> 24);
}
-inline ResourceId::ResourceId(uint32_t resId) : id(resId) {
+inline uint8_t ResourceId::type_id() const {
+ return static_cast<uint8_t>(id >> 16);
}
-inline ResourceId::ResourceId(uint8_t p, uint8_t t, uint16_t e) : id((p << 24) | (t << 16) | e) {
-}
-
-inline bool ResourceId::isValid() const {
- return (id & 0xff000000u) != 0 && (id & 0x00ff0000u) != 0;
-}
-
-inline uint8_t ResourceId::packageId() const {
- return static_cast<uint8_t>(id >> 24);
-}
-
-inline uint8_t ResourceId::typeId() const {
- return static_cast<uint8_t>(id >> 16);
-}
-
-inline uint16_t ResourceId::entryId() const {
- return static_cast<uint16_t>(id);
+inline uint16_t ResourceId::entry_id() const {
+ return static_cast<uint16_t>(id);
}
inline bool operator<(const ResourceId& lhs, const ResourceId& rhs) {
- return lhs.id < rhs.id;
+ return lhs.id < rhs.id;
}
inline bool operator>(const ResourceId& lhs, const ResourceId& rhs) {
- return lhs.id > rhs.id;
+ return lhs.id > rhs.id;
}
inline bool operator==(const ResourceId& lhs, const ResourceId& rhs) {
- return lhs.id == rhs.id;
+ return lhs.id == rhs.id;
}
inline bool operator!=(const ResourceId& lhs, const ResourceId& rhs) {
- return lhs.id != rhs.id;
+ return lhs.id != rhs.id;
}
-inline ::std::ostream& operator<<(::std::ostream& out, const ResourceId& resId) {
- std::ios_base::fmtflags oldFlags = out.flags();
- char oldFill = out.fill();
- out << "0x" << std::internal << std::setfill('0') << std::setw(8)
- << std::hex << resId.id;
- out.flags(oldFlags);
- out.fill(oldFill);
- return out;
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ResourceId& res_id) {
+ std::ios_base::fmtflags old_flags = out.flags();
+ char old_fill = out.fill();
+ out << "0x" << std::internal << std::setfill('0') << std::setw(8) << std::hex
+ << res_id.id;
+ out.flags(old_flags);
+ out.fill(old_fill);
+ return out;
}
//
// ResourceType implementation.
//
-inline ::std::ostream& operator<<(::std::ostream& out, const ResourceType& val) {
- return out << toString(val);
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ResourceType& val) {
+ return out << ToString(val);
}
//
// ResourceName implementation.
//
-inline ResourceName::ResourceName(const StringPiece& p, ResourceType t, const StringPiece& e) :
- package(p.toString()), type(t), entry(e.toString()) {
-}
+inline ResourceName::ResourceName(const StringPiece& p, ResourceType t,
+ const StringPiece& e)
+ : package(p.ToString()), type(t), entry(e.ToString()) {}
inline int ResourceName::compare(const ResourceName& other) const {
- int cmp = package.compare(other.package);
- if (cmp != 0) return cmp;
- cmp = static_cast<int>(type) - static_cast<int>(other.type);
- if (cmp != 0) return cmp;
- cmp = entry.compare(other.entry);
- return cmp;
+ int cmp = package.compare(other.package);
+ if (cmp != 0) return cmp;
+ cmp = static_cast<int>(type) - static_cast<int>(other.type);
+ if (cmp != 0) return cmp;
+ cmp = entry.compare(other.entry);
+ return cmp;
}
-inline bool ResourceName::isValid() const {
- return !package.empty() && !entry.empty();
+inline bool ResourceName::is_valid() const {
+ return !package.empty() && !entry.empty();
}
inline bool operator<(const ResourceName& lhs, const ResourceName& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- < std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) <
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
inline bool operator==(const ResourceName& lhs, const ResourceName& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- == std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) ==
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
inline bool operator!=(const ResourceName& lhs, const ResourceName& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- != std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) !=
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
-inline ::std::ostream& operator<<(::std::ostream& out, const ResourceName& name) {
- if (!name.package.empty()) {
- out << name.package << ":";
- }
- return out << name.type << "/" << name.entry;
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ResourceName& name) {
+ if (!name.package.empty()) {
+ out << name.package << ":";
+ }
+ return out << name.type << "/" << name.entry;
}
-inline std::string ResourceName::toString() const {
- std::stringstream stream;
- stream << *this;
- return stream.str();
+inline std::string ResourceName::ToString() const {
+ std::stringstream stream;
+ stream << *this;
+ return stream.str();
}
//
// ResourceNameRef implementation.
//
-inline ResourceNameRef::ResourceNameRef(const ResourceName& rhs) :
- package(rhs.package), type(rhs.type), entry(rhs.entry) {
-}
+inline ResourceNameRef::ResourceNameRef(const ResourceName& rhs)
+ : package(rhs.package), type(rhs.type), entry(rhs.entry) {}
inline ResourceNameRef::ResourceNameRef(const StringPiece& p, ResourceType t,
- const StringPiece& e) :
- package(p), type(t), entry(e) {
-}
+ const StringPiece& e)
+ : package(p), type(t), entry(e) {}
inline ResourceNameRef& ResourceNameRef::operator=(const ResourceName& rhs) {
- package = rhs.package;
- type = rhs.type;
- entry = rhs.entry;
- return *this;
+ package = rhs.package;
+ type = rhs.type;
+ entry = rhs.entry;
+ return *this;
}
-inline ResourceName ResourceNameRef::toResourceName() const {
- return ResourceName(package, type, entry);
+inline ResourceName ResourceNameRef::ToResourceName() const {
+ return ResourceName(package, type, entry);
}
-inline bool ResourceNameRef::isValid() const {
- return !package.empty() && !entry.empty();
+inline bool ResourceNameRef::is_valid() const {
+ return !package.empty() && !entry.empty();
}
inline bool operator<(const ResourceNameRef& lhs, const ResourceNameRef& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- < std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) <
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
inline bool operator==(const ResourceNameRef& lhs, const ResourceNameRef& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- == std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) ==
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
inline bool operator!=(const ResourceNameRef& lhs, const ResourceNameRef& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- != std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) !=
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
-inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNameRef& name) {
- if (!name.package.empty()) {
- out << name.package << ":";
- }
- return out << name.type << "/" << name.entry;
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ResourceNameRef& name) {
+ if (!name.package.empty()) {
+ out << name.package << ":";
+ }
+ return out << name.type << "/" << name.entry;
}
inline bool operator<(const ResourceName& lhs, const ResourceNameRef& b) {
- return ResourceNameRef(lhs) < b;
+ return ResourceNameRef(lhs) < b;
}
inline bool operator!=(const ResourceName& lhs, const ResourceNameRef& rhs) {
- return ResourceNameRef(lhs) != rhs;
+ return ResourceNameRef(lhs) != rhs;
}
-inline bool operator==(const SourcedResourceName& lhs, const SourcedResourceName& rhs) {
- return lhs.name == rhs.name && lhs.line == rhs.line;
+inline bool operator==(const SourcedResourceName& lhs,
+ const SourcedResourceName& rhs) {
+ return lhs.name == rhs.name && lhs.line == rhs.line;
}
-} // namespace aapt
+} // namespace aapt
namespace std {
-template <> struct hash<aapt::ResourceName> {
- size_t operator()(const aapt::ResourceName& name) const {
- android::hash_t h = 0;
- h = android::JenkinsHashMix(h, hash<string>()(name.package));
- h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type));
- h = android::JenkinsHashMix(h, hash<string>()(name.entry));
- return static_cast<size_t>(h);
- }
+template <>
+struct hash<aapt::ResourceName> {
+ size_t operator()(const aapt::ResourceName& name) const {
+ android::hash_t h = 0;
+ h = android::JenkinsHashMix(h, hash<string>()(name.package));
+ h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type));
+ h = android::JenkinsHashMix(h, hash<string>()(name.entry));
+ return static_cast<size_t>(h);
+ }
};
-} // namespace std
+} // namespace std
-#endif // AAPT_RESOURCE_H
+#endif // AAPT_RESOURCE_H
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index c430c46..b16def4 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -15,6 +15,12 @@
*/
#include "ResourceParser.h"
+
+#include <functional>
+#include <sstream>
+
+#include "android-base/logging.h"
+
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
@@ -23,450 +29,492 @@
#include "util/Util.h"
#include "xml/XmlPullParser.h"
-#include <functional>
-#include <sstream>
-
namespace aapt {
-constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
+constexpr const char* sXliffNamespaceUri =
+ "urn:oasis:names:tc:xliff:document:1.2";
/**
- * Returns true if the element is <skip> or <eat-comment> and can be safely ignored.
+ * Returns true if the element is <skip> or <eat-comment> and can be safely
+ * ignored.
*/
-static bool shouldIgnoreElement(const StringPiece& ns, const StringPiece& name) {
- return ns.empty() && (name == "skip" || name == "eat-comment");
+static bool ShouldIgnoreElement(const StringPiece& ns,
+ const StringPiece& name) {
+ return ns.empty() && (name == "skip" || name == "eat-comment");
}
-static uint32_t parseFormatType(const StringPiece& piece) {
- if (piece == "reference") return android::ResTable_map::TYPE_REFERENCE;
- else if (piece == "string") return android::ResTable_map::TYPE_STRING;
- else if (piece == "integer") return android::ResTable_map::TYPE_INTEGER;
- else if (piece == "boolean") return android::ResTable_map::TYPE_BOOLEAN;
- else if (piece == "color") return android::ResTable_map::TYPE_COLOR;
- else if (piece == "float") return android::ResTable_map::TYPE_FLOAT;
- else if (piece == "dimension") return android::ResTable_map::TYPE_DIMENSION;
- else if (piece == "fraction") return android::ResTable_map::TYPE_FRACTION;
- else if (piece == "enum") return android::ResTable_map::TYPE_ENUM;
- else if (piece == "flags") return android::ResTable_map::TYPE_FLAGS;
- return 0;
+static uint32_t ParseFormatType(const StringPiece& piece) {
+ if (piece == "reference")
+ return android::ResTable_map::TYPE_REFERENCE;
+ else if (piece == "string")
+ return android::ResTable_map::TYPE_STRING;
+ else if (piece == "integer")
+ return android::ResTable_map::TYPE_INTEGER;
+ else if (piece == "boolean")
+ return android::ResTable_map::TYPE_BOOLEAN;
+ else if (piece == "color")
+ return android::ResTable_map::TYPE_COLOR;
+ else if (piece == "float")
+ return android::ResTable_map::TYPE_FLOAT;
+ else if (piece == "dimension")
+ return android::ResTable_map::TYPE_DIMENSION;
+ else if (piece == "fraction")
+ return android::ResTable_map::TYPE_FRACTION;
+ else if (piece == "enum")
+ return android::ResTable_map::TYPE_ENUM;
+ else if (piece == "flags")
+ return android::ResTable_map::TYPE_FLAGS;
+ return 0;
}
-static uint32_t parseFormatAttribute(const StringPiece& str) {
- uint32_t mask = 0;
- for (StringPiece part : util::tokenize(str, '|')) {
- StringPiece trimmedPart = util::trimWhitespace(part);
- uint32_t type = parseFormatType(trimmedPart);
- if (type == 0) {
- return 0;
- }
- mask |= type;
+static uint32_t ParseFormatAttribute(const StringPiece& str) {
+ uint32_t mask = 0;
+ for (StringPiece part : util::Tokenize(str, '|')) {
+ StringPiece trimmed_part = util::TrimWhitespace(part);
+ uint32_t type = ParseFormatType(trimmed_part);
+ if (type == 0) {
+ return 0;
}
- return mask;
+ mask |= type;
+ }
+ return mask;
}
/**
* A parsed resource ready to be added to the ResourceTable.
*/
struct ParsedResource {
- ResourceName name;
- ConfigDescription config;
- std::string product;
- Source source;
- ResourceId id;
- Maybe<SymbolState> symbolState;
- std::string comment;
- std::unique_ptr<Value> value;
- std::list<ParsedResource> childResources;
+ ResourceName name;
+ ConfigDescription config;
+ std::string product;
+ Source source;
+ ResourceId id;
+ Maybe<SymbolState> symbol_state;
+ std::string comment;
+ std::unique_ptr<Value> value;
+ std::list<ParsedResource> child_resources;
};
// Recursively adds resources to the ResourceTable.
-static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
- StringPiece trimmedComment = util::trimWhitespace(res->comment);
- if (trimmedComment.size() != res->comment.size()) {
- // Only if there was a change do we re-assign.
- res->comment = trimmedComment.toString();
- }
+static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag,
+ ParsedResource* res) {
+ StringPiece trimmed_comment = util::TrimWhitespace(res->comment);
+ if (trimmed_comment.size() != res->comment.size()) {
+ // Only if there was a change do we re-assign.
+ res->comment = trimmed_comment.ToString();
+ }
- if (res->symbolState) {
- Symbol symbol;
- symbol.state = res->symbolState.value();
- symbol.source = res->source;
- symbol.comment = res->comment;
- if (!table->setSymbolState(res->name, res->id, symbol, diag)) {
- return false;
- }
+ if (res->symbol_state) {
+ Symbol symbol;
+ symbol.state = res->symbol_state.value();
+ symbol.source = res->source;
+ symbol.comment = res->comment;
+ if (!table->SetSymbolState(res->name, res->id, symbol, diag)) {
+ return false;
}
+ }
- if (res->value) {
- // Attach the comment, source and config to the value.
- res->value->setComment(std::move(res->comment));
- res->value->setSource(std::move(res->source));
+ if (res->value) {
+ // Attach the comment, source and config to the value.
+ res->value->SetComment(std::move(res->comment));
+ res->value->SetSource(std::move(res->source));
- if (!table->addResource(res->name, res->id, res->config, res->product,
- std::move(res->value), diag)) {
- return false;
- }
+ if (!table->AddResource(res->name, res->id, res->config, res->product,
+ std::move(res->value), diag)) {
+ return false;
}
+ }
- bool error = false;
- for (ParsedResource& child : res->childResources) {
- error |= !addResourcesToTable(table, diag, &child);
- }
- return !error;
+ bool error = false;
+ for (ParsedResource& child : res->child_resources) {
+ error |= !AddResourcesToTable(table, diag, &child);
+ }
+ return !error;
}
// Convenient aliases for more readable function calls.
-enum {
- kAllowRawString = true,
- kNoRawString = false
-};
+enum { kAllowRawString = true, kNoRawString = false };
-ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
+ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table,
+ const Source& source,
const ConfigDescription& config,
- const ResourceParserOptions& options) :
- mDiag(diag), mTable(table), mSource(source), mConfig(config), mOptions(options) {
-}
+ const ResourceParserOptions& options)
+ : diag_(diag),
+ table_(table),
+ source_(source),
+ config_(config),
+ options_(options) {}
/**
* Build a string from XML that converts nested elements into Span objects.
*/
-bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::string* outRawString,
- StyleString* outStyleString) {
- std::vector<Span> spanStack;
+bool ResourceParser::FlattenXmlSubtree(xml::XmlPullParser* parser,
+ std::string* out_raw_string,
+ StyleString* out_style_string) {
+ std::vector<Span> span_stack;
- bool error = false;
- outRawString->clear();
- outStyleString->spans.clear();
- util::StringBuilder builder;
- size_t depth = 1;
- while (xml::XmlPullParser::isGoodEvent(parser->next())) {
- const xml::XmlPullParser::Event event = parser->getEvent();
- if (event == xml::XmlPullParser::Event::kEndElement) {
- if (!parser->getElementNamespace().empty()) {
- // We already warned and skipped the start element, so just skip here too
- continue;
- }
+ bool error = false;
+ out_raw_string->clear();
+ out_style_string->spans.clear();
+ util::StringBuilder builder;
+ size_t depth = 1;
+ while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
+ const xml::XmlPullParser::Event event = parser->event();
+ if (event == xml::XmlPullParser::Event::kEndElement) {
+ if (!parser->element_namespace().empty()) {
+ // We already warned and skipped the start element, so just skip here
+ // too
+ continue;
+ }
- depth--;
- if (depth == 0) {
- break;
- }
-
- spanStack.back().lastChar = builder.utf16Len() - 1;
- outStyleString->spans.push_back(spanStack.back());
- spanStack.pop_back();
-
- } else if (event == xml::XmlPullParser::Event::kText) {
- outRawString->append(parser->getText());
- builder.append(parser->getText());
-
- } else if (event == xml::XmlPullParser::Event::kStartElement) {
- if (!parser->getElementNamespace().empty()) {
- if (parser->getElementNamespace() != sXliffNamespaceUri) {
- // Only warn if this isn't an xliff namespace.
- mDiag->warn(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "skipping element '"
- << parser->getElementName()
- << "' with unknown namespace '"
- << parser->getElementNamespace()
- << "'");
- }
- continue;
- }
- depth++;
-
- // Build a span object out of the nested element.
- std::string spanName = parser->getElementName();
- const auto endAttrIter = parser->endAttributes();
- for (auto attrIter = parser->beginAttributes(); attrIter != endAttrIter; ++attrIter) {
- spanName += ";";
- spanName += attrIter->name;
- spanName += "=";
- spanName += attrIter->value;
- }
-
- if (builder.utf16Len() > std::numeric_limits<uint32_t>::max()) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "style string '" << builder.str() << "' is too long");
- error = true;
- } else {
- spanStack.push_back(Span{ spanName, static_cast<uint32_t>(builder.utf16Len()) });
- }
-
- } else if (event == xml::XmlPullParser::Event::kComment) {
- // Skip
- } else {
- assert(false);
- }
- }
- assert(spanStack.empty() && "spans haven't been fully processed");
-
- outStyleString->str = builder.str();
- return !error;
-}
-
-bool ResourceParser::parse(xml::XmlPullParser* parser) {
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip comments and text.
- continue;
- }
-
- if (!parser->getElementNamespace().empty() || parser->getElementName() != "resources") {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "root element must be <resources>");
- return false;
- }
-
- error |= !parseResources(parser);
+ depth--;
+ if (depth == 0) {
break;
- };
+ }
- if (parser->getEvent() == xml::XmlPullParser::Event::kBadDocument) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "xml parser error: " << parser->getLastError());
- return false;
+ span_stack.back().last_char = builder.Utf16Len() - 1;
+ out_style_string->spans.push_back(span_stack.back());
+ span_stack.pop_back();
+
+ } else if (event == xml::XmlPullParser::Event::kText) {
+ out_raw_string->append(parser->text());
+ builder.Append(parser->text());
+
+ } else if (event == xml::XmlPullParser::Event::kStartElement) {
+ if (!parser->element_namespace().empty()) {
+ if (parser->element_namespace() != sXliffNamespaceUri) {
+ // Only warn if this isn't an xliff namespace.
+ diag_->Warn(DiagMessage(source_.WithLine(parser->line_number()))
+ << "skipping element '" << parser->element_name()
+ << "' with unknown namespace '"
+ << parser->element_namespace() << "'");
+ }
+ continue;
+ }
+ depth++;
+
+ // Build a span object out of the nested element.
+ std::string span_name = parser->element_name();
+ const auto end_attr_iter = parser->end_attributes();
+ for (auto attr_iter = parser->begin_attributes();
+ attr_iter != end_attr_iter; ++attr_iter) {
+ span_name += ";";
+ span_name += attr_iter->name;
+ span_name += "=";
+ span_name += attr_iter->value;
+ }
+
+ if (builder.Utf16Len() > std::numeric_limits<uint32_t>::max()) {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << "style string '" << builder.ToString()
+ << "' is too long");
+ error = true;
+ } else {
+ span_stack.push_back(
+ Span{span_name, static_cast<uint32_t>(builder.Utf16Len())});
+ }
+
+ } else if (event == xml::XmlPullParser::Event::kComment) {
+ // Skip
+ } else {
+ LOG(FATAL) << "unhandled XML event";
}
- return !error;
+ }
+ CHECK(span_stack.empty()) << "spans haven't been fully processed";
+
+ out_style_string->str = builder.ToString();
+ return !error;
}
-bool ResourceParser::parseResources(xml::XmlPullParser* parser) {
- std::set<ResourceName> strippedResources;
-
- bool error = false;
- std::string comment;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- const xml::XmlPullParser::Event event = parser->getEvent();
- if (event == xml::XmlPullParser::Event::kComment) {
- comment = parser->getComment();
- continue;
- }
-
- if (event == xml::XmlPullParser::Event::kText) {
- if (!util::trimWhitespace(parser->getText()).empty()) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "plain text not allowed here");
- error = true;
- }
- continue;
- }
-
- assert(event == xml::XmlPullParser::Event::kStartElement);
-
- if (!parser->getElementNamespace().empty()) {
- // Skip unknown namespace.
- continue;
- }
-
- std::string elementName = parser->getElementName();
- if (elementName == "skip" || elementName == "eat-comment") {
- comment = "";
- continue;
- }
-
- ParsedResource parsedResource;
- parsedResource.config = mConfig;
- parsedResource.source = mSource.withLine(parser->getLineNumber());
- parsedResource.comment = std::move(comment);
-
- // Extract the product name if it exists.
- if (Maybe<StringPiece> maybeProduct = xml::findNonEmptyAttribute(parser, "product")) {
- parsedResource.product = maybeProduct.value().toString();
- }
-
- // Parse the resource regardless of product.
- if (!parseResource(parser, &parsedResource)) {
- error = true;
- continue;
- }
-
- if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
- error = true;
- }
+bool ResourceParser::Parse(xml::XmlPullParser* parser) {
+ bool error = false;
+ const size_t depth = parser->depth();
+ while (xml::XmlPullParser::NextChildNode(parser, depth)) {
+ if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip comments and text.
+ continue;
}
- // Check that we included at least one variant of each stripped resource.
- for (const ResourceName& strippedResource : strippedResources) {
- if (!mTable->findResource(strippedResource)) {
- // Failed to find the resource.
- mDiag->error(DiagMessage(mSource) << "resource '" << strippedResource << "' "
- "was filtered out but no product variant remains");
- error = true;
- }
+ if (!parser->element_namespace().empty() ||
+ parser->element_name() != "resources") {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << "root element must be <resources>");
+ return false;
}
- return !error;
-}
+ error |= !ParseResources(parser);
+ break;
+ };
-
-bool ResourceParser::parseResource(xml::XmlPullParser* parser, ParsedResource* outResource) {
- struct ItemTypeFormat {
- ResourceType type;
- uint32_t format;
- };
-
- using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*, ParsedResource*)>;
-
- static const auto elToItemMap = ImmutableMap<std::string, ItemTypeFormat>::createPreSorted({
- { "bool", { ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN } },
- { "color", { ResourceType::kColor, android::ResTable_map::TYPE_COLOR } },
- { "dimen", { ResourceType::kDimen, android::ResTable_map::TYPE_FLOAT
- | android::ResTable_map::TYPE_FRACTION
- | android::ResTable_map::TYPE_DIMENSION } },
- { "drawable", { ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR } },
- { "fraction", { ResourceType::kFraction, android::ResTable_map::TYPE_FLOAT
- | android::ResTable_map::TYPE_FRACTION
- | android::ResTable_map::TYPE_DIMENSION } },
- { "integer", { ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER } },
- { "string", { ResourceType::kString, android::ResTable_map::TYPE_STRING } },
- });
-
- static const auto elToBagMap = ImmutableMap<std::string, BagParseFunc>::createPreSorted({
- { "add-resource", std::mem_fn(&ResourceParser::parseAddResource) },
- { "array", std::mem_fn(&ResourceParser::parseArray) },
- { "attr", std::mem_fn(&ResourceParser::parseAttr) },
- { "declare-styleable", std::mem_fn(&ResourceParser::parseDeclareStyleable) },
- { "integer-array", std::mem_fn(&ResourceParser::parseIntegerArray) },
- { "java-symbol", std::mem_fn(&ResourceParser::parseSymbol) },
- { "plurals", std::mem_fn(&ResourceParser::parsePlural) },
- { "public", std::mem_fn(&ResourceParser::parsePublic) },
- { "public-group", std::mem_fn(&ResourceParser::parsePublicGroup) },
- { "string-array", std::mem_fn(&ResourceParser::parseStringArray) },
- { "style", std::mem_fn(&ResourceParser::parseStyle) },
- { "symbol", std::mem_fn(&ResourceParser::parseSymbol) },
- });
-
- std::string resourceType = parser->getElementName();
-
- // The value format accepted for this resource.
- uint32_t resourceFormat = 0u;
-
- if (resourceType == "item") {
- // Items have their type encoded in the type attribute.
- if (Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type")) {
- resourceType = maybeType.value().toString();
- } else {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "<item> must have a 'type' attribute");
- return false;
- }
-
- if (Maybe<StringPiece> maybeFormat = xml::findNonEmptyAttribute(parser, "format")) {
- // An explicit format for this resource was specified. The resource will retain
- // its type in its name, but the accepted value for this type is overridden.
- resourceFormat = parseFormatType(maybeFormat.value());
- if (!resourceFormat) {
- mDiag->error(DiagMessage(outResource->source)
- << "'" << maybeFormat.value() << "' is an invalid format");
- return false;
- }
- }
- }
-
- // Get the name of the resource. This will be checked later, because not all
- // XML elements require a name.
- Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
-
- if (resourceType == "id") {
- if (!maybeName) {
- mDiag->error(DiagMessage(outResource->source)
- << "<" << parser->getElementName() << "> missing 'name' attribute");
- return false;
- }
-
- outResource->name.type = ResourceType::kId;
- outResource->name.entry = maybeName.value().toString();
- outResource->value = util::make_unique<Id>();
- return true;
- }
-
- const auto itemIter = elToItemMap.find(resourceType);
- if (itemIter != elToItemMap.end()) {
- // This is an item, record its type and format and start parsing.
-
- if (!maybeName) {
- mDiag->error(DiagMessage(outResource->source)
- << "<" << parser->getElementName() << "> missing 'name' attribute");
- return false;
- }
-
- outResource->name.type = itemIter->second.type;
- outResource->name.entry = maybeName.value().toString();
-
- // Only use the implicit format for this type if it wasn't overridden.
- if (!resourceFormat) {
- resourceFormat = itemIter->second.format;
- }
-
- if (!parseItem(parser, outResource, resourceFormat)) {
- return false;
- }
- return true;
- }
-
- // This might be a bag or something.
- const auto bagIter = elToBagMap.find(resourceType);
- if (bagIter != elToBagMap.end()) {
- // Ensure we have a name (unless this is a <public-group>).
- if (resourceType != "public-group") {
- if (!maybeName) {
- mDiag->error(DiagMessage(outResource->source)
- << "<" << parser->getElementName() << "> missing 'name' attribute");
- return false;
- }
-
- outResource->name.entry = maybeName.value().toString();
- }
-
- // Call the associated parse method. The type will be filled in by the
- // parse func.
- if (!bagIter->second(this, parser, outResource)) {
- return false;
- }
- return true;
- }
-
- // Try parsing the elementName (or type) as a resource. These shall only be
- // resources like 'layout' or 'xml' and they can only be references.
- const ResourceType* parsedType = parseResourceType(resourceType);
- if (parsedType) {
- if (!maybeName) {
- mDiag->error(DiagMessage(outResource->source)
- << "<" << parser->getElementName() << "> missing 'name' attribute");
- return false;
- }
-
- outResource->name.type = *parsedType;
- outResource->name.entry = maybeName.value().toString();
- outResource->value = parseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
- if (!outResource->value) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid value for type '" << *parsedType << "'. Expected a reference");
- return false;
- }
- return true;
- }
-
- mDiag->warn(DiagMessage(outResource->source)
- << "unknown resource type '" << parser->getElementName() << "'");
+ if (parser->event() == xml::XmlPullParser::Event::kBadDocument) {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << "xml parser error: " << parser->error());
return false;
+ }
+ return !error;
}
-bool ResourceParser::parseItem(xml::XmlPullParser* parser, ParsedResource* outResource,
- const uint32_t format) {
- if (format == android::ResTable_map::TYPE_STRING) {
- return parseString(parser, outResource);
+bool ResourceParser::ParseResources(xml::XmlPullParser* parser) {
+ std::set<ResourceName> stripped_resources;
+
+ bool error = false;
+ std::string comment;
+ const size_t depth = parser->depth();
+ while (xml::XmlPullParser::NextChildNode(parser, depth)) {
+ const xml::XmlPullParser::Event event = parser->event();
+ if (event == xml::XmlPullParser::Event::kComment) {
+ comment = parser->comment();
+ continue;
}
- outResource->value = parseXml(parser, format, kNoRawString);
- if (!outResource->value) {
- mDiag->error(DiagMessage(outResource->source) << "invalid " << outResource->name.type);
+ if (event == xml::XmlPullParser::Event::kText) {
+ if (!util::TrimWhitespace(parser->text()).empty()) {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << "plain text not allowed here");
+ error = true;
+ }
+ continue;
+ }
+
+ CHECK(event == xml::XmlPullParser::Event::kStartElement);
+
+ if (!parser->element_namespace().empty()) {
+ // Skip unknown namespace.
+ continue;
+ }
+
+ std::string element_name = parser->element_name();
+ if (element_name == "skip" || element_name == "eat-comment") {
+ comment = "";
+ continue;
+ }
+
+ ParsedResource parsed_resource;
+ parsed_resource.config = config_;
+ parsed_resource.source = source_.WithLine(parser->line_number());
+ parsed_resource.comment = std::move(comment);
+
+ // Extract the product name if it exists.
+ if (Maybe<StringPiece> maybe_product =
+ xml::FindNonEmptyAttribute(parser, "product")) {
+ parsed_resource.product = maybe_product.value().ToString();
+ }
+
+ // Parse the resource regardless of product.
+ if (!ParseResource(parser, &parsed_resource)) {
+ error = true;
+ continue;
+ }
+
+ if (!AddResourcesToTable(table_, diag_, &parsed_resource)) {
+ error = true;
+ }
+ }
+
+ // Check that we included at least one variant of each stripped resource.
+ for (const ResourceName& stripped_resource : stripped_resources) {
+ if (!table_->FindResource(stripped_resource)) {
+ // Failed to find the resource.
+ diag_->Error(DiagMessage(source_)
+ << "resource '" << stripped_resource
+ << "' "
+ "was filtered out but no product variant remains");
+ error = true;
+ }
+ }
+
+ return !error;
+}
+
+bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ struct ItemTypeFormat {
+ ResourceType type;
+ uint32_t format;
+ };
+
+ using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*,
+ ParsedResource*)>;
+
+ static const auto elToItemMap =
+ ImmutableMap<std::string, ItemTypeFormat>::CreatePreSorted({
+ {"bool", {ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN}},
+ {"color", {ResourceType::kColor, android::ResTable_map::TYPE_COLOR}},
+ {"dimen",
+ {ResourceType::kDimen, android::ResTable_map::TYPE_FLOAT |
+ android::ResTable_map::TYPE_FRACTION |
+ android::ResTable_map::TYPE_DIMENSION}},
+ {"drawable",
+ {ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR}},
+ {"fraction",
+ {ResourceType::kFraction,
+ android::ResTable_map::TYPE_FLOAT |
+ android::ResTable_map::TYPE_FRACTION |
+ android::ResTable_map::TYPE_DIMENSION}},
+ {"integer",
+ {ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER}},
+ {"string",
+ {ResourceType::kString, android::ResTable_map::TYPE_STRING}},
+ });
+
+ static const auto elToBagMap =
+ ImmutableMap<std::string, BagParseFunc>::CreatePreSorted({
+ {"add-resource", std::mem_fn(&ResourceParser::ParseAddResource)},
+ {"array", std::mem_fn(&ResourceParser::ParseArray)},
+ {"attr", std::mem_fn(&ResourceParser::ParseAttr)},
+ {"declare-styleable",
+ std::mem_fn(&ResourceParser::ParseDeclareStyleable)},
+ {"integer-array", std::mem_fn(&ResourceParser::ParseIntegerArray)},
+ {"java-symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
+ {"plurals", std::mem_fn(&ResourceParser::ParsePlural)},
+ {"public", std::mem_fn(&ResourceParser::ParsePublic)},
+ {"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
+ {"string-array", std::mem_fn(&ResourceParser::ParseStringArray)},
+ {"style", std::mem_fn(&ResourceParser::ParseStyle)},
+ {"symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
+ });
+
+ std::string resource_type = parser->element_name();
+
+ // The value format accepted for this resource.
+ uint32_t resource_format = 0u;
+
+ if (resource_type == "item") {
+ // Items have their type encoded in the type attribute.
+ if (Maybe<StringPiece> maybe_type =
+ xml::FindNonEmptyAttribute(parser, "type")) {
+ resource_type = maybe_type.value().ToString();
+ } else {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << "<item> must have a 'type' attribute");
+ return false;
+ }
+
+ if (Maybe<StringPiece> maybe_format =
+ xml::FindNonEmptyAttribute(parser, "format")) {
+ // An explicit format for this resource was specified. The resource will
+ // retain
+ // its type in its name, but the accepted value for this type is
+ // overridden.
+ resource_format = ParseFormatType(maybe_format.value());
+ if (!resource_format) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "'" << maybe_format.value()
+ << "' is an invalid format");
return false;
+ }
+ }
+ }
+
+ // Get the name of the resource. This will be checked later, because not all
+ // XML elements require a name.
+ Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+
+ if (resource_type == "id") {
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << parser->element_name()
+ << "> missing 'name' attribute");
+ return false;
+ }
+
+ out_resource->name.type = ResourceType::kId;
+ out_resource->name.entry = maybe_name.value().ToString();
+ out_resource->value = util::make_unique<Id>();
+ return true;
+ }
+
+ const auto item_iter = elToItemMap.find(resource_type);
+ if (item_iter != elToItemMap.end()) {
+ // This is an item, record its type and format and start parsing.
+
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << parser->element_name()
+ << "> missing 'name' attribute");
+ return false;
+ }
+
+ out_resource->name.type = item_iter->second.type;
+ out_resource->name.entry = maybe_name.value().ToString();
+
+ // Only use the implicit format for this type if it wasn't overridden.
+ if (!resource_format) {
+ resource_format = item_iter->second.format;
+ }
+
+ if (!ParseItem(parser, out_resource, resource_format)) {
+ return false;
}
return true;
+ }
+
+ // This might be a bag or something.
+ const auto bag_iter = elToBagMap.find(resource_type);
+ if (bag_iter != elToBagMap.end()) {
+ // Ensure we have a name (unless this is a <public-group>).
+ if (resource_type != "public-group") {
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << parser->element_name()
+ << "> missing 'name' attribute");
+ return false;
+ }
+
+ out_resource->name.entry = maybe_name.value().ToString();
+ }
+
+ // Call the associated parse method. The type will be filled in by the
+ // parse func.
+ if (!bag_iter->second(this, parser, out_resource)) {
+ return false;
+ }
+ return true;
+ }
+
+ // Try parsing the elementName (or type) as a resource. These shall only be
+ // resources like 'layout' or 'xml' and they can only be references.
+ const ResourceType* parsed_type = ParseResourceType(resource_type);
+ if (parsed_type) {
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << parser->element_name()
+ << "> missing 'name' attribute");
+ return false;
+ }
+
+ out_resource->name.type = *parsed_type;
+ out_resource->name.entry = maybe_name.value().ToString();
+ out_resource->value =
+ ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
+ if (!out_resource->value) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "invalid value for type '" << *parsed_type
+ << "'. Expected a reference");
+ return false;
+ }
+ return true;
+ }
+
+ diag_->Warn(DiagMessage(out_resource->source)
+ << "unknown resource type '" << parser->element_name() << "'");
+ return false;
+}
+
+bool ResourceParser::ParseItem(xml::XmlPullParser* parser,
+ ParsedResource* out_resource,
+ const uint32_t format) {
+ if (format == android::ResTable_map::TYPE_STRING) {
+ return ParseString(parser, out_resource);
+ }
+
+ out_resource->value = ParseXml(parser, format, kNoRawString);
+ if (!out_resource->value) {
+ diag_->Error(DiagMessage(out_resource->source) << "invalid "
+ << out_resource->name.type);
+ return false;
+ }
+ return true;
}
/**
@@ -476,771 +524,839 @@
* an Item. If allowRawValue is false, nullptr is returned in this
* case.
*/
-std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const uint32_t typeMask,
- const bool allowRawValue) {
- const size_t beginXmlLine = parser->getLineNumber();
+std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
+ const uint32_t type_mask,
+ const bool allow_raw_value) {
+ const size_t begin_xml_line = parser->line_number();
- std::string rawValue;
- StyleString styleString;
- if (!flattenXmlSubtree(parser, &rawValue, &styleString)) {
- return {};
- }
-
- if (!styleString.spans.empty()) {
- // This can only be a StyledString.
- return util::make_unique<StyledString>(
- mTable->stringPool.makeRef(styleString, StringPool::Context{ 1, mConfig }));
- }
-
- auto onCreateReference = [&](const ResourceName& name) {
- // name.package can be empty here, as it will assume the package name of the table.
- std::unique_ptr<Id> id = util::make_unique<Id>();
- id->setSource(mSource.withLine(beginXmlLine));
- mTable->addResource(name, {}, {}, std::move(id), mDiag);
- };
-
- // Process the raw value.
- std::unique_ptr<Item> processedItem = ResourceUtils::tryParseItemForAttribute(
- rawValue, typeMask, onCreateReference);
- if (processedItem) {
- // Fix up the reference.
- if (Reference* ref = valueCast<Reference>(processedItem.get())) {
- transformReferenceFromNamespace(parser, "", ref);
- }
- return processedItem;
- }
-
- // Try making a regular string.
- if (typeMask & android::ResTable_map::TYPE_STRING) {
- // Use the trimmed, escaped string.
- return util::make_unique<String>(
- mTable->stringPool.makeRef(styleString.str, StringPool::Context{ 1, mConfig }));
- }
-
- if (allowRawValue) {
- // We can't parse this so return a RawString if we are allowed.
- return util::make_unique<RawString>(
- mTable->stringPool.makeRef(rawValue, StringPool::Context{ 1, mConfig }));
- }
+ std::string raw_value;
+ StyleString style_string;
+ if (!FlattenXmlSubtree(parser, &raw_value, &style_string)) {
return {};
+ }
+
+ if (!style_string.spans.empty()) {
+ // This can only be a StyledString.
+ return util::make_unique<StyledString>(table_->string_pool.MakeRef(
+ style_string,
+ StringPool::Context(StringPool::Context::kStylePriority, config_)));
+ }
+
+ auto on_create_reference = [&](const ResourceName& name) {
+ // name.package can be empty here, as it will assume the package name of the
+ // table.
+ std::unique_ptr<Id> id = util::make_unique<Id>();
+ id->SetSource(source_.WithLine(begin_xml_line));
+ table_->AddResource(name, {}, {}, std::move(id), diag_);
+ };
+
+ // Process the raw value.
+ std::unique_ptr<Item> processed_item =
+ ResourceUtils::TryParseItemForAttribute(raw_value, type_mask,
+ on_create_reference);
+ if (processed_item) {
+ // Fix up the reference.
+ if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
+ TransformReferenceFromNamespace(parser, "", ref);
+ }
+ return processed_item;
+ }
+
+ // Try making a regular string.
+ if (type_mask & android::ResTable_map::TYPE_STRING) {
+ // Use the trimmed, escaped string.
+ return util::make_unique<String>(table_->string_pool.MakeRef(
+ style_string.str, StringPool::Context(config_)));
+ }
+
+ if (allow_raw_value) {
+ // We can't parse this so return a RawString if we are allowed.
+ return util::make_unique<RawString>(
+ table_->string_pool.MakeRef(raw_value, StringPool::Context(config_)));
+ }
+ return {};
}
-bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* outResource) {
- bool formatted = true;
- if (Maybe<StringPiece> formattedAttr = xml::findAttribute(parser, "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 ResourceParser::ParseString(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ bool formatted = true;
+ if (Maybe<StringPiece> formatted_attr =
+ xml::FindAttribute(parser, "formatted")) {
+ Maybe<bool> maybe_formatted =
+ ResourceUtils::ParseBool(formatted_attr.value());
+ if (!maybe_formatted) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "invalid value for 'formatted'. Must be a boolean");
+ return false;
}
+ formatted = maybe_formatted.value();
+ }
- bool translateable = mOptions.translatable;
- if (Maybe<StringPiece> translateableAttr = xml::findAttribute(parser, "translatable")) {
- 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();
+ bool translateable = options_.translatable;
+ if (Maybe<StringPiece> translateable_attr =
+ xml::FindAttribute(parser, "translatable")) {
+ Maybe<bool> maybe_translateable =
+ ResourceUtils::ParseBool(translateable_attr.value());
+ if (!maybe_translateable) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "invalid value for 'translatable'. Must be a boolean");
+ return false;
}
+ translateable = maybe_translateable.value();
+ }
- outResource->value = parseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
- if (!outResource->value) {
- mDiag->error(DiagMessage(outResource->source) << "not a valid string");
- return false;
- }
-
- if (String* stringValue = valueCast<String>(outResource->value.get())) {
- stringValue->setTranslateable(translateable);
-
- if (formatted && translateable) {
- if (!util::verifyJavaStringFormat(*stringValue->value)) {
- DiagMessage msg(outResource->source);
- msg << "multiple substitutions specified in non-positional format; "
- "did you mean to add the formatted=\"false\" attribute?";
- if (mOptions.errorOnPositionalArguments) {
- mDiag->error(msg);
- return false;
- }
-
- mDiag->warn(msg);
- }
- }
-
- } else if (StyledString* stringValue = valueCast<StyledString>(outResource->value.get())) {
- stringValue->setTranslateable(translateable);
- }
- return true;
-}
-
-bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource) {
- Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type");
- if (!maybeType) {
- mDiag->error(DiagMessage(outResource->source) << "<public> must have a 'type' attribute");
- return false;
- }
-
- const ResourceType* parsedType = parseResourceType(maybeType.value());
- if (!parsedType) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid resource type '" << maybeType.value() << "' in <public>");
- return false;
- }
-
- outResource->name.type = *parsedType;
-
- if (Maybe<StringPiece> maybeIdStr = xml::findNonEmptyAttribute(parser, "id")) {
- Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(maybeIdStr.value());
- if (!maybeId) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid resource ID '" << maybeId.value() << "' in <public>");
- return false;
- }
- outResource->id = maybeId.value();
- }
-
- if (*parsedType == ResourceType::kId) {
- // An ID marked as public is also the definition of an ID.
- outResource->value = util::make_unique<Id>();
- }
-
- outResource->symbolState = SymbolState::kPublic;
- return true;
-}
-
-bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource) {
- Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type");
- if (!maybeType) {
- mDiag->error(DiagMessage(outResource->source)
- << "<public-group> must have a 'type' attribute");
- return false;
- }
-
- const ResourceType* parsedType = parseResourceType(maybeType.value());
- if (!parsedType) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid resource type '" << maybeType.value() << "' in <public-group>");
- return false;
- }
-
- Maybe<StringPiece> maybeIdStr = xml::findNonEmptyAttribute(parser, "first-id");
- if (!maybeIdStr) {
- mDiag->error(DiagMessage(outResource->source)
- << "<public-group> must have a 'first-id' attribute");
- return false;
- }
-
- Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(maybeIdStr.value());
- if (!maybeId) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid resource ID '" << maybeIdStr.value() << "' in <public-group>");
- return false;
- }
-
- ResourceId nextId = maybeId.value();
-
- std::string comment;
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
- comment = util::trimWhitespace(parser->getComment()).toString();
- continue;
- } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip text.
- continue;
- }
-
- const Source itemSource = mSource.withLine(parser->getLineNumber());
- const std::string& elementNamespace = parser->getElementNamespace();
- const std::string& elementName = parser->getElementName();
- if (elementNamespace.empty() && elementName == "public") {
- Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
- if (!maybeName) {
- mDiag->error(DiagMessage(itemSource) << "<public> must have a 'name' attribute");
- error = true;
- continue;
- }
-
- if (xml::findNonEmptyAttribute(parser, "id")) {
- mDiag->error(DiagMessage(itemSource) << "'id' is ignored within <public-group>");
- error = true;
- continue;
- }
-
- if (xml::findNonEmptyAttribute(parser, "type")) {
- mDiag->error(DiagMessage(itemSource) << "'type' is ignored within <public-group>");
- error = true;
- continue;
- }
-
- ParsedResource childResource;
- childResource.name.type = *parsedType;
- childResource.name.entry = maybeName.value().toString();
- childResource.id = nextId;
- childResource.comment = std::move(comment);
- childResource.source = itemSource;
- childResource.symbolState = SymbolState::kPublic;
- outResource->childResources.push_back(std::move(childResource));
-
- nextId.id += 1;
-
- } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
- mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
- error = true;
- }
- }
- return !error;
-}
-
-bool ResourceParser::parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource) {
- Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type");
- if (!maybeType) {
- mDiag->error(DiagMessage(outResource->source)
- << "<" << parser->getElementName() << "> must have a 'type' attribute");
- return false;
- }
-
- const ResourceType* parsedType = parseResourceType(maybeType.value());
- if (!parsedType) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid resource type '" << maybeType.value()
- << "' in <" << parser->getElementName() << ">");
- return false;
- }
-
- outResource->name.type = *parsedType;
- return true;
-}
-
-bool ResourceParser::parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource) {
- if (parseSymbolImpl(parser, outResource)) {
- outResource->symbolState = SymbolState::kPrivate;
- return true;
- }
+ out_resource->value =
+ ParseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
+ if (!out_resource->value) {
+ diag_->Error(DiagMessage(out_resource->source) << "not a valid string");
return false;
+ }
+
+ if (String* string_value = ValueCast<String>(out_resource->value.get())) {
+ string_value->SetTranslateable(translateable);
+
+ if (formatted && translateable) {
+ if (!util::VerifyJavaStringFormat(*string_value->value)) {
+ DiagMessage msg(out_resource->source);
+ msg << "multiple substitutions specified in non-positional format; "
+ "did you mean to add the formatted=\"false\" attribute?";
+ if (options_.error_on_positional_arguments) {
+ diag_->Error(msg);
+ return false;
+ }
+
+ diag_->Warn(msg);
+ }
+ }
+
+ } else if (StyledString* string_value =
+ ValueCast<StyledString>(out_resource->value.get())) {
+ string_value->SetTranslateable(translateable);
+ }
+ return true;
}
-bool ResourceParser::parseAddResource(xml::XmlPullParser* parser, ParsedResource* outResource) {
- if (parseSymbolImpl(parser, outResource)) {
- outResource->symbolState = SymbolState::kUndefined;
- return true;
- }
+bool ResourceParser::ParsePublic(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ if (!maybe_type) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<public> must have a 'type' attribute");
return false;
+ }
+
+ const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+ if (!parsed_type) {
+ diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
+ << maybe_type.value()
+ << "' in <public>");
+ return false;
+ }
+
+ out_resource->name.type = *parsed_type;
+
+ if (Maybe<StringPiece> maybe_id_str =
+ xml::FindNonEmptyAttribute(parser, "id")) {
+ Maybe<ResourceId> maybe_id =
+ ResourceUtils::ParseResourceId(maybe_id_str.value());
+ if (!maybe_id) {
+ diag_->Error(DiagMessage(out_resource->source) << "invalid resource ID '"
+ << maybe_id.value()
+ << "' in <public>");
+ return false;
+ }
+ out_resource->id = maybe_id.value();
+ }
+
+ if (*parsed_type == ResourceType::kId) {
+ // An ID marked as public is also the definition of an ID.
+ out_resource->value = util::make_unique<Id>();
+ }
+
+ out_resource->symbol_state = SymbolState::kPublic;
+ return true;
}
+bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ if (!maybe_type) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<public-group> must have a 'type' attribute");
+ return false;
+ }
-bool ResourceParser::parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource) {
- return parseAttrImpl(parser, outResource, false);
+ const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+ if (!parsed_type) {
+ diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
+ << maybe_type.value()
+ << "' in <public-group>");
+ return false;
+ }
+
+ Maybe<StringPiece> maybe_id_str =
+ xml::FindNonEmptyAttribute(parser, "first-id");
+ if (!maybe_id_str) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<public-group> must have a 'first-id' attribute");
+ return false;
+ }
+
+ Maybe<ResourceId> maybe_id =
+ ResourceUtils::ParseResourceId(maybe_id_str.value());
+ if (!maybe_id) {
+ diag_->Error(DiagMessage(out_resource->source) << "invalid resource ID '"
+ << maybe_id_str.value()
+ << "' in <public-group>");
+ return false;
+ }
+
+ ResourceId next_id = maybe_id.value();
+
+ std::string comment;
+ bool error = false;
+ const size_t depth = parser->depth();
+ while (xml::XmlPullParser::NextChildNode(parser, depth)) {
+ if (parser->event() == xml::XmlPullParser::Event::kComment) {
+ comment = util::TrimWhitespace(parser->comment()).ToString();
+ continue;
+ } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text.
+ continue;
+ }
+
+ const Source item_source = source_.WithLine(parser->line_number());
+ const std::string& element_namespace = parser->element_namespace();
+ const std::string& element_name = parser->element_name();
+ if (element_namespace.empty() && element_name == "public") {
+ Maybe<StringPiece> maybe_name =
+ xml::FindNonEmptyAttribute(parser, "name");
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(item_source)
+ << "<public> must have a 'name' attribute");
+ error = true;
+ continue;
+ }
+
+ if (xml::FindNonEmptyAttribute(parser, "id")) {
+ diag_->Error(DiagMessage(item_source)
+ << "'id' is ignored within <public-group>");
+ error = true;
+ continue;
+ }
+
+ if (xml::FindNonEmptyAttribute(parser, "type")) {
+ diag_->Error(DiagMessage(item_source)
+ << "'type' is ignored within <public-group>");
+ error = true;
+ continue;
+ }
+
+ ParsedResource child_resource;
+ child_resource.name.type = *parsed_type;
+ child_resource.name.entry = maybe_name.value().ToString();
+ child_resource.id = next_id;
+ child_resource.comment = std::move(comment);
+ child_resource.source = item_source;
+ child_resource.symbol_state = SymbolState::kPublic;
+ out_resource->child_resources.push_back(std::move(child_resource));
+
+ next_id.id += 1;
+
+ } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
+ diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
+ error = true;
+ }
+ }
+ return !error;
}
-bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
- bool weak) {
- outResource->name.type = ResourceType::kAttr;
+bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ if (!maybe_type) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << parser->element_name()
+ << "> must have a 'type' attribute");
+ return false;
+ }
- // Attributes only end up in default configuration.
- if (outResource->config != ConfigDescription::defaultConfig()) {
- mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
- << outResource->config << "' for attribute " << outResource->name);
- outResource->config = ConfigDescription::defaultConfig();
- }
+ const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+ if (!parsed_type) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "invalid resource type '" << maybe_type.value() << "' in <"
+ << parser->element_name() << ">");
+ return false;
+ }
- uint32_t typeMask = 0;
+ out_resource->name.type = *parsed_type;
+ return true;
+}
- Maybe<StringPiece> maybeFormat = xml::findAttribute(parser, "format");
- if (maybeFormat) {
- typeMask = parseFormatAttribute(maybeFormat.value());
- if (typeMask == 0) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "invalid attribute format '" << maybeFormat.value() << "'");
- return false;
- }
- }
-
- Maybe<int32_t> maybeMin, maybeMax;
-
- if (Maybe<StringPiece> maybeMinStr = xml::findAttribute(parser, "min")) {
- StringPiece minStr = util::trimWhitespace(maybeMinStr.value());
- if (!minStr.empty()) {
- std::u16string minStr16 = util::utf8ToUtf16(minStr);
- android::Res_value value;
- if (android::ResTable::stringToInt(minStr16.data(), minStr16.size(), &value)) {
- maybeMin = static_cast<int32_t>(value.data);
- }
- }
-
- if (!maybeMin) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "invalid 'min' value '" << minStr << "'");
- return false;
- }
- }
-
- if (Maybe<StringPiece> maybeMaxStr = xml::findAttribute(parser, "max")) {
- StringPiece maxStr = util::trimWhitespace(maybeMaxStr.value());
- if (!maxStr.empty()) {
- std::u16string maxStr16 = util::utf8ToUtf16(maxStr);
- android::Res_value value;
- if (android::ResTable::stringToInt(maxStr16.data(), maxStr16.size(), &value)) {
- maybeMax = static_cast<int32_t>(value.data);
- }
- }
-
- if (!maybeMax) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "invalid 'max' value '" << maxStr << "'");
- return false;
- }
- }
-
- if ((maybeMin || maybeMax) && (typeMask & android::ResTable_map::TYPE_INTEGER) == 0) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "'min' and 'max' can only be used when format='integer'");
- return false;
- }
-
- struct SymbolComparator {
- bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) {
- return a.symbol.name.value() < b.symbol.name.value();
- }
- };
-
- std::set<Attribute::Symbol, SymbolComparator> items;
-
- std::string comment;
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
- comment = util::trimWhitespace(parser->getComment()).toString();
- continue;
- } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip text.
- continue;
- }
-
- const Source itemSource = mSource.withLine(parser->getLineNumber());
- const std::string& elementNamespace = parser->getElementNamespace();
- const std::string& elementName = parser->getElementName();
- if (elementNamespace.empty() && (elementName == "flag" || elementName == "enum")) {
- if (elementName == "enum") {
- if (typeMask & android::ResTable_map::TYPE_FLAGS) {
- mDiag->error(DiagMessage(itemSource)
- << "can not define an <enum>; already defined a <flag>");
- error = true;
- continue;
- }
- typeMask |= android::ResTable_map::TYPE_ENUM;
-
- } else if (elementName == "flag") {
- if (typeMask & android::ResTable_map::TYPE_ENUM) {
- mDiag->error(DiagMessage(itemSource)
- << "can not define a <flag>; already defined an <enum>");
- error = true;
- continue;
- }
- typeMask |= android::ResTable_map::TYPE_FLAGS;
- }
-
- if (Maybe<Attribute::Symbol> s = parseEnumOrFlagItem(parser, elementName)) {
- Attribute::Symbol& symbol = s.value();
- ParsedResource childResource;
- childResource.name = symbol.symbol.name.value();
- childResource.source = itemSource;
- childResource.value = util::make_unique<Id>();
- outResource->childResources.push_back(std::move(childResource));
-
- symbol.symbol.setComment(std::move(comment));
- symbol.symbol.setSource(itemSource);
-
- auto insertResult = items.insert(std::move(symbol));
- if (!insertResult.second) {
- const Attribute::Symbol& existingSymbol = *insertResult.first;
- mDiag->error(DiagMessage(itemSource)
- << "duplicate symbol '" << existingSymbol.symbol.name.value().entry
- << "'");
-
- mDiag->note(DiagMessage(existingSymbol.symbol.getSource())
- << "first defined here");
- error = true;
- }
- } else {
- error = true;
- }
- } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
- mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
- error = true;
- }
-
- comment = {};
- }
-
- if (error) {
- return false;
- }
-
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
- attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
- attr->typeMask = typeMask ? typeMask : uint32_t(android::ResTable_map::TYPE_ANY);
- if (maybeMin) {
- attr->minInt = maybeMin.value();
- }
-
- if (maybeMax) {
- attr->maxInt = maybeMax.value();
- }
- outResource->value = std::move(attr);
+bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ if (ParseSymbolImpl(parser, out_resource)) {
+ out_resource->symbol_state = SymbolState::kPrivate;
return true;
+ }
+ return false;
}
-Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(xml::XmlPullParser* parser,
- const StringPiece& tag) {
- const Source source = mSource.withLine(parser->getLineNumber());
-
- Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
- if (!maybeName) {
- mDiag->error(DiagMessage(source) << "no attribute 'name' found for tag <" << tag << ">");
- return {};
- }
-
- Maybe<StringPiece> maybeValue = xml::findNonEmptyAttribute(parser, "value");
- if (!maybeValue) {
- mDiag->error(DiagMessage(source) << "no attribute 'value' found for tag <" << tag << ">");
- return {};
- }
-
- std::u16string value16 = util::utf8ToUtf16(maybeValue.value());
- android::Res_value val;
- if (!android::ResTable::stringToInt(value16.data(), value16.size(), &val)) {
- mDiag->error(DiagMessage(source) << "invalid value '" << maybeValue.value()
- << "' for <" << tag << ">; must be an integer");
- return {};
- }
-
- return Attribute::Symbol{
- Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())), val.data };
-}
-
-bool ResourceParser::parseStyleItem(xml::XmlPullParser* parser, Style* style) {
- const Source source = mSource.withLine(parser->getLineNumber());
-
- Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
- if (!maybeName) {
- mDiag->error(DiagMessage(source) << "<item> must have a 'name' attribute");
- return false;
- }
-
- Maybe<Reference> maybeKey = ResourceUtils::parseXmlAttributeName(maybeName.value());
- if (!maybeKey) {
- mDiag->error(DiagMessage(source) << "invalid attribute name '" << maybeName.value() << "'");
- return false;
- }
-
- transformReferenceFromNamespace(parser, "", &maybeKey.value());
- maybeKey.value().setSource(source);
-
- std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
- if (!value) {
- mDiag->error(DiagMessage(source) << "could not parse style item");
- return false;
- }
-
- style->entries.push_back(Style::Entry{ std::move(maybeKey.value()), std::move(value) });
+bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ if (ParseSymbolImpl(parser, out_resource)) {
+ out_resource->symbol_state = SymbolState::kUndefined;
return true;
+ }
+ return false;
}
-bool ResourceParser::parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource) {
- outResource->name.type = ResourceType::kStyle;
+bool ResourceParser::ParseAttr(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ return ParseAttrImpl(parser, out_resource, false);
+}
- std::unique_ptr<Style> style = util::make_unique<Style>();
+bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
+ ParsedResource* out_resource, bool weak) {
+ out_resource->name.type = ResourceType::kAttr;
- Maybe<StringPiece> maybeParent = xml::findAttribute(parser, "parent");
- if (maybeParent) {
- // If the parent is empty, we don't have a parent, but we also don't infer either.
- if (!maybeParent.value().empty()) {
- std::string errStr;
- style->parent = ResourceUtils::parseStyleParentReference(maybeParent.value(), &errStr);
- if (!style->parent) {
- mDiag->error(DiagMessage(outResource->source) << errStr);
- return false;
- }
+ // Attributes only end up in default configuration.
+ if (out_resource->config != ConfigDescription::DefaultConfig()) {
+ diag_->Warn(DiagMessage(out_resource->source)
+ << "ignoring configuration '" << out_resource->config
+ << "' for attribute " << out_resource->name);
+ out_resource->config = ConfigDescription::DefaultConfig();
+ }
- // Transform the namespace prefix to the actual package name, and mark the reference as
- // private if appropriate.
- transformReferenceFromNamespace(parser, "", &style->parent.value());
- }
+ uint32_t type_mask = 0;
- } else {
- // No parent was specified, so try inferring it from the style name.
- std::string styleName = outResource->name.entry;
- size_t pos = styleName.find_last_of(u'.');
- if (pos != std::string::npos) {
- style->parentInferred = true;
- style->parent = Reference(ResourceName({}, ResourceType::kStyle,
- styleName.substr(0, pos)));
- }
+ Maybe<StringPiece> maybe_format = xml::FindAttribute(parser, "format");
+ if (maybe_format) {
+ type_mask = ParseFormatAttribute(maybe_format.value());
+ if (type_mask == 0) {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << "invalid attribute format '" << maybe_format.value()
+ << "'");
+ return false;
+ }
+ }
+
+ Maybe<int32_t> maybe_min, maybe_max;
+
+ if (Maybe<StringPiece> maybe_min_str = xml::FindAttribute(parser, "min")) {
+ StringPiece min_str = util::TrimWhitespace(maybe_min_str.value());
+ if (!min_str.empty()) {
+ std::u16string min_str16 = util::Utf8ToUtf16(min_str);
+ android::Res_value value;
+ if (android::ResTable::stringToInt(min_str16.data(), min_str16.size(),
+ &value)) {
+ maybe_min = static_cast<int32_t>(value.data);
+ }
}
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip text and comments.
- continue;
- }
+ if (!maybe_min) {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << "invalid 'min' value '" << min_str << "'");
+ return false;
+ }
+ }
- const std::string& elementNamespace = parser->getElementNamespace();
- const std::string& elementName = parser->getElementName();
- if (elementNamespace == "" && elementName == "item") {
- error |= !parseStyleItem(parser, style.get());
-
- } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << ":" << elementName << ">");
- error = true;
- }
+ if (Maybe<StringPiece> maybe_max_str = xml::FindAttribute(parser, "max")) {
+ StringPiece max_str = util::TrimWhitespace(maybe_max_str.value());
+ if (!max_str.empty()) {
+ std::u16string max_str16 = util::Utf8ToUtf16(max_str);
+ android::Res_value value;
+ if (android::ResTable::stringToInt(max_str16.data(), max_str16.size(),
+ &value)) {
+ maybe_max = static_cast<int32_t>(value.data);
+ }
}
- if (error) {
+ if (!maybe_max) {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << "invalid 'max' value '" << max_str << "'");
+ return false;
+ }
+ }
+
+ if ((maybe_min || maybe_max) &&
+ (type_mask & android::ResTable_map::TYPE_INTEGER) == 0) {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << "'min' and 'max' can only be used when format='integer'");
+ return false;
+ }
+
+ struct SymbolComparator {
+ bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) {
+ return a.symbol.name.value() < b.symbol.name.value();
+ }
+ };
+
+ std::set<Attribute::Symbol, SymbolComparator> items;
+
+ std::string comment;
+ bool error = false;
+ const size_t depth = parser->depth();
+ while (xml::XmlPullParser::NextChildNode(parser, depth)) {
+ if (parser->event() == xml::XmlPullParser::Event::kComment) {
+ comment = util::TrimWhitespace(parser->comment()).ToString();
+ continue;
+ } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text.
+ continue;
+ }
+
+ const Source item_source = source_.WithLine(parser->line_number());
+ const std::string& element_namespace = parser->element_namespace();
+ const std::string& element_name = parser->element_name();
+ if (element_namespace.empty() &&
+ (element_name == "flag" || element_name == "enum")) {
+ if (element_name == "enum") {
+ if (type_mask & android::ResTable_map::TYPE_FLAGS) {
+ diag_->Error(DiagMessage(item_source)
+ << "can not define an <enum>; already defined a <flag>");
+ error = true;
+ continue;
+ }
+ type_mask |= android::ResTable_map::TYPE_ENUM;
+
+ } else if (element_name == "flag") {
+ if (type_mask & android::ResTable_map::TYPE_ENUM) {
+ diag_->Error(DiagMessage(item_source)
+ << "can not define a <flag>; already defined an <enum>");
+ error = true;
+ continue;
+ }
+ type_mask |= android::ResTable_map::TYPE_FLAGS;
+ }
+
+ if (Maybe<Attribute::Symbol> s =
+ ParseEnumOrFlagItem(parser, element_name)) {
+ Attribute::Symbol& symbol = s.value();
+ ParsedResource child_resource;
+ child_resource.name = symbol.symbol.name.value();
+ child_resource.source = item_source;
+ child_resource.value = util::make_unique<Id>();
+ out_resource->child_resources.push_back(std::move(child_resource));
+
+ symbol.symbol.SetComment(std::move(comment));
+ symbol.symbol.SetSource(item_source);
+
+ auto insert_result = items.insert(std::move(symbol));
+ if (!insert_result.second) {
+ const Attribute::Symbol& existing_symbol = *insert_result.first;
+ diag_->Error(DiagMessage(item_source)
+ << "duplicate symbol '"
+ << existing_symbol.symbol.name.value().entry << "'");
+
+ diag_->Note(DiagMessage(existing_symbol.symbol.GetSource())
+ << "first defined here");
+ error = true;
+ }
+ } else {
+ error = true;
+ }
+ } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
+ diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
+ error = true;
+ }
+
+ comment = {};
+ }
+
+ if (error) {
+ return false;
+ }
+
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
+ attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
+ attr->type_mask =
+ type_mask ? type_mask : uint32_t(android::ResTable_map::TYPE_ANY);
+ if (maybe_min) {
+ attr->min_int = maybe_min.value();
+ }
+
+ if (maybe_max) {
+ attr->max_int = maybe_max.value();
+ }
+ out_resource->value = std::move(attr);
+ return true;
+}
+
+Maybe<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(
+ xml::XmlPullParser* parser, const StringPiece& tag) {
+ const Source source = source_.WithLine(parser->line_number());
+
+ Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(source) << "no attribute 'name' found for tag <"
+ << tag << ">");
+ return {};
+ }
+
+ Maybe<StringPiece> maybe_value = xml::FindNonEmptyAttribute(parser, "value");
+ if (!maybe_value) {
+ diag_->Error(DiagMessage(source) << "no attribute 'value' found for tag <"
+ << tag << ">");
+ return {};
+ }
+
+ std::u16string value16 = util::Utf8ToUtf16(maybe_value.value());
+ android::Res_value val;
+ if (!android::ResTable::stringToInt(value16.data(), value16.size(), &val)) {
+ diag_->Error(DiagMessage(source) << "invalid value '" << maybe_value.value()
+ << "' for <" << tag
+ << ">; must be an integer");
+ return {};
+ }
+
+ return Attribute::Symbol{
+ Reference(ResourceNameRef({}, ResourceType::kId, maybe_name.value())),
+ val.data};
+}
+
+bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
+ const Source source = source_.WithLine(parser->line_number());
+
+ Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(source) << "<item> must have a 'name' attribute");
+ return false;
+ }
+
+ Maybe<Reference> maybe_key =
+ ResourceUtils::ParseXmlAttributeName(maybe_name.value());
+ if (!maybe_key) {
+ diag_->Error(DiagMessage(source) << "invalid attribute name '"
+ << maybe_name.value() << "'");
+ return false;
+ }
+
+ TransformReferenceFromNamespace(parser, "", &maybe_key.value());
+ maybe_key.value().SetSource(source);
+
+ std::unique_ptr<Item> value = ParseXml(parser, 0, kAllowRawString);
+ if (!value) {
+ diag_->Error(DiagMessage(source) << "could not parse style item");
+ return false;
+ }
+
+ style->entries.push_back(
+ Style::Entry{std::move(maybe_key.value()), std::move(value)});
+ return true;
+}
+
+bool ResourceParser::ParseStyle(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ out_resource->name.type = ResourceType::kStyle;
+
+ std::unique_ptr<Style> style = util::make_unique<Style>();
+
+ Maybe<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
+ if (maybe_parent) {
+ // If the parent is empty, we don't have a parent, but we also don't infer
+ // either.
+ if (!maybe_parent.value().empty()) {
+ std::string err_str;
+ style->parent = ResourceUtils::ParseStyleParentReference(
+ maybe_parent.value(), &err_str);
+ if (!style->parent) {
+ diag_->Error(DiagMessage(out_resource->source) << err_str);
return false;
+ }
+
+ // Transform the namespace prefix to the actual package name, and mark the
+ // reference as
+ // private if appropriate.
+ TransformReferenceFromNamespace(parser, "", &style->parent.value());
}
- outResource->value = std::move(style);
- return true;
+ } else {
+ // No parent was specified, so try inferring it from the style name.
+ std::string style_name = out_resource->name.entry;
+ size_t pos = style_name.find_last_of(u'.');
+ if (pos != std::string::npos) {
+ style->parent_inferred = true;
+ style->parent = Reference(
+ ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
+ }
+ }
+
+ bool error = false;
+ const size_t depth = parser->depth();
+ while (xml::XmlPullParser::NextChildNode(parser, depth)) {
+ if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text and comments.
+ continue;
+ }
+
+ const std::string& element_namespace = parser->element_namespace();
+ const std::string& element_name = parser->element_name();
+ if (element_namespace == "" && element_name == "item") {
+ error |= !ParseStyleItem(parser, style.get());
+
+ } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << ":" << element_name << ">");
+ error = true;
+ }
+ }
+
+ if (error) {
+ return false;
+ }
+
+ out_resource->value = std::move(style);
+ return true;
}
-bool ResourceParser::parseArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
- return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_ANY);
+bool ResourceParser::ParseArray(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ return ParseArrayImpl(parser, out_resource, android::ResTable_map::TYPE_ANY);
}
-bool ResourceParser::parseIntegerArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
- return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_INTEGER);
+bool ResourceParser::ParseIntegerArray(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ return ParseArrayImpl(parser, out_resource,
+ android::ResTable_map::TYPE_INTEGER);
}
-bool ResourceParser::parseStringArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
- return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_STRING);
+bool ResourceParser::ParseStringArray(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ return ParseArrayImpl(parser, out_resource,
+ android::ResTable_map::TYPE_STRING);
}
-bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
+bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
+ ParsedResource* out_resource,
const uint32_t typeMask) {
- outResource->name.type = ResourceType::kArray;
+ out_resource->name.type = ResourceType::kArray;
- std::unique_ptr<Array> array = util::make_unique<Array>();
+ std::unique_ptr<Array> array = util::make_unique<Array>();
- bool translateable = mOptions.translatable;
- if (Maybe<StringPiece> translateableAttr = xml::findAttribute(parser, "translatable")) {
- 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();
+ bool translateable = options_.translatable;
+ if (Maybe<StringPiece> translateable_attr =
+ xml::FindAttribute(parser, "translatable")) {
+ Maybe<bool> maybe_translateable =
+ ResourceUtils::ParseBool(translateable_attr.value());
+ if (!maybe_translateable) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "invalid value for 'translatable'. Must be a boolean");
+ return false;
}
- array->setTranslateable(translateable);
+ translateable = maybe_translateable.value();
+ }
+ array->SetTranslateable(translateable);
-
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip text and comments.
- continue;
- }
-
- const Source itemSource = mSource.withLine(parser->getLineNumber());
- const std::string& elementNamespace = parser->getElementNamespace();
- const std::string& elementName = parser->getElementName();
- if (elementNamespace.empty() && elementName == "item") {
- std::unique_ptr<Item> item = parseXml(parser, typeMask, kNoRawString);
- if (!item) {
- mDiag->error(DiagMessage(itemSource) << "could not parse array item");
- error = true;
- continue;
- }
- item->setSource(itemSource);
- array->items.emplace_back(std::move(item));
-
- } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "unknown tag <" << elementNamespace << ":" << elementName << ">");
- error = true;
- }
+ bool error = false;
+ const size_t depth = parser->depth();
+ while (xml::XmlPullParser::NextChildNode(parser, depth)) {
+ if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text and comments.
+ continue;
}
- if (error) {
- return false;
- }
+ const Source item_source = source_.WithLine(parser->line_number());
+ const std::string& element_namespace = parser->element_namespace();
+ const std::string& element_name = parser->element_name();
+ if (element_namespace.empty() && element_name == "item") {
+ std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString);
+ if (!item) {
+ diag_->Error(DiagMessage(item_source) << "could not parse array item");
+ error = true;
+ continue;
+ }
+ item->SetSource(item_source);
+ array->items.emplace_back(std::move(item));
- outResource->value = std::move(array);
- return true;
+ } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
+ diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
+ << "unknown tag <" << element_namespace << ":"
+ << element_name << ">");
+ error = true;
+ }
+ }
+
+ if (error) {
+ return false;
+ }
+
+ out_resource->value = std::move(array);
+ return true;
}
-bool ResourceParser::parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource) {
- outResource->name.type = ResourceType::kPlurals;
+bool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ out_resource->name.type = ResourceType::kPlurals;
- std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip text and comments.
- continue;
- }
-
- const Source itemSource = mSource.withLine(parser->getLineNumber());
- const std::string& elementNamespace = parser->getElementNamespace();
- const std::string& elementName = parser->getElementName();
- if (elementNamespace.empty() && elementName == "item") {
- Maybe<StringPiece> maybeQuantity = xml::findNonEmptyAttribute(parser, "quantity");
- if (!maybeQuantity) {
- mDiag->error(DiagMessage(itemSource) << "<item> in <plurals> requires attribute "
- << "'quantity'");
- error = true;
- continue;
- }
-
- StringPiece trimmedQuantity = util::trimWhitespace(maybeQuantity.value());
- size_t index = 0;
- if (trimmedQuantity == "zero") {
- index = Plural::Zero;
- } else if (trimmedQuantity == "one") {
- index = Plural::One;
- } else if (trimmedQuantity == "two") {
- index = Plural::Two;
- } else if (trimmedQuantity == "few") {
- index = Plural::Few;
- } else if (trimmedQuantity == "many") {
- index = Plural::Many;
- } else if (trimmedQuantity == "other") {
- index = Plural::Other;
- } else {
- mDiag->error(DiagMessage(itemSource)
- << "<item> in <plural> has invalid value '" << trimmedQuantity
- << "' for attribute 'quantity'");
- error = true;
- continue;
- }
-
- if (plural->values[index]) {
- mDiag->error(DiagMessage(itemSource)
- << "duplicate quantity '" << trimmedQuantity << "'");
- error = true;
- continue;
- }
-
- if (!(plural->values[index] = parseXml(parser, android::ResTable_map::TYPE_STRING,
- kNoRawString))) {
- error = true;
- }
- plural->values[index]->setSource(itemSource);
-
- } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
- mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
- << elementName << ">");
- error = true;
- }
+ bool error = false;
+ const size_t depth = parser->depth();
+ while (xml::XmlPullParser::NextChildNode(parser, depth)) {
+ if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text and comments.
+ continue;
}
- if (error) {
- return false;
- }
+ const Source item_source = source_.WithLine(parser->line_number());
+ const std::string& element_namespace = parser->element_namespace();
+ const std::string& element_name = parser->element_name();
+ if (element_namespace.empty() && element_name == "item") {
+ Maybe<StringPiece> maybe_quantity =
+ xml::FindNonEmptyAttribute(parser, "quantity");
+ if (!maybe_quantity) {
+ diag_->Error(DiagMessage(item_source)
+ << "<item> in <plurals> requires attribute "
+ << "'quantity'");
+ error = true;
+ continue;
+ }
- outResource->value = std::move(plural);
- return true;
+ StringPiece trimmed_quantity =
+ util::TrimWhitespace(maybe_quantity.value());
+ size_t index = 0;
+ if (trimmed_quantity == "zero") {
+ index = Plural::Zero;
+ } else if (trimmed_quantity == "one") {
+ index = Plural::One;
+ } else if (trimmed_quantity == "two") {
+ index = Plural::Two;
+ } else if (trimmed_quantity == "few") {
+ index = Plural::Few;
+ } else if (trimmed_quantity == "many") {
+ index = Plural::Many;
+ } else if (trimmed_quantity == "other") {
+ index = Plural::Other;
+ } else {
+ diag_->Error(DiagMessage(item_source)
+ << "<item> in <plural> has invalid value '"
+ << trimmed_quantity << "' for attribute 'quantity'");
+ error = true;
+ continue;
+ }
+
+ if (plural->values[index]) {
+ diag_->Error(DiagMessage(item_source) << "duplicate quantity '"
+ << trimmed_quantity << "'");
+ error = true;
+ continue;
+ }
+
+ if (!(plural->values[index] = ParseXml(
+ parser, android::ResTable_map::TYPE_STRING, kNoRawString))) {
+ error = true;
+ }
+ plural->values[index]->SetSource(item_source);
+
+ } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
+ diag_->Error(DiagMessage(item_source) << "unknown tag <"
+ << element_namespace << ":"
+ << element_name << ">");
+ error = true;
+ }
+ }
+
+ if (error) {
+ return false;
+ }
+
+ out_resource->value = std::move(plural);
+ return true;
}
-bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser,
- ParsedResource* outResource) {
- outResource->name.type = ResourceType::kStyleable;
+bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ out_resource->name.type = ResourceType::kStyleable;
- // Declare-styleable is kPrivate by default, because it technically only exists in R.java.
- outResource->symbolState = SymbolState::kPublic;
+ // Declare-styleable is kPrivate by default, because it technically only
+ // exists in R.java.
+ out_resource->symbol_state = SymbolState::kPublic;
- // Declare-styleable only ends up in default config;
- if (outResource->config != ConfigDescription::defaultConfig()) {
- mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
- << outResource->config << "' for styleable "
- << outResource->name.entry);
- outResource->config = ConfigDescription::defaultConfig();
+ // Declare-styleable only ends up in default config;
+ if (out_resource->config != ConfigDescription::DefaultConfig()) {
+ diag_->Warn(DiagMessage(out_resource->source)
+ << "ignoring configuration '" << out_resource->config
+ << "' for styleable " << out_resource->name.entry);
+ out_resource->config = ConfigDescription::DefaultConfig();
+ }
+
+ std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+
+ std::string comment;
+ bool error = false;
+ const size_t depth = parser->depth();
+ while (xml::XmlPullParser::NextChildNode(parser, depth)) {
+ if (parser->event() == xml::XmlPullParser::Event::kComment) {
+ comment = util::TrimWhitespace(parser->comment()).ToString();
+ continue;
+ } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
+ // Ignore text.
+ continue;
}
- std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+ const Source item_source = source_.WithLine(parser->line_number());
+ const std::string& element_namespace = parser->element_namespace();
+ const std::string& element_name = parser->element_name();
+ if (element_namespace.empty() && element_name == "attr") {
+ Maybe<StringPiece> maybe_name =
+ xml::FindNonEmptyAttribute(parser, "name");
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(item_source)
+ << "<attr> tag must have a 'name' attribute");
+ error = true;
+ continue;
+ }
- std::string comment;
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
- comment = util::trimWhitespace(parser->getComment()).toString();
- continue;
- } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Ignore text.
- continue;
- }
+ // If this is a declaration, the package name may be in the name. Separate
+ // these out.
+ // Eg. <attr name="android:text" />
+ Maybe<Reference> maybe_ref =
+ ResourceUtils::ParseXmlAttributeName(maybe_name.value());
+ if (!maybe_ref) {
+ diag_->Error(DiagMessage(item_source) << "<attr> tag has invalid name '"
+ << maybe_name.value() << "'");
+ error = true;
+ continue;
+ }
- const Source itemSource = mSource.withLine(parser->getLineNumber());
- const std::string& elementNamespace = parser->getElementNamespace();
- const std::string& elementName = parser->getElementName();
- if (elementNamespace.empty() && elementName == "attr") {
- Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
- if (!maybeName) {
- mDiag->error(DiagMessage(itemSource) << "<attr> tag must have a 'name' attribute");
- error = true;
- continue;
- }
+ Reference& child_ref = maybe_ref.value();
+ xml::TransformReferenceFromNamespace(parser, "", &child_ref);
- // If this is a declaration, the package name may be in the name. Separate these out.
- // Eg. <attr name="android:text" />
- Maybe<Reference> maybeRef = ResourceUtils::parseXmlAttributeName(maybeName.value());
- if (!maybeRef) {
- mDiag->error(DiagMessage(itemSource) << "<attr> tag has invalid name '"
- << maybeName.value() << "'");
- error = true;
- continue;
- }
+ // Create the ParsedResource that will add the attribute to the table.
+ ParsedResource child_resource;
+ child_resource.name = child_ref.name.value();
+ child_resource.source = item_source;
+ child_resource.comment = std::move(comment);
- Reference& childRef = maybeRef.value();
- xml::transformReferenceFromNamespace(parser, "", &childRef);
+ if (!ParseAttrImpl(parser, &child_resource, true)) {
+ error = true;
+ continue;
+ }
- // Create the ParsedResource that will add the attribute to the table.
- ParsedResource childResource;
- childResource.name = childRef.name.value();
- childResource.source = itemSource;
- childResource.comment = std::move(comment);
+ // Create the reference to this attribute.
+ child_ref.SetComment(child_resource.comment);
+ child_ref.SetSource(item_source);
+ styleable->entries.push_back(std::move(child_ref));
- if (!parseAttrImpl(parser, &childResource, true)) {
- error = true;
- continue;
- }
+ out_resource->child_resources.push_back(std::move(child_resource));
- // Create the reference to this attribute.
- childRef.setComment(childResource.comment);
- childRef.setSource(itemSource);
- styleable->entries.push_back(std::move(childRef));
-
- outResource->childResources.push_back(std::move(childResource));
-
- } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
- mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
- << elementName << ">");
- error = true;
- }
-
- comment = {};
+ } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
+ diag_->Error(DiagMessage(item_source) << "unknown tag <"
+ << element_namespace << ":"
+ << element_name << ">");
+ error = true;
}
- if (error) {
- return false;
- }
+ comment = {};
+ }
- outResource->value = std::move(styleable);
- return true;
+ if (error) {
+ return false;
+ }
+
+ out_resource->value = std::move(styleable);
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index ece3090..11b1e5b 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -17,6 +17,10 @@
#ifndef AAPT_RESOURCE_PARSER_H
#define AAPT_RESOURCE_PARSER_H
+#include <memory>
+
+#include "android-base/macros.h"
+
#include "ConfigDescription.h"
#include "Diagnostics.h"
#include "ResourceTable.h"
@@ -26,86 +30,98 @@
#include "util/StringPiece.h"
#include "xml/XmlPullParser.h"
-#include <memory>
-
namespace aapt {
struct ParsedResource;
struct ResourceParserOptions {
- /**
- * Whether the default setting for this parser is to allow translation.
- */
- bool translatable = true;
+ /**
+ * Whether the default setting for this parser is to allow translation.
+ */
+ bool translatable = true;
- /**
- * Whether positional arguments in formatted strings are treated as errors or warnings.
- */
- bool errorOnPositionalArguments = true;
+ /**
+ * Whether positional arguments in formatted strings are treated as errors or
+ * warnings.
+ */
+ bool error_on_positional_arguments = true;
};
/*
* Parses an XML file for resources and adds them to a ResourceTable.
*/
class ResourceParser {
-public:
- ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
- const ConfigDescription& config, const ResourceParserOptions& options = {});
+ public:
+ ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
+ const ConfigDescription& config,
+ const ResourceParserOptions& options = {});
+ bool Parse(xml::XmlPullParser* parser);
- ResourceParser(const ResourceParser&) = delete; // No copy.
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceParser);
- bool parse(xml::XmlPullParser* parser);
+ /*
+ * Parses the XML subtree as a StyleString (flattened XML representation for
+ * strings
+ * with formatting). If successful, `out_style_string`
+ * contains the escaped and whitespace trimmed text, while `out_raw_string`
+ * contains the unescaped text. Returns true on success.
+ */
+ bool FlattenXmlSubtree(xml::XmlPullParser* parser,
+ std::string* out_raw_string,
+ StyleString* out_style_string);
-private:
- /*
- * Parses the XML subtree as a StyleString (flattened XML representation for strings
- * with formatting). If successful, `outStyleString`
- * contains the escaped and whitespace trimmed text, while `outRawString`
- * contains the unescaped text. Returns true on success.
- */
- bool flattenXmlSubtree(xml::XmlPullParser* parser, std::string* outRawString,
- StyleString* outStyleString);
+ /*
+ * Parses the XML subtree and returns an Item.
+ * The type of Item that can be parsed is denoted by the `type_mask`.
+ * If `allow_raw_value` is true and the subtree can not be parsed as a regular
+ * Item, then a
+ * RawString is returned. Otherwise this returns false;
+ */
+ std::unique_ptr<Item> ParseXml(xml::XmlPullParser* parser,
+ const uint32_t type_mask,
+ const bool allow_raw_value);
- /*
- * Parses the XML subtree and returns an Item.
- * The type of Item that can be parsed is denoted by the `typeMask`.
- * If `allowRawValue` is true and the subtree can not be parsed as a regular Item, then a
- * RawString is returned. Otherwise this returns false;
- */
- std::unique_ptr<Item> parseXml(xml::XmlPullParser* parser, const uint32_t typeMask,
- const bool allowRawValue);
+ bool ParseResources(xml::XmlPullParser* parser);
+ bool ParseResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
- bool parseResources(xml::XmlPullParser* parser);
- bool parseResource(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource,
+ uint32_t format);
+ bool ParseString(xml::XmlPullParser* parser, ParsedResource* out_resource);
- bool parseItem(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t format);
- bool parseString(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParsePublicGroup(xml::XmlPullParser* parser,
+ ParsedResource* out_resource);
+ bool ParseSymbolImpl(xml::XmlPullParser* parser,
+ ParsedResource* out_resource);
+ bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseAddResource(xml::XmlPullParser* parser,
+ ParsedResource* out_resource);
+ bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
+ bool weak);
+ Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
+ const StringPiece& tag);
+ bool ParseStyle(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
+ bool ParseDeclareStyleable(xml::XmlPullParser* parser,
+ ParsedResource* out_resource);
+ bool ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseIntegerArray(xml::XmlPullParser* parser,
+ ParsedResource* out_resource);
+ bool ParseStringArray(xml::XmlPullParser* parser,
+ ParsedResource* out_resource);
+ bool ParseArrayImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
+ uint32_t typeMask);
+ bool ParsePlural(xml::XmlPullParser* parser, ParsedResource* out_resource);
- bool parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseAddResource(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource, bool weak);
- Maybe<Attribute::Symbol> parseEnumOrFlagItem(xml::XmlPullParser* parser,
- const StringPiece& tag);
- bool parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseStyleItem(xml::XmlPullParser* parser, Style* style);
- bool parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseArray(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseIntegerArray(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseStringArray(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
- bool parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource);
-
- IDiagnostics* mDiag;
- ResourceTable* mTable;
- Source mSource;
- ConfigDescription mConfig;
- ResourceParserOptions mOptions;
+ IDiagnostics* diag_;
+ ResourceTable* table_;
+ Source source_;
+ ConfigDescription config_;
+ ResourceParserOptions options_;
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_RESOURCE_PARSER_H
+#endif // AAPT_RESOURCE_PARSER_H
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index e097740..2463911 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -15,523 +15,573 @@
*/
#include "ResourceParser.h"
+
+#include <sstream>
+#include <string>
+
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "test/Test.h"
#include "xml/XmlPullParser.h"
-#include <sstream>
-#include <string>
-
namespace aapt {
-constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+constexpr const char* kXmlPreamble =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::stringstream input(kXmlPreamble);
- input << "<attr name=\"foo\"/>" << std::endl;
- ResourceTable table;
- ResourceParser parser(context->getDiagnostics(), &table, Source{ "test" }, {});
- xml::XmlPullParser xmlParser(input);
- ASSERT_FALSE(parser.parse(&xmlParser));
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::stringstream input(kXmlPreamble);
+ input << "<attr name=\"foo\"/>" << std::endl;
+ ResourceTable table;
+ ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {});
+ xml::XmlPullParser xml_parser(input);
+ ASSERT_FALSE(parser.Parse(&xml_parser));
}
-struct ResourceParserTest : public ::testing::Test {
- ResourceTable mTable;
- std::unique_ptr<IAaptContext> mContext;
+class ResourceParserTest : public ::testing::Test {
+ public:
+ void SetUp() override { context_ = test::ContextBuilder().Build(); }
- void SetUp() override {
- mContext = test::ContextBuilder().build();
- }
+ ::testing::AssertionResult TestParse(const StringPiece& str) {
+ return TestParse(str, ConfigDescription{});
+ }
- ::testing::AssertionResult testParse(const StringPiece& str) {
- return testParse(str, ConfigDescription{});
+ ::testing::AssertionResult TestParse(const StringPiece& str,
+ const ConfigDescription& config) {
+ std::stringstream input(kXmlPreamble);
+ input << "<resources>\n" << str << "\n</resources>" << std::endl;
+ ResourceParserOptions parserOptions;
+ ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"},
+ config, parserOptions);
+ xml::XmlPullParser xmlParser(input);
+ if (parser.Parse(&xmlParser)) {
+ return ::testing::AssertionSuccess();
}
+ return ::testing::AssertionFailure();
+ }
- ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config) {
- std::stringstream input(kXmlPreamble);
- input << "<resources>\n" << str << "\n</resources>" << std::endl;
- ResourceParserOptions parserOptions;
- ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, config,
- parserOptions);
- xml::XmlPullParser xmlParser(input);
- if (parser.parse(&xmlParser)) {
- return ::testing::AssertionSuccess();
- }
- return ::testing::AssertionFailure();
- }
+ protected:
+ ResourceTable table_;
+ std::unique_ptr<IAaptContext> context_;
};
TEST_F(ResourceParserTest, ParseQuotedString) {
- std::string input = "<string name=\"foo\"> \" hey there \" </string>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<string name=\"foo\"> \" hey there \" </string>";
+ ASSERT_TRUE(TestParse(input));
- String* str = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string(" hey there "), *str->value);
+ String* str = test::GetValue<String>(&table_, "string/foo");
+ ASSERT_NE(nullptr, str);
+ EXPECT_EQ(std::string(" hey there "), *str->value);
}
TEST_F(ResourceParserTest, ParseEscapedString) {
- std::string input = "<string name=\"foo\">\\?123</string>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<string name=\"foo\">\\?123</string>";
+ ASSERT_TRUE(TestParse(input));
- String* str = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string("?123"), *str->value);
+ String* str = test::GetValue<String>(&table_, "string/foo");
+ ASSERT_NE(nullptr, str);
+ EXPECT_EQ(std::string("?123"), *str->value);
}
TEST_F(ResourceParserTest, ParseFormattedString) {
- std::string input = "<string name=\"foo\">%d %s</string>";
- ASSERT_FALSE(testParse(input));
+ std::string input = "<string name=\"foo\">%d %s</string>";
+ ASSERT_FALSE(TestParse(input));
- input = "<string name=\"foo\">%1$d %2$s</string>";
- ASSERT_TRUE(testParse(input));
+ input = "<string name=\"foo\">%1$d %2$s</string>";
+ ASSERT_TRUE(TestParse(input));
}
TEST_F(ResourceParserTest, ParseStyledString) {
- // Use a surrogate pair unicode point so that we can verify that the span indices
- // use UTF-16 length and not UTF-18 length.
- std::string input = "<string name=\"foo\">This is my aunt\u2019s <b>string</b></string>";
- ASSERT_TRUE(testParse(input));
+ // Use a surrogate pair unicode point so that we can verify that the span
+ // indices
+ // use UTF-16 length and not UTF-18 length.
+ std::string input =
+ "<string name=\"foo\">This is my aunt\u2019s <b>string</b></string>";
+ ASSERT_TRUE(TestParse(input));
- StyledString* str = test::getValue<StyledString>(&mTable, "string/foo");
- ASSERT_NE(nullptr, str);
+ StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
+ ASSERT_NE(nullptr, str);
- const std::string expectedStr = "This is my aunt\u2019s string";
- EXPECT_EQ(expectedStr, *str->value->str);
- EXPECT_EQ(1u, str->value->spans.size());
+ const std::string expected_str = "This is my aunt\u2019s string";
+ EXPECT_EQ(expected_str, *str->value->str);
+ EXPECT_EQ(1u, str->value->spans.size());
- EXPECT_EQ(std::string("b"), *str->value->spans[0].name);
- EXPECT_EQ(17u, str->value->spans[0].firstChar);
- EXPECT_EQ(23u, str->value->spans[0].lastChar);
+ EXPECT_EQ(std::string("b"), *str->value->spans[0].name);
+ EXPECT_EQ(17u, str->value->spans[0].first_char);
+ EXPECT_EQ(23u, str->value->spans[0].last_char);
}
TEST_F(ResourceParserTest, ParseStringWithWhitespace) {
- std::string input = "<string name=\"foo\"> This is what I think </string>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<string name=\"foo\"> This is what I think </string>";
+ ASSERT_TRUE(TestParse(input));
- String* str = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string("This is what I think"), *str->value);
+ String* str = test::GetValue<String>(&table_, "string/foo");
+ ASSERT_NE(nullptr, str);
+ EXPECT_EQ(std::string("This is what I think"), *str->value);
- input = "<string name=\"foo2\">\" This is what I think \"</string>";
- ASSERT_TRUE(testParse(input));
+ input = "<string name=\"foo2\">\" This is what I think \"</string>";
+ ASSERT_TRUE(TestParse(input));
- str = test::getValue<String>(&mTable, "string/foo2");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string(" This is what I think "), *str->value);
+ str = test::GetValue<String>(&table_, "string/foo2");
+ ASSERT_NE(nullptr, str);
+ EXPECT_EQ(std::string(" This is what I think "), *str->value);
}
TEST_F(ResourceParserTest, IgnoreXliffTags) {
- std::string input = "<string name=\"foo\" \n"
- " xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n"
- " There are <xliff:g id=\"count\">%1$d</xliff:g> apples</string>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<string name=\"foo\" \n"
+ " xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n"
+ " There are <xliff:g id=\"count\">%1$d</xliff:g> apples</string>";
+ ASSERT_TRUE(TestParse(input));
- String* str = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value));
+ String* str = test::GetValue<String>(&table_, "string/foo");
+ ASSERT_NE(nullptr, str);
+ EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value));
}
TEST_F(ResourceParserTest, ParseNull) {
- std::string input = "<integer name=\"foo\">@null</integer>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<integer name=\"foo\">@null</integer>";
+ ASSERT_TRUE(TestParse(input));
- // The Android runtime treats a value of android::Res_value::TYPE_NULL as
- // a non-existing value, and this causes problems in styles when trying to resolve
- // an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE
- // with a data value of 0.
- BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, "integer/foo");
- ASSERT_NE(nullptr, integer);
- EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType);
- EXPECT_EQ(0u, integer->value.data);
+ // The Android runtime treats a value of android::Res_value::TYPE_NULL as
+ // a non-existing value, and this causes problems in styles when trying to
+ // resolve
+ // an attribute. Null values must be encoded as
+ // android::Res_value::TYPE_REFERENCE
+ // with a data value of 0.
+ BinaryPrimitive* integer =
+ test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
+ ASSERT_NE(nullptr, integer);
+ EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE),
+ integer->value.dataType);
+ EXPECT_EQ(0u, integer->value.data);
}
TEST_F(ResourceParserTest, ParseEmpty) {
- std::string input = "<integer name=\"foo\">@empty</integer>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<integer name=\"foo\">@empty</integer>";
+ ASSERT_TRUE(TestParse(input));
- BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, "integer/foo");
- ASSERT_NE(nullptr, integer);
- EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
- EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
+ BinaryPrimitive* integer =
+ test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
+ ASSERT_NE(nullptr, integer);
+ EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
+ EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
}
TEST_F(ResourceParserTest, ParseAttr) {
- std::string input = "<attr name=\"foo\" format=\"string\"/>\n"
- "<attr name=\"bar\"/>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<attr name=\"foo\" format=\"string\"/>\n"
+ "<attr name=\"bar\"/>";
+ ASSERT_TRUE(TestParse(input));
- Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
+ Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask);
- attr = test::getValue<Attribute>(&mTable, "attr/bar");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
+ attr = test::GetValue<Attribute>(&table_, "attr/bar");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->type_mask);
}
-// Old AAPT allowed attributes to be defined under different configurations, but ultimately
-// stored them with the default configuration. Check that we have the same behavior.
-TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
- const ConfigDescription watchConfig = test::parseConfigOrDie("watch");
- std::string input = R"EOF(
+// Old AAPT allowed attributes to be defined under different configurations, but
+// ultimately
+// stored them with the default configuration. Check that we have the same
+// behavior.
+TEST_F(ResourceParserTest,
+ ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
+ const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
+ std::string input = R"EOF(
<attr name="foo" />
<declare-styleable name="bar">
<attr name="baz" />
</declare-styleable>)EOF";
- ASSERT_TRUE(testParse(input, watchConfig));
+ ASSERT_TRUE(TestParse(input, watch_config));
- EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, "attr/foo", watchConfig));
- EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, "attr/baz", watchConfig));
- EXPECT_EQ(nullptr, test::getValueForConfig<Styleable>(&mTable, "styleable/bar", watchConfig));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/foo",
+ watch_config));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/baz",
+ watch_config));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<Styleable>(
+ &table_, "styleable/bar", watch_config));
- EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, "attr/foo"));
- EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, "attr/baz"));
- EXPECT_NE(nullptr, test::getValue<Styleable>(&mTable, "styleable/bar"));
+ EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/foo"));
+ EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/baz"));
+ EXPECT_NE(nullptr, test::GetValue<Styleable>(&table_, "styleable/bar"));
}
TEST_F(ResourceParserTest, ParseAttrWithMinMax) {
- std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
+ ASSERT_TRUE(TestParse(input));
- Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->typeMask);
- EXPECT_EQ(10, attr->minInt);
- EXPECT_EQ(23, attr->maxInt);
+ Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->type_mask);
+ EXPECT_EQ(10, attr->min_int);
+ EXPECT_EQ(23, attr->max_int);
}
TEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) {
- std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>";
- ASSERT_FALSE(testParse(input));
+ std::string input =
+ "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>";
+ ASSERT_FALSE(TestParse(input));
}
TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
- std::string input = "<declare-styleable name=\"Styleable\">\n"
- " <attr name=\"foo\" />\n"
- "</declare-styleable>\n"
- "<attr name=\"foo\" format=\"string\"/>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<declare-styleable name=\"Styleable\">\n"
+ " <attr name=\"foo\" />\n"
+ "</declare-styleable>\n"
+ "<attr name=\"foo\" format=\"string\"/>";
+ ASSERT_TRUE(TestParse(input));
- Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
+ Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->type_mask);
}
TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
- std::string input = "<declare-styleable name=\"Theme\">"
- " <attr name=\"foo\" />\n"
- "</declare-styleable>\n"
- "<declare-styleable name=\"Window\">\n"
- " <attr name=\"foo\" format=\"boolean\"/>\n"
- "</declare-styleable>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<declare-styleable name=\"Theme\">"
+ " <attr name=\"foo\" />\n"
+ "</declare-styleable>\n"
+ "<declare-styleable name=\"Window\">\n"
+ " <attr name=\"foo\" format=\"boolean\"/>\n"
+ "</declare-styleable>";
+ ASSERT_TRUE(TestParse(input));
- Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask);
+ Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->type_mask);
}
TEST_F(ResourceParserTest, ParseEnumAttr) {
- std::string input = "<attr name=\"foo\">\n"
- " <enum name=\"bar\" value=\"0\"/>\n"
- " <enum name=\"bat\" value=\"1\"/>\n"
- " <enum name=\"baz\" value=\"2\"/>\n"
- "</attr>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<attr name=\"foo\">\n"
+ " <enum name=\"bar\" value=\"0\"/>\n"
+ " <enum name=\"bat\" value=\"1\"/>\n"
+ " <enum name=\"baz\" value=\"2\"/>\n"
+ "</attr>";
+ ASSERT_TRUE(TestParse(input));
- Attribute* enumAttr = test::getValue<Attribute>(&mTable, "attr/foo");
- ASSERT_NE(enumAttr, nullptr);
- EXPECT_EQ(enumAttr->typeMask, android::ResTable_map::TYPE_ENUM);
- ASSERT_EQ(enumAttr->symbols.size(), 3u);
+ Attribute* enum_attr = test::GetValue<Attribute>(&table_, "attr/foo");
+ ASSERT_NE(enum_attr, nullptr);
+ EXPECT_EQ(enum_attr->type_mask, android::ResTable_map::TYPE_ENUM);
+ ASSERT_EQ(enum_attr->symbols.size(), 3u);
- AAPT_ASSERT_TRUE(enumAttr->symbols[0].symbol.name);
- EXPECT_EQ(enumAttr->symbols[0].symbol.name.value().entry, "bar");
- EXPECT_EQ(enumAttr->symbols[0].value, 0u);
+ AAPT_ASSERT_TRUE(enum_attr->symbols[0].symbol.name);
+ EXPECT_EQ(enum_attr->symbols[0].symbol.name.value().entry, "bar");
+ EXPECT_EQ(enum_attr->symbols[0].value, 0u);
- AAPT_ASSERT_TRUE(enumAttr->symbols[1].symbol.name);
- EXPECT_EQ(enumAttr->symbols[1].symbol.name.value().entry, "bat");
- EXPECT_EQ(enumAttr->symbols[1].value, 1u);
+ AAPT_ASSERT_TRUE(enum_attr->symbols[1].symbol.name);
+ EXPECT_EQ(enum_attr->symbols[1].symbol.name.value().entry, "bat");
+ EXPECT_EQ(enum_attr->symbols[1].value, 1u);
- AAPT_ASSERT_TRUE(enumAttr->symbols[2].symbol.name);
- EXPECT_EQ(enumAttr->symbols[2].symbol.name.value().entry, "baz");
- EXPECT_EQ(enumAttr->symbols[2].value, 2u);
+ AAPT_ASSERT_TRUE(enum_attr->symbols[2].symbol.name);
+ EXPECT_EQ(enum_attr->symbols[2].symbol.name.value().entry, "baz");
+ EXPECT_EQ(enum_attr->symbols[2].value, 2u);
}
TEST_F(ResourceParserTest, ParseFlagAttr) {
- std::string input = "<attr name=\"foo\">\n"
- " <flag name=\"bar\" value=\"0\"/>\n"
- " <flag name=\"bat\" value=\"1\"/>\n"
- " <flag name=\"baz\" value=\"2\"/>\n"
- "</attr>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<attr name=\"foo\">\n"
+ " <flag name=\"bar\" value=\"0\"/>\n"
+ " <flag name=\"bat\" value=\"1\"/>\n"
+ " <flag name=\"baz\" value=\"2\"/>\n"
+ "</attr>";
+ ASSERT_TRUE(TestParse(input));
- Attribute* flagAttr = test::getValue<Attribute>(&mTable, "attr/foo");
- ASSERT_NE(nullptr, flagAttr);
- EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS);
- ASSERT_EQ(flagAttr->symbols.size(), 3u);
+ Attribute* flag_attr = test::GetValue<Attribute>(&table_, "attr/foo");
+ ASSERT_NE(nullptr, flag_attr);
+ EXPECT_EQ(flag_attr->type_mask, android::ResTable_map::TYPE_FLAGS);
+ ASSERT_EQ(flag_attr->symbols.size(), 3u);
- AAPT_ASSERT_TRUE(flagAttr->symbols[0].symbol.name);
- EXPECT_EQ(flagAttr->symbols[0].symbol.name.value().entry, "bar");
- EXPECT_EQ(flagAttr->symbols[0].value, 0u);
+ AAPT_ASSERT_TRUE(flag_attr->symbols[0].symbol.name);
+ EXPECT_EQ(flag_attr->symbols[0].symbol.name.value().entry, "bar");
+ EXPECT_EQ(flag_attr->symbols[0].value, 0u);
- AAPT_ASSERT_TRUE(flagAttr->symbols[1].symbol.name);
- EXPECT_EQ(flagAttr->symbols[1].symbol.name.value().entry, "bat");
- EXPECT_EQ(flagAttr->symbols[1].value, 1u);
+ AAPT_ASSERT_TRUE(flag_attr->symbols[1].symbol.name);
+ EXPECT_EQ(flag_attr->symbols[1].symbol.name.value().entry, "bat");
+ EXPECT_EQ(flag_attr->symbols[1].value, 1u);
- AAPT_ASSERT_TRUE(flagAttr->symbols[2].symbol.name);
- EXPECT_EQ(flagAttr->symbols[2].symbol.name.value().entry, "baz");
- EXPECT_EQ(flagAttr->symbols[2].value, 2u);
+ AAPT_ASSERT_TRUE(flag_attr->symbols[2].symbol.name);
+ EXPECT_EQ(flag_attr->symbols[2].symbol.name.value().entry, "baz");
+ EXPECT_EQ(flag_attr->symbols[2].value, 2u);
- std::unique_ptr<BinaryPrimitive> flagValue = ResourceUtils::tryParseFlagSymbol(flagAttr,
- "baz|bat");
- ASSERT_NE(nullptr, flagValue);
- EXPECT_EQ(flagValue->value.data, 1u | 2u);
+ std::unique_ptr<BinaryPrimitive> flag_value =
+ ResourceUtils::TryParseFlagSymbol(flag_attr, "baz|bat");
+ ASSERT_NE(nullptr, flag_value);
+ EXPECT_EQ(flag_value->value.data, 1u | 2u);
}
TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
- std::string input = "<attr name=\"foo\">\n"
- " <enum name=\"bar\" value=\"0\"/>\n"
- " <enum name=\"bat\" value=\"1\"/>\n"
- " <enum name=\"bat\" value=\"2\"/>\n"
- "</attr>";
- ASSERT_FALSE(testParse(input));
+ std::string input =
+ "<attr name=\"foo\">\n"
+ " <enum name=\"bar\" value=\"0\"/>\n"
+ " <enum name=\"bat\" value=\"1\"/>\n"
+ " <enum name=\"bat\" value=\"2\"/>\n"
+ "</attr>";
+ ASSERT_FALSE(TestParse(input));
}
TEST_F(ResourceParserTest, ParseStyle) {
- std::string input = "<style name=\"foo\" parent=\"@style/fu\">\n"
- " <item name=\"bar\">#ffffffff</item>\n"
- " <item name=\"bat\">@string/hey</item>\n"
- " <item name=\"baz\"><b>hey</b></item>\n"
- "</style>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<style name=\"foo\" parent=\"@style/fu\">\n"
+ " <item name=\"bar\">#ffffffff</item>\n"
+ " <item name=\"bat\">@string/hey</item>\n"
+ " <item name=\"baz\"><b>hey</b></item>\n"
+ "</style>";
+ ASSERT_TRUE(TestParse(input));
- Style* style = test::getValue<Style>(&mTable, "style/foo");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().name);
- EXPECT_EQ(test::parseNameOrDie("style/fu"), style->parent.value().name.value());
- ASSERT_EQ(3u, style->entries.size());
+ Style* style = test::GetValue<Style>(&table_, "style/foo");
+ ASSERT_NE(nullptr, style);
+ AAPT_ASSERT_TRUE(style->parent);
+ AAPT_ASSERT_TRUE(style->parent.value().name);
+ EXPECT_EQ(test::ParseNameOrDie("style/fu"),
+ style->parent.value().name.value());
+ ASSERT_EQ(3u, style->entries.size());
- AAPT_ASSERT_TRUE(style->entries[0].key.name);
- EXPECT_EQ(test::parseNameOrDie("attr/bar"), style->entries[0].key.name.value());
+ AAPT_ASSERT_TRUE(style->entries[0].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("attr/bar"),
+ style->entries[0].key.name.value());
- AAPT_ASSERT_TRUE(style->entries[1].key.name);
- EXPECT_EQ(test::parseNameOrDie("attr/bat"), style->entries[1].key.name.value());
+ AAPT_ASSERT_TRUE(style->entries[1].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("attr/bat"),
+ style->entries[1].key.name.value());
- AAPT_ASSERT_TRUE(style->entries[2].key.name);
- EXPECT_EQ(test::parseNameOrDie("attr/baz"), style->entries[2].key.name.value());
+ AAPT_ASSERT_TRUE(style->entries[2].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("attr/baz"),
+ style->entries[2].key.name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
- std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
+ ASSERT_TRUE(TestParse(input));
- Style* style = test::getValue<Style>(&mTable, "style/foo");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().name);
- EXPECT_EQ(test::parseNameOrDie("com.app:style/Theme"), style->parent.value().name.value());
+ Style* style = test::GetValue<Style>(&table_, "style/foo");
+ ASSERT_NE(nullptr, style);
+ AAPT_ASSERT_TRUE(style->parent);
+ AAPT_ASSERT_TRUE(style->parent.value().name);
+ EXPECT_EQ(test::ParseNameOrDie("com.app:style/Theme"),
+ style->parent.value().name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
- std::string input = "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
- " name=\"foo\" parent=\"app:Theme\"/>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
+ " name=\"foo\" parent=\"app:Theme\"/>";
+ ASSERT_TRUE(TestParse(input));
- Style* style = test::getValue<Style>(&mTable, "style/foo");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().name);
- EXPECT_EQ(test::parseNameOrDie("android:style/Theme"), style->parent.value().name.value());
+ Style* style = test::GetValue<Style>(&table_, "style/foo");
+ ASSERT_NE(nullptr, style);
+ AAPT_ASSERT_TRUE(style->parent);
+ AAPT_ASSERT_TRUE(style->parent.value().name);
+ EXPECT_EQ(test::ParseNameOrDie("android:style/Theme"),
+ style->parent.value().name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
- std::string input =
- "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" name=\"foo\">\n"
- " <item name=\"app:bar\">0</item>\n"
- "</style>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" "
+ "name=\"foo\">\n"
+ " <item name=\"app:bar\">0</item>\n"
+ "</style>";
+ ASSERT_TRUE(TestParse(input));
- Style* style = test::getValue<Style>(&mTable, "style/foo");
- ASSERT_NE(nullptr, style);
- ASSERT_EQ(1u, style->entries.size());
- EXPECT_EQ(test::parseNameOrDie("android:attr/bar"), style->entries[0].key.name.value());
+ Style* style = test::GetValue<Style>(&table_, "style/foo");
+ ASSERT_NE(nullptr, style);
+ ASSERT_EQ(1u, style->entries.size());
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/bar"),
+ style->entries[0].key.name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
- std::string input = "<style name=\"foo.bar\"/>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<style name=\"foo.bar\"/>";
+ ASSERT_TRUE(TestParse(input));
- Style* style = test::getValue<Style>(&mTable, "style/foo.bar");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().name);
- EXPECT_EQ(style->parent.value().name.value(), test::parseNameOrDie("style/foo"));
- EXPECT_TRUE(style->parentInferred);
+ Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
+ ASSERT_NE(nullptr, style);
+ AAPT_ASSERT_TRUE(style->parent);
+ AAPT_ASSERT_TRUE(style->parent.value().name);
+ EXPECT_EQ(style->parent.value().name.value(),
+ test::ParseNameOrDie("style/foo"));
+ EXPECT_TRUE(style->parent_inferred);
}
-TEST_F(ResourceParserTest, ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
- std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
- ASSERT_TRUE(testParse(input));
+TEST_F(ResourceParserTest,
+ ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
+ std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
+ ASSERT_TRUE(TestParse(input));
- Style* style = test::getValue<Style>(&mTable, "style/foo.bar");
- ASSERT_NE(nullptr, style);
- AAPT_EXPECT_FALSE(style->parent);
- EXPECT_FALSE(style->parentInferred);
+ Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
+ ASSERT_NE(nullptr, style);
+ AAPT_EXPECT_FALSE(style->parent);
+ EXPECT_FALSE(style->parent_inferred);
}
TEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) {
- std::string input = R"EOF(<style name="foo" parent="*android:style/bar" />)EOF";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ R"EOF(<style name="foo" parent="*android:style/bar" />)EOF";
+ ASSERT_TRUE(TestParse(input));
- Style* style = test::getValue<Style>(&mTable, "style/foo");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
- EXPECT_TRUE(style->parent.value().privateReference);
+ Style* style = test::GetValue<Style>(&table_, "style/foo");
+ ASSERT_NE(nullptr, style);
+ AAPT_ASSERT_TRUE(style->parent);
+ EXPECT_TRUE(style->parent.value().private_reference);
}
TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
- std::string input = "<string name=\"foo\">@+id/bar</string>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<string name=\"foo\">@+id/bar</string>";
+ ASSERT_TRUE(TestParse(input));
- Id* id = test::getValue<Id>(&mTable, "id/bar");
- ASSERT_NE(id, nullptr);
+ Id* id = test::GetValue<Id>(&table_, "id/bar");
+ ASSERT_NE(id, nullptr);
}
TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
- std::string input = "<declare-styleable name=\"foo\">\n"
- " <attr name=\"bar\" />\n"
- " <attr name=\"bat\" format=\"string|reference\"/>\n"
- " <attr name=\"baz\">\n"
- " <enum name=\"foo\" value=\"1\"/>\n"
- " </attr>\n"
- "</declare-styleable>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<declare-styleable name=\"foo\">\n"
+ " <attr name=\"bar\" />\n"
+ " <attr name=\"bat\" format=\"string|reference\"/>\n"
+ " <attr name=\"baz\">\n"
+ " <enum name=\"foo\" value=\"1\"/>\n"
+ " </attr>\n"
+ "</declare-styleable>";
+ ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result =
- mTable.findResource(test::parseNameOrDie("styleable/foo"));
- AAPT_ASSERT_TRUE(result);
- EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
+ Maybe<ResourceTable::SearchResult> result =
+ table_.FindResource(test::ParseNameOrDie("styleable/foo"));
+ AAPT_ASSERT_TRUE(result);
+ EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state);
- Attribute* attr = test::getValue<Attribute>(&mTable, "attr/bar");
- ASSERT_NE(attr, nullptr);
- EXPECT_TRUE(attr->isWeak());
+ Attribute* attr = test::GetValue<Attribute>(&table_, "attr/bar");
+ ASSERT_NE(attr, nullptr);
+ EXPECT_TRUE(attr->IsWeak());
- attr = test::getValue<Attribute>(&mTable, "attr/bat");
- ASSERT_NE(attr, nullptr);
- EXPECT_TRUE(attr->isWeak());
+ attr = test::GetValue<Attribute>(&table_, "attr/bat");
+ ASSERT_NE(attr, nullptr);
+ EXPECT_TRUE(attr->IsWeak());
- attr = test::getValue<Attribute>(&mTable, "attr/baz");
- ASSERT_NE(attr, nullptr);
- EXPECT_TRUE(attr->isWeak());
- EXPECT_EQ(1u, attr->symbols.size());
+ attr = test::GetValue<Attribute>(&table_, "attr/baz");
+ ASSERT_NE(attr, nullptr);
+ EXPECT_TRUE(attr->IsWeak());
+ EXPECT_EQ(1u, attr->symbols.size());
- EXPECT_NE(nullptr, test::getValue<Id>(&mTable, "id/foo"));
+ EXPECT_NE(nullptr, test::GetValue<Id>(&table_, "id/foo"));
- Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo");
- ASSERT_NE(styleable, nullptr);
- ASSERT_EQ(3u, styleable->entries.size());
+ Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
+ ASSERT_NE(styleable, nullptr);
+ ASSERT_EQ(3u, styleable->entries.size());
- EXPECT_EQ(test::parseNameOrDie("attr/bar"), styleable->entries[0].name.value());
- EXPECT_EQ(test::parseNameOrDie("attr/bat"), styleable->entries[1].name.value());
+ EXPECT_EQ(test::ParseNameOrDie("attr/bar"),
+ styleable->entries[0].name.value());
+ EXPECT_EQ(test::ParseNameOrDie("attr/bat"),
+ styleable->entries[1].name.value());
}
TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) {
- std::string input = "<declare-styleable name=\"foo\" xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n"
- " <attr name=\"*android:bar\" />\n"
- " <attr name=\"privAndroid:bat\" />\n"
- "</declare-styleable>";
- ASSERT_TRUE(testParse(input));
- Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo");
- ASSERT_NE(nullptr, styleable);
- ASSERT_EQ(2u, styleable->entries.size());
+ std::string input =
+ "<declare-styleable name=\"foo\" "
+ "xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n"
+ " <attr name=\"*android:bar\" />\n"
+ " <attr name=\"privAndroid:bat\" />\n"
+ "</declare-styleable>";
+ ASSERT_TRUE(TestParse(input));
+ Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
+ ASSERT_NE(nullptr, styleable);
+ ASSERT_EQ(2u, styleable->entries.size());
- EXPECT_TRUE(styleable->entries[0].privateReference);
- AAPT_ASSERT_TRUE(styleable->entries[0].name);
- EXPECT_EQ(std::string("android"), styleable->entries[0].name.value().package);
+ EXPECT_TRUE(styleable->entries[0].private_reference);
+ AAPT_ASSERT_TRUE(styleable->entries[0].name);
+ EXPECT_EQ(std::string("android"), styleable->entries[0].name.value().package);
- EXPECT_TRUE(styleable->entries[1].privateReference);
- AAPT_ASSERT_TRUE(styleable->entries[1].name);
- EXPECT_EQ(std::string("android"), styleable->entries[1].name.value().package);
+ EXPECT_TRUE(styleable->entries[1].private_reference);
+ AAPT_ASSERT_TRUE(styleable->entries[1].name);
+ EXPECT_EQ(std::string("android"), styleable->entries[1].name.value().package);
}
TEST_F(ResourceParserTest, ParseArray) {
- std::string input = "<array name=\"foo\">\n"
- " <item>@string/ref</item>\n"
- " <item>hey</item>\n"
- " <item>23</item>\n"
- "</array>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<array name=\"foo\">\n"
+ " <item>@string/ref</item>\n"
+ " <item>hey</item>\n"
+ " <item>23</item>\n"
+ "</array>";
+ ASSERT_TRUE(TestParse(input));
- Array* array = test::getValue<Array>(&mTable, "array/foo");
- ASSERT_NE(array, nullptr);
- ASSERT_EQ(3u, array->items.size());
+ Array* array = test::GetValue<Array>(&table_, "array/foo");
+ ASSERT_NE(array, nullptr);
+ ASSERT_EQ(3u, array->items.size());
- EXPECT_NE(nullptr, valueCast<Reference>(array->items[0].get()));
- EXPECT_NE(nullptr, valueCast<String>(array->items[1].get()));
- EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(array->items[2].get()));
+ EXPECT_NE(nullptr, ValueCast<Reference>(array->items[0].get()));
+ EXPECT_NE(nullptr, ValueCast<String>(array->items[1].get()));
+ EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(array->items[2].get()));
}
TEST_F(ResourceParserTest, ParseStringArray) {
- std::string input = "<string-array name=\"foo\">\n"
- " <item>\"Werk\"</item>\n"
- "</string-array>\n";
- ASSERT_TRUE(testParse(input));
- EXPECT_NE(nullptr, test::getValue<Array>(&mTable, "array/foo"));
+ std::string input =
+ "<string-array name=\"foo\">\n"
+ " <item>\"Werk\"</item>\n"
+ "</string-array>\n";
+ ASSERT_TRUE(TestParse(input));
+ EXPECT_NE(nullptr, test::GetValue<Array>(&table_, "array/foo"));
}
TEST_F(ResourceParserTest, ParsePlural) {
- std::string input = "<plurals name=\"foo\">\n"
- " <item quantity=\"other\">apples</item>\n"
- " <item quantity=\"one\">apple</item>\n"
- "</plurals>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<plurals name=\"foo\">\n"
+ " <item quantity=\"other\">apples</item>\n"
+ " <item quantity=\"one\">apple</item>\n"
+ "</plurals>";
+ ASSERT_TRUE(TestParse(input));
}
TEST_F(ResourceParserTest, ParseCommentsWithResource) {
- std::string input = "<!--This is a comment-->\n"
- "<string name=\"foo\">Hi</string>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<!--This is a comment-->\n"
+ "<string name=\"foo\">Hi</string>";
+ ASSERT_TRUE(TestParse(input));
- String* value = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(value->getComment(), "This is a comment");
+ String* value = test::GetValue<String>(&table_, "string/foo");
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(value->GetComment(), "This is a comment");
}
TEST_F(ResourceParserTest, DoNotCombineMultipleComments) {
- std::string input = "<!--One-->\n"
- "<!--Two-->\n"
- "<string name=\"foo\">Hi</string>";
+ std::string input =
+ "<!--One-->\n"
+ "<!--Two-->\n"
+ "<string name=\"foo\">Hi</string>";
- ASSERT_TRUE(testParse(input));
+ ASSERT_TRUE(TestParse(input));
- String* value = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(value->getComment(), "Two");
+ String* value = test::GetValue<String>(&table_, "string/foo");
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(value->GetComment(), "Two");
}
TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) {
- std::string input = "<!--One-->\n"
- "<string name=\"foo\">\n"
- " Hi\n"
- "<!--Two-->\n"
- "</string>";
+ std::string input =
+ "<!--One-->\n"
+ "<string name=\"foo\">\n"
+ " Hi\n"
+ "<!--Two-->\n"
+ "</string>";
- ASSERT_TRUE(testParse(input));
+ ASSERT_TRUE(TestParse(input));
- String* value = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(value->getComment(), "One");
+ String* value = test::GetValue<String>(&table_, "string/foo");
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(value->GetComment(), "One");
}
TEST_F(ResourceParserTest, ParseNestedComments) {
- // We only care about declare-styleable and enum/flag attributes because comments
- // from those end up in R.java
- std::string input = R"EOF(
+ // We only care about declare-styleable and enum/flag attributes because
+ // comments
+ // from those end up in R.java
+ std::string input = R"EOF(
<declare-styleable name="foo">
<!-- The name of the bar -->
<attr name="barName" format="string|reference" />
@@ -541,19 +591,21 @@
<!-- The very first -->
<enum name="one" value="1" />
</attr>)EOF";
- ASSERT_TRUE(testParse(input));
+ ASSERT_TRUE(TestParse(input));
- Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo");
- ASSERT_NE(nullptr, styleable);
- ASSERT_EQ(1u, styleable->entries.size());
+ Styleable* styleable = test::GetValue<Styleable>(&table_, "styleable/foo");
+ ASSERT_NE(nullptr, styleable);
+ ASSERT_EQ(1u, styleable->entries.size());
- EXPECT_EQ(StringPiece("The name of the bar"), styleable->entries.front().getComment());
+ EXPECT_EQ(StringPiece("The name of the bar"),
+ styleable->entries.front().GetComment());
- Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
- ASSERT_NE(nullptr, attr);
- ASSERT_EQ(1u, attr->symbols.size());
+ Attribute* attr = test::GetValue<Attribute>(&table_, "attr/foo");
+ ASSERT_NE(nullptr, attr);
+ ASSERT_EQ(1u, attr->symbols.size());
- EXPECT_EQ(StringPiece("The very first"), attr->symbols.front().symbol.getComment());
+ EXPECT_EQ(StringPiece("The very first"),
+ attr->symbols.front().symbol.GetComment());
}
/*
@@ -561,15 +613,15 @@
* (as an ID has no value).
*/
TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
- std::string input = "<public type=\"id\" name=\"foo\"/>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<public type=\"id\" name=\"foo\"/>";
+ ASSERT_TRUE(TestParse(input));
- Id* id = test::getValue<Id>(&mTable, "id/foo");
- ASSERT_NE(nullptr, id);
+ Id* id = test::GetValue<Id>(&table_, "id/foo");
+ ASSERT_NE(nullptr, id);
}
TEST_F(ResourceParserTest, KeepAllProducts) {
- std::string input = R"EOF(
+ std::string input = R"EOF(
<string name="foo" product="phone">hi</string>
<string name="foo" product="no-sdcard">ho</string>
<string name="bar" product="">wee</string>
@@ -577,88 +629,92 @@
<string name="bit" product="phablet">hoot</string>
<string name="bot" product="default">yes</string>
)EOF";
- ASSERT_TRUE(testParse(input));
+ ASSERT_TRUE(TestParse(input));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/foo",
- ConfigDescription::defaultConfig(),
- "phone"));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/foo",
- ConfigDescription::defaultConfig(),
- "no-sdcard"));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/bar",
- ConfigDescription::defaultConfig(),
- ""));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/baz",
- ConfigDescription::defaultConfig(),
- ""));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/bit",
- ConfigDescription::defaultConfig(),
- "phablet"));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/bot",
- ConfigDescription::defaultConfig(),
- "default"));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
+ &table_, "string/foo",
+ ConfigDescription::DefaultConfig(), "phone"));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
+ &table_, "string/foo",
+ ConfigDescription::DefaultConfig(), "no-sdcard"));
+ EXPECT_NE(nullptr,
+ test::GetValueForConfigAndProduct<String>(
+ &table_, "string/bar", ConfigDescription::DefaultConfig(), ""));
+ EXPECT_NE(nullptr,
+ test::GetValueForConfigAndProduct<String>(
+ &table_, "string/baz", ConfigDescription::DefaultConfig(), ""));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
+ &table_, "string/bit",
+ ConfigDescription::DefaultConfig(), "phablet"));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<String>(
+ &table_, "string/bot",
+ ConfigDescription::DefaultConfig(), "default"));
}
TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
- std::string input = R"EOF(
+ std::string input = R"EOF(
<public-group type="attr" first-id="0x01010040">
<public name="foo" />
<public name="bar" />
</public-group>)EOF";
- ASSERT_TRUE(testParse(input));
+ ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result = mTable.findResource(
- test::parseNameOrDie("attr/foo"));
- AAPT_ASSERT_TRUE(result);
+ Maybe<ResourceTable::SearchResult> result =
+ table_.FindResource(test::ParseNameOrDie("attr/foo"));
+ AAPT_ASSERT_TRUE(result);
- AAPT_ASSERT_TRUE(result.value().package->id);
- AAPT_ASSERT_TRUE(result.value().type->id);
- AAPT_ASSERT_TRUE(result.value().entry->id);
- ResourceId actualId(result.value().package->id.value(),
- result.value().type->id.value(),
- result.value().entry->id.value());
- EXPECT_EQ(ResourceId(0x01010040), actualId);
+ AAPT_ASSERT_TRUE(result.value().package->id);
+ AAPT_ASSERT_TRUE(result.value().type->id);
+ AAPT_ASSERT_TRUE(result.value().entry->id);
+ ResourceId actual_id(result.value().package->id.value(),
+ result.value().type->id.value(),
+ result.value().entry->id.value());
+ EXPECT_EQ(ResourceId(0x01010040), actual_id);
- result = mTable.findResource(test::parseNameOrDie("attr/bar"));
- AAPT_ASSERT_TRUE(result);
+ result = table_.FindResource(test::ParseNameOrDie("attr/bar"));
+ AAPT_ASSERT_TRUE(result);
- AAPT_ASSERT_TRUE(result.value().package->id);
- AAPT_ASSERT_TRUE(result.value().type->id);
- AAPT_ASSERT_TRUE(result.value().entry->id);
- actualId = ResourceId(result.value().package->id.value(),
- result.value().type->id.value(),
- result.value().entry->id.value());
- EXPECT_EQ(ResourceId(0x01010041), actualId);
+ AAPT_ASSERT_TRUE(result.value().package->id);
+ AAPT_ASSERT_TRUE(result.value().type->id);
+ AAPT_ASSERT_TRUE(result.value().entry->id);
+ actual_id = ResourceId(result.value().package->id.value(),
+ result.value().type->id.value(),
+ result.value().entry->id.value());
+ EXPECT_EQ(ResourceId(0x01010041), actual_id);
}
TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
- std::string input = R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF";
+ ASSERT_TRUE(TestParse(input));
- input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF";
- ASSERT_FALSE(testParse(input));
+ input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF";
+ ASSERT_FALSE(TestParse(input));
}
-TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
- std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
- ASSERT_TRUE(testParse(input));
+TEST_F(ResourceParserTest,
+ AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
+ std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
+ ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result = mTable.findResource(
- test::parseNameOrDie("string/bar"));
- AAPT_ASSERT_TRUE(result);
- const ResourceEntry* entry = result.value().entry;
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(SymbolState::kUndefined, entry->symbolStatus.state);
+ Maybe<ResourceTable::SearchResult> result =
+ table_.FindResource(test::ParseNameOrDie("string/bar"));
+ AAPT_ASSERT_TRUE(result);
+ const ResourceEntry* entry = result.value().entry;
+ ASSERT_NE(nullptr, entry);
+ EXPECT_EQ(SymbolState::kUndefined, entry->symbol_status.state);
}
TEST_F(ResourceParserTest, ParseItemElementWithFormat) {
- std::string input = R"EOF(<item name="foo" type="integer" format="float">0.3</item>)EOF";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ R"EOF(<item name="foo" type="integer" format="float">0.3</item>)EOF";
+ ASSERT_TRUE(TestParse(input));
- BinaryPrimitive* val = test::getValue<BinaryPrimitive>(&mTable, "integer/foo");
- ASSERT_NE(nullptr, val);
+ BinaryPrimitive* val =
+ test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
+ ASSERT_NE(nullptr, val);
- EXPECT_EQ(uint32_t(android::Res_value::TYPE_FLOAT), val->value.dataType);
+ EXPECT_EQ(uint32_t(android::Res_value::TYPE_FLOAT), val->value.dataType);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index bdc6a8c..4e6a50a 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -14,551 +14,543 @@
* limitations under the License.
*/
+#include "ResourceTable.h"
#include "ConfigDescription.h"
#include "NameMangler.h"
-#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "util/Util.h"
-#include <algorithm>
+#include <android-base/logging.h>
#include <androidfw/ResourceTypes.h>
+#include <algorithm>
#include <memory>
#include <string>
#include <tuple>
namespace aapt {
-static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
- return lhs->type < rhs;
+static bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs,
+ ResourceType rhs) {
+ return lhs->type < rhs;
}
template <typename T>
-static bool lessThanStructWithName(const std::unique_ptr<T>& lhs,
- const StringPiece& rhs) {
- return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
+static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs,
+ const StringPiece& rhs) {
+ return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
}
-ResourceTablePackage* ResourceTable::findPackage(const StringPiece& name) {
- const auto last = packages.end();
- auto iter = std::lower_bound(packages.begin(), last, name,
- lessThanStructWithName<ResourceTablePackage>);
- if (iter != last && name == (*iter)->name) {
- return iter->get();
- }
- return nullptr;
+ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) {
+ const auto last = packages.end();
+ auto iter =
+ std::lower_bound(packages.begin(), last, name,
+ less_than_struct_with_name<ResourceTablePackage>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
+ }
+ return nullptr;
}
-ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) {
- for (auto& package : packages) {
- if (package->id && package->id.value() == id) {
- return package.get();
- }
+ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) {
+ for (auto& package : packages) {
+ if (package->id && package->id.value() == id) {
+ return package.get();
}
- return nullptr;
+ }
+ return nullptr;
}
-ResourceTablePackage* ResourceTable::createPackage(const StringPiece& name, Maybe<uint8_t> id) {
- ResourceTablePackage* package = findOrCreatePackage(name);
- if (id && !package->id) {
- package->id = id;
- return package;
- }
-
- if (id && package->id && package->id.value() != id.value()) {
- return nullptr;
- }
+ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name,
+ Maybe<uint8_t> id) {
+ ResourceTablePackage* package = FindOrCreatePackage(name);
+ if (id && !package->id) {
+ package->id = id;
return package;
-}
+ }
-ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece& name) {
- const auto last = packages.end();
- auto iter = std::lower_bound(packages.begin(), last, name,
- lessThanStructWithName<ResourceTablePackage>);
- if (iter != last && name == (*iter)->name) {
- return iter->get();
- }
-
- std::unique_ptr<ResourceTablePackage> newPackage = util::make_unique<ResourceTablePackage>();
- newPackage->name = name.toString();
- return packages.emplace(iter, std::move(newPackage))->get();
-}
-
-ResourceTableType* ResourceTablePackage::findType(ResourceType type) {
- const auto last = types.end();
- auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
- if (iter != last && (*iter)->type == type) {
- return iter->get();
- }
+ if (id && package->id && package->id.value() != id.value()) {
return nullptr;
+ }
+ return package;
}
-ResourceTableType* ResourceTablePackage::findOrCreateType(ResourceType type) {
- const auto last = types.end();
- auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
- if (iter != last && (*iter)->type == type) {
- return iter->get();
- }
- return types.emplace(iter, new ResourceTableType(type))->get();
+ResourceTablePackage* ResourceTable::FindOrCreatePackage(
+ const StringPiece& name) {
+ const auto last = packages.end();
+ auto iter =
+ std::lower_bound(packages.begin(), last, name,
+ less_than_struct_with_name<ResourceTablePackage>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
+ }
+
+ std::unique_ptr<ResourceTablePackage> new_package =
+ util::make_unique<ResourceTablePackage>();
+ new_package->name = name.ToString();
+ return packages.emplace(iter, std::move(new_package))->get();
}
-ResourceEntry* ResourceTableType::findEntry(const StringPiece& name) {
- const auto last = entries.end();
- auto iter = std::lower_bound(entries.begin(), last, name,
- lessThanStructWithName<ResourceEntry>);
- if (iter != last && name == (*iter)->name) {
- return iter->get();
- }
- return nullptr;
+ResourceTableType* ResourceTablePackage::FindType(ResourceType type) {
+ const auto last = types.end();
+ auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
+ if (iter != last && (*iter)->type == type) {
+ return iter->get();
+ }
+ return nullptr;
}
-ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece& name) {
- auto last = entries.end();
- auto iter = std::lower_bound(entries.begin(), last, name,
- lessThanStructWithName<ResourceEntry>);
- if (iter != last && name == (*iter)->name) {
- return iter->get();
- }
- return entries.emplace(iter, new ResourceEntry(name))->get();
+ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) {
+ const auto last = types.end();
+ auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
+ if (iter != last && (*iter)->type == type) {
+ return iter->get();
+ }
+ return types.emplace(iter, new ResourceTableType(type))->get();
}
-ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config) {
- return findValue(config, StringPiece());
+ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name) {
+ const auto last = entries.end();
+ auto iter = std::lower_bound(entries.begin(), last, name,
+ less_than_struct_with_name<ResourceEntry>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
+ }
+ return nullptr;
+}
+
+ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name) {
+ auto last = entries.end();
+ auto iter = std::lower_bound(entries.begin(), last, name,
+ less_than_struct_with_name<ResourceEntry>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
+ }
+ return entries.emplace(iter, new ResourceEntry(name))->get();
+}
+
+ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
+ return FindValue(config, StringPiece());
}
struct ConfigKey {
- const ConfigDescription* config;
- const StringPiece& product;
+ const ConfigDescription* config;
+ const StringPiece& product;
};
-bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
- int cmp = lhs->config.compare(*rhs.config);
- if (cmp == 0) {
- cmp = StringPiece(lhs->product).compare(rhs.product);
- }
- return cmp < 0;
+bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs,
+ const ConfigKey& rhs) {
+ int cmp = lhs->config.compare(*rhs.config);
+ if (cmp == 0) {
+ cmp = StringPiece(lhs->product).compare(rhs.product);
+ }
+ return cmp < 0;
}
-ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config,
+ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
const StringPiece& product) {
- auto iter = std::lower_bound(values.begin(), values.end(),
- ConfigKey{ &config, product }, ltConfigKeyRef);
- if (iter != values.end()) {
- ResourceConfigValue* value = iter->get();
- if (value->config == config && StringPiece(value->product) == product) {
- return value;
- }
+ auto iter = std::lower_bound(values.begin(), values.end(),
+ ConfigKey{&config, product}, ltConfigKeyRef);
+ if (iter != values.end()) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config && StringPiece(value->product) == product) {
+ return value;
}
- return nullptr;
+ }
+ return nullptr;
}
-ResourceConfigValue* ResourceEntry::findOrCreateValue(const ConfigDescription& config,
- const StringPiece& product) {
- auto iter = std::lower_bound(values.begin(), values.end(),
- ConfigKey{ &config, product }, ltConfigKeyRef);
- if (iter != values.end()) {
- ResourceConfigValue* value = iter->get();
- if (value->config == config && StringPiece(value->product) == product) {
- return value;
- }
+ResourceConfigValue* ResourceEntry::FindOrCreateValue(
+ const ConfigDescription& config, const StringPiece& product) {
+ auto iter = std::lower_bound(values.begin(), values.end(),
+ ConfigKey{&config, product}, ltConfigKeyRef);
+ if (iter != values.end()) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config && StringPiece(value->product) == product) {
+ return value;
}
- ResourceConfigValue* newValue = values.insert(
- iter, util::make_unique<ResourceConfigValue>(config, product))->get();
- return newValue;
+ }
+ ResourceConfigValue* newValue =
+ values
+ .insert(iter, util::make_unique<ResourceConfigValue>(config, product))
+ ->get();
+ return newValue;
}
-std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(const ConfigDescription& config) {
- std::vector<ResourceConfigValue*> results;
+std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(
+ const ConfigDescription& config) {
+ std::vector<ResourceConfigValue*> results;
- auto iter = values.begin();
- for (; iter != values.end(); ++iter) {
- ResourceConfigValue* value = iter->get();
- if (value->config == config) {
- results.push_back(value);
- ++iter;
- break;
- }
+ auto iter = values.begin();
+ for (; iter != values.end(); ++iter) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config) {
+ results.push_back(value);
+ ++iter;
+ break;
}
+ }
- for (; iter != values.end(); ++iter) {
- ResourceConfigValue* value = iter->get();
- if (value->config == config) {
- results.push_back(value);
- }
+ for (; iter != values.end(); ++iter) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config) {
+ results.push_back(value);
}
- return results;
+ }
+ return results;
}
-std::vector<ResourceConfigValue*> ResourceEntry::findValuesIf(
- const std::function<bool(ResourceConfigValue*)>& f) {
- std::vector<ResourceConfigValue*> results;
- for (auto& configValue : values) {
- if (f(configValue.get())) {
- results.push_back(configValue.get());
- }
+std::vector<ResourceConfigValue*> ResourceEntry::FindValuesIf(
+ const std::function<bool(ResourceConfigValue*)>& f) {
+ std::vector<ResourceConfigValue*> results;
+ for (auto& configValue : values) {
+ if (f(configValue.get())) {
+ results.push_back(configValue.get());
}
- return results;
+ }
+ return results;
}
/**
* The default handler for collisions.
*
- * Typically, a weak value will be overridden by a strong value. An existing weak
+ * Typically, a weak value will be overridden by a strong value. An existing
+ * weak
* value will not be overridden by an incoming weak value.
*
* There are some exceptions:
*
* Attributes: There are two types of Attribute values: USE and DECL.
*
- * USE is anywhere an Attribute is declared without a format, and in a place that would
+ * USE is anywhere an Attribute is declared without a format, and in a place
+ * that would
* be legal to declare if the Attribute already existed. This is typically in a
- * <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
+ * <declare-styleable> tag. Attributes defined in a <declare-styleable> are also
+ * weak.
*
- * DECL is an absolute declaration of an Attribute and specifies an explicit format.
+ * DECL is an absolute declaration of an Attribute and specifies an explicit
+ * format.
*
- * A DECL will override a USE without error. Two DECLs must match in their format for there to be
+ * A DECL will override a USE without error. Two DECLs must match in their
+ * format for there to be
* no error.
*/
-ResourceTable::CollisionResult ResourceTable::resolveValueCollision(
- Value* existing, Value* incoming) {
- Attribute* existingAttr = valueCast<Attribute>(existing);
- Attribute* incomingAttr = valueCast<Attribute>(incoming);
- if (!incomingAttr) {
- if (incoming->isWeak()) {
- // We're trying to add a weak resource but a resource
- // already exists. Keep the existing.
- return CollisionResult::kKeepOriginal;
- } else if (existing->isWeak()) {
- // Override the weak resource with the new strong resource.
- return CollisionResult::kTakeNew;
- }
- // The existing and incoming values are strong, this is an error
- // if the values are not both attributes.
- return CollisionResult::kConflict;
+ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(
+ Value* existing, Value* incoming) {
+ Attribute* existing_attr = ValueCast<Attribute>(existing);
+ Attribute* incoming_attr = ValueCast<Attribute>(incoming);
+ if (!incoming_attr) {
+ if (incoming->IsWeak()) {
+ // We're trying to add a weak resource but a resource
+ // already exists. Keep the existing.
+ return CollisionResult::kKeepOriginal;
+ } else if (existing->IsWeak()) {
+ // Override the weak resource with the new strong resource.
+ return CollisionResult::kTakeNew;
}
-
- if (!existingAttr) {
- if (existing->isWeak()) {
- // The existing value is not an attribute and it is weak,
- // so take the incoming attribute value.
- return CollisionResult::kTakeNew;
- }
- // The existing value is not an attribute and it is strong,
- // so the incoming attribute value is an error.
- return CollisionResult::kConflict;
- }
-
- assert(incomingAttr && existingAttr);
-
- //
- // Attribute specific handling. At this point we know both
- // values are attributes. Since we can declare and define
- // attributes all-over, we do special handling to see
- // which definition sticks.
- //
- if (existingAttr->typeMask == incomingAttr->typeMask) {
- // The two attributes are both DECLs, but they are plain attributes
- // with the same formats.
- // Keep the strongest one.
- return existingAttr->isWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
- }
-
- if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
- // Any incoming attribute is better than this.
- return CollisionResult::kTakeNew;
- }
-
- if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
- // The incoming attribute may be a USE instead of a DECL.
- // Keep the existing attribute.
- return CollisionResult::kKeepOriginal;
- }
+ // The existing and incoming values are strong, this is an error
+ // if the values are not both attributes.
return CollisionResult::kConflict;
+ }
+
+ if (!existing_attr) {
+ if (existing->IsWeak()) {
+ // The existing value is not an attribute and it is weak,
+ // so take the incoming attribute value.
+ return CollisionResult::kTakeNew;
+ }
+ // The existing value is not an attribute and it is strong,
+ // so the incoming attribute value is an error.
+ return CollisionResult::kConflict;
+ }
+
+ CHECK(incoming_attr != nullptr && existing_attr != nullptr);
+
+ //
+ // Attribute specific handling. At this point we know both
+ // values are attributes. Since we can declare and define
+ // attributes all-over, we do special handling to see
+ // which definition sticks.
+ //
+ if (existing_attr->type_mask == incoming_attr->type_mask) {
+ // The two attributes are both DECLs, but they are plain attributes
+ // with the same formats.
+ // Keep the strongest one.
+ return existing_attr->IsWeak() ? CollisionResult::kTakeNew
+ : CollisionResult::kKeepOriginal;
+ }
+
+ if (existing_attr->IsWeak() &&
+ existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
+ // Any incoming attribute is better than this.
+ return CollisionResult::kTakeNew;
+ }
+
+ if (incoming_attr->IsWeak() &&
+ incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
+ // The incoming attribute may be a USE instead of a DECL.
+ // Keep the existing attribute.
+ return CollisionResult::kKeepOriginal;
+ }
+ return CollisionResult::kConflict;
}
static constexpr const char* kValidNameChars = "._-";
static constexpr const char* kValidNameMangledChars = "._-$";
-bool ResourceTable::addResource(const ResourceNameRef& name,
+bool ResourceTable::AddResource(const ResourceNameRef& name,
const ConfigDescription& config,
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, {}, config, product, std::move(value), kValidNameChars,
- resolveValueCollision, diag);
+ return AddResourceImpl(name, {}, config, product, std::move(value),
+ kValidNameChars, ResolveValueCollision, diag);
}
-bool ResourceTable::addResource(const ResourceNameRef& name,
- const ResourceId& resId,
+bool ResourceTable::AddResource(const ResourceNameRef& name,
+ const ResourceId& res_id,
const ConfigDescription& config,
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, resId, config, product, std::move(value), kValidNameChars,
- resolveValueCollision, diag);
+ return AddResourceImpl(name, res_id, config, product, std::move(value),
+ kValidNameChars, ResolveValueCollision, diag);
}
-bool ResourceTable::addFileReference(const ResourceNameRef& name,
+bool ResourceTable::AddFileReference(const ResourceNameRef& name,
const ConfigDescription& config,
const Source& source,
const StringPiece& path,
IDiagnostics* diag) {
- return addFileReferenceImpl(name, config, source, path, nullptr, kValidNameChars, diag);
+ return AddFileReferenceImpl(name, config, source, path, nullptr,
+ kValidNameChars, diag);
}
-bool ResourceTable::addFileReferenceAllowMangled(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Source& source,
- const StringPiece& path,
- io::IFile* file,
- IDiagnostics* diag) {
- return addFileReferenceImpl(name, config, source, path, file, kValidNameMangledChars, diag);
+bool ResourceTable::AddFileReferenceAllowMangled(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const Source& source, const StringPiece& path, io::IFile* file,
+ IDiagnostics* diag) {
+ return AddFileReferenceImpl(name, config, source, path, file,
+ kValidNameMangledChars, diag);
}
-bool ResourceTable::addFileReferenceImpl(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Source& source,
- const StringPiece& path,
- io::IFile* file,
- const char* validChars,
- IDiagnostics* diag) {
- std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
- stringPool.makeRef(path));
- fileRef->setSource(source);
- fileRef->file = file;
- return addResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
- validChars, resolveValueCollision, diag);
+bool ResourceTable::AddFileReferenceImpl(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const Source& source, const StringPiece& path, io::IFile* file,
+ const char* valid_chars, IDiagnostics* diag) {
+ std::unique_ptr<FileReference> fileRef =
+ util::make_unique<FileReference>(string_pool.MakeRef(path));
+ fileRef->SetSource(source);
+ fileRef->file = file;
+ return AddResourceImpl(name, ResourceId{}, config, StringPiece{},
+ std::move(fileRef), valid_chars, ResolveValueCollision,
+ diag);
}
-bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
+bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
const ConfigDescription& config,
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, ResourceId{}, config, product, std::move(value),
- kValidNameMangledChars, resolveValueCollision, diag);
+ return AddResourceImpl(name, ResourceId{}, config, product, std::move(value),
+ kValidNameMangledChars, ResolveValueCollision, diag);
}
-bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
+bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
const ResourceId& id,
const ConfigDescription& config,
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, id, config, product, std::move(value), kValidNameMangledChars,
- resolveValueCollision, diag);
+ return AddResourceImpl(name, id, config, product, std::move(value),
+ kValidNameMangledChars, ResolveValueCollision, diag);
}
-bool ResourceTable::addResourceImpl(const ResourceNameRef& name,
- const ResourceId& resId,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- const char* validChars,
- const CollisionResolverFunc& conflictResolver,
- IDiagnostics* diag) {
- assert(value && "value can't be nullptr");
- assert(diag && "diagnostics can't be nullptr");
+bool ResourceTable::AddResourceImpl(
+ const ResourceNameRef& name, const ResourceId& res_id,
+ const ConfigDescription& config, const StringPiece& product,
+ std::unique_ptr<Value> value, const char* valid_chars,
+ const CollisionResolverFunc& conflictResolver, IDiagnostics* diag) {
+ CHECK(value != nullptr);
+ CHECK(diag != nullptr);
- auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
- if (badCharIter != name.entry.end()) {
- diag->error(DiagMessage(value->getSource())
- << "resource '"
- << name
- << "' has invalid entry name '"
- << name.entry
- << "'. Invalid character '"
- << StringPiece(badCharIter, 1)
- << "'");
+ auto bad_char_iter =
+ util::FindNonAlphaNumericAndNotInSet(name.entry, valid_chars);
+ if (bad_char_iter != name.entry.end()) {
+ diag->Error(DiagMessage(value->GetSource())
+ << "resource '" << name << "' has invalid entry name '"
+ << name.entry << "'. Invalid character '"
+ << StringPiece(bad_char_iter, 1) << "'");
+ return false;
+ }
+
+ ResourceTablePackage* package = FindOrCreatePackage(name.package);
+ if (res_id.is_valid() && package->id &&
+ package->id.value() != res_id.package_id()) {
+ diag->Error(DiagMessage(value->GetSource())
+ << "trying to add resource '" << name << "' with ID " << res_id
+ << " but package '" << package->name << "' already has ID "
+ << std::hex << (int)package->id.value() << std::dec);
+ return false;
+ }
+
+ ResourceTableType* type = package->FindOrCreateType(name.type);
+ if (res_id.is_valid() && type->id && type->id.value() != res_id.type_id()) {
+ diag->Error(DiagMessage(value->GetSource())
+ << "trying to add resource '" << name << "' with ID " << res_id
+ << " but type '" << type->type << "' already has ID "
+ << std::hex << (int)type->id.value() << std::dec);
+ return false;
+ }
+
+ ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
+ if (res_id.is_valid() && entry->id &&
+ entry->id.value() != res_id.entry_id()) {
+ diag->Error(DiagMessage(value->GetSource())
+ << "trying to add resource '" << name << "' with ID " << res_id
+ << " but resource already has ID "
+ << ResourceId(package->id.value(), type->id.value(),
+ entry->id.value()));
+ return false;
+ }
+
+ ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
+ if (!config_value->value) {
+ // Resource does not exist, add it now.
+ config_value->value = std::move(value);
+
+ } else {
+ switch (conflictResolver(config_value->value.get(), value.get())) {
+ case CollisionResult::kTakeNew:
+ // Take the incoming value.
+ config_value->value = std::move(value);
+ break;
+
+ case CollisionResult::kConflict:
+ diag->Error(DiagMessage(value->GetSource())
+ << "duplicate value for resource '" << name << "' "
+ << "with config '" << config << "'");
+ diag->Error(DiagMessage(config_value->value->GetSource())
+ << "resource previously defined here");
return false;
+
+ case CollisionResult::kKeepOriginal:
+ break;
}
+ }
- ResourceTablePackage* package = findOrCreatePackage(name.package);
- if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
- diag->error(DiagMessage(value->getSource())
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but package '"
- << package->name
- << "' already has ID "
- << std::hex << (int) package->id.value() << std::dec);
- return false;
- }
-
- ResourceTableType* type = package->findOrCreateType(name.type);
- if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
- diag->error(DiagMessage(value->getSource())
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but type '"
- << type->type
- << "' already has ID "
- << std::hex << (int) type->id.value() << std::dec);
- return false;
- }
-
- ResourceEntry* entry = type->findOrCreateEntry(name.entry);
- if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
- diag->error(DiagMessage(value->getSource())
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but resource already has ID "
- << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
- return false;
- }
-
- ResourceConfigValue* configValue = entry->findOrCreateValue(config, product);
- if (!configValue->value) {
- // Resource does not exist, add it now.
- configValue->value = std::move(value);
-
- } else {
- switch (conflictResolver(configValue->value.get(), value.get())) {
- case CollisionResult::kTakeNew:
- // Take the incoming value.
- configValue->value = std::move(value);
- break;
-
- case CollisionResult::kConflict:
- diag->error(DiagMessage(value->getSource())
- << "duplicate value for resource '" << name << "' "
- << "with config '" << config << "'");
- diag->error(DiagMessage(configValue->value->getSource())
- << "resource previously defined here");
- return false;
-
- case CollisionResult::kKeepOriginal:
- break;
- }
- }
-
- if (resId.isValid()) {
- package->id = resId.packageId();
- type->id = resId.typeId();
- entry->id = resId.entryId();
- }
- return true;
+ if (res_id.is_valid()) {
+ package->id = res_id.package_id();
+ type->id = res_id.type_id();
+ entry->id = res_id.entry_id();
+ }
+ return true;
}
-bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId& resId,
+bool ResourceTable::SetSymbolState(const ResourceNameRef& name,
+ const ResourceId& res_id,
const Symbol& symbol, IDiagnostics* diag) {
- return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
+ return SetSymbolStateImpl(name, res_id, symbol, kValidNameChars, diag);
}
-bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name,
- const ResourceId& resId,
- const Symbol& symbol, IDiagnostics* diag) {
- return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag);
+bool ResourceTable::SetSymbolStateAllowMangled(const ResourceNameRef& name,
+ const ResourceId& res_id,
+ const Symbol& symbol,
+ IDiagnostics* diag) {
+ return SetSymbolStateImpl(name, res_id, symbol, kValidNameMangledChars, diag);
}
-bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId& resId,
- const Symbol& symbol, const char* validChars,
+bool ResourceTable::SetSymbolStateImpl(const ResourceNameRef& name,
+ const ResourceId& res_id,
+ const Symbol& symbol,
+ const char* valid_chars,
IDiagnostics* diag) {
- assert(diag && "diagnostics can't be nullptr");
+ CHECK(diag != nullptr);
- auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
- if (badCharIter != name.entry.end()) {
- diag->error(DiagMessage(symbol.source)
- << "resource '"
- << name
- << "' has invalid entry name '"
- << name.entry
- << "'. Invalid character '"
- << StringPiece(badCharIter, 1)
- << "'");
- return false;
- }
+ auto bad_char_iter =
+ util::FindNonAlphaNumericAndNotInSet(name.entry, valid_chars);
+ if (bad_char_iter != name.entry.end()) {
+ diag->Error(DiagMessage(symbol.source)
+ << "resource '" << name << "' has invalid entry name '"
+ << name.entry << "'. Invalid character '"
+ << StringPiece(bad_char_iter, 1) << "'");
+ return false;
+ }
- ResourceTablePackage* package = findOrCreatePackage(name.package);
- if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
- diag->error(DiagMessage(symbol.source)
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but package '"
- << package->name
- << "' already has ID "
- << std::hex << (int) package->id.value() << std::dec);
- return false;
- }
+ ResourceTablePackage* package = FindOrCreatePackage(name.package);
+ if (res_id.is_valid() && package->id &&
+ package->id.value() != res_id.package_id()) {
+ diag->Error(DiagMessage(symbol.source)
+ << "trying to add resource '" << name << "' with ID " << res_id
+ << " but package '" << package->name << "' already has ID "
+ << std::hex << (int)package->id.value() << std::dec);
+ return false;
+ }
- ResourceTableType* type = package->findOrCreateType(name.type);
- if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
- diag->error(DiagMessage(symbol.source)
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but type '"
- << type->type
- << "' already has ID "
- << std::hex << (int) type->id.value() << std::dec);
- return false;
- }
+ ResourceTableType* type = package->FindOrCreateType(name.type);
+ if (res_id.is_valid() && type->id && type->id.value() != res_id.type_id()) {
+ diag->Error(DiagMessage(symbol.source)
+ << "trying to add resource '" << name << "' with ID " << res_id
+ << " but type '" << type->type << "' already has ID "
+ << std::hex << (int)type->id.value() << std::dec);
+ return false;
+ }
- ResourceEntry* entry = type->findOrCreateEntry(name.entry);
- if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
- diag->error(DiagMessage(symbol.source)
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but resource already has ID "
- << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
- return false;
- }
+ ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
+ if (res_id.is_valid() && entry->id &&
+ entry->id.value() != res_id.entry_id()) {
+ diag->Error(DiagMessage(symbol.source)
+ << "trying to add resource '" << name << "' with ID " << res_id
+ << " but resource already has ID "
+ << ResourceId(package->id.value(), type->id.value(),
+ entry->id.value()));
+ return false;
+ }
- if (resId.isValid()) {
- package->id = resId.packageId();
- type->id = resId.typeId();
- entry->id = resId.entryId();
- }
+ if (res_id.is_valid()) {
+ package->id = res_id.package_id();
+ type->id = res_id.type_id();
+ entry->id = res_id.entry_id();
+ }
- // Only mark the type state as public, it doesn't care about being private.
- if (symbol.state == SymbolState::kPublic) {
- type->symbolStatus.state = SymbolState::kPublic;
- }
+ // Only mark the type state as public, it doesn't care about being private.
+ if (symbol.state == SymbolState::kPublic) {
+ type->symbol_status.state = SymbolState::kPublic;
+ }
- if (symbol.state == SymbolState::kUndefined &&
- entry->symbolStatus.state != SymbolState::kUndefined) {
- // We can't undefine a symbol (remove its visibility). Ignore.
- return true;
- }
-
- if (symbol.state == SymbolState::kPrivate &&
- entry->symbolStatus.state == SymbolState::kPublic) {
- // We can't downgrade public to private. Ignore.
- return true;
- }
-
- entry->symbolStatus = std::move(symbol);
+ if (symbol.state == SymbolState::kUndefined &&
+ entry->symbol_status.state != SymbolState::kUndefined) {
+ // We can't undefine a symbol (remove its visibility). Ignore.
return true;
+ }
+
+ if (symbol.state == SymbolState::kPrivate &&
+ entry->symbol_status.state == SymbolState::kPublic) {
+ // We can't downgrade public to private. Ignore.
+ return true;
+ }
+
+ entry->symbol_status = std::move(symbol);
+ return true;
}
-Maybe<ResourceTable::SearchResult>
-ResourceTable::findResource(const ResourceNameRef& name) {
- ResourceTablePackage* package = findPackage(name.package);
- if (!package) {
- return {};
- }
+Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(
+ const ResourceNameRef& name) {
+ ResourceTablePackage* package = FindPackage(name.package);
+ if (!package) {
+ return {};
+ }
- ResourceTableType* type = package->findType(name.type);
- if (!type) {
- return {};
- }
+ ResourceTableType* type = package->FindType(name.type);
+ if (!type) {
+ return {};
+ }
- ResourceEntry* entry = type->findEntry(name.entry);
- if (!entry) {
- return {};
- }
- return SearchResult{ package, type, entry };
+ ResourceEntry* entry = type->FindEntry(name.entry);
+ if (!entry) {
+ return {};
+ }
+ return SearchResult{package, type, entry};
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 6c246d0..a0c3217 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -37,42 +37,43 @@
namespace aapt {
enum class SymbolState {
- kUndefined,
- kPrivate,
- kPublic,
+ kUndefined,
+ kPrivate,
+ kPublic,
};
/**
* The Public status of a resource.
*/
struct Symbol {
- SymbolState state = SymbolState::kUndefined;
- Source source;
- std::string comment;
+ SymbolState state = SymbolState::kUndefined;
+ Source source;
+ std::string comment;
};
class ResourceConfigValue {
-public:
- /**
- * The configuration for which this value is defined.
- */
- const ConfigDescription config;
+ public:
+ /**
+ * The configuration for which this value is defined.
+ */
+ const ConfigDescription config;
- /**
- * The product for which this value is defined.
- */
- const std::string product;
+ /**
+ * The product for which this value is defined.
+ */
+ const std::string product;
- /**
- * The actual Value.
- */
- std::unique_ptr<Value> value;
+ /**
+ * The actual Value.
+ */
+ std::unique_ptr<Value> value;
- ResourceConfigValue(const ConfigDescription& config, const StringPiece& product) :
- config(config), product(product.toString()) { }
+ ResourceConfigValue(const ConfigDescription& config,
+ const StringPiece& product)
+ : config(config), product(product.ToString()) {}
-private:
- DISALLOW_COPY_AND_ASSIGN(ResourceConfigValue);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceConfigValue);
};
/**
@@ -80,42 +81,44 @@
* varying values for each defined configuration.
*/
class ResourceEntry {
-public:
- /**
- * The name of the resource. Immutable, as
- * this determines the order of this resource
- * when doing lookups.
- */
- const std::string name;
+ public:
+ /**
+ * The name of the resource. Immutable, as
+ * this determines the order of this resource
+ * when doing lookups.
+ */
+ const std::string name;
- /**
- * The entry ID for this resource.
- */
- Maybe<uint16_t> id;
+ /**
+ * The entry ID for this resource.
+ */
+ Maybe<uint16_t> id;
- /**
- * Whether this resource is public (and must maintain the same entry ID across builds).
- */
- Symbol symbolStatus;
+ /**
+ * Whether this resource is public (and must maintain the same entry ID across
+ * builds).
+ */
+ Symbol symbol_status;
- /**
- * The resource's values for each configuration.
- */
- std::vector<std::unique_ptr<ResourceConfigValue>> values;
+ /**
+ * The resource's values for each configuration.
+ */
+ std::vector<std::unique_ptr<ResourceConfigValue>> values;
- explicit ResourceEntry(const StringPiece& name) : name(name.toString()) { }
+ explicit ResourceEntry(const StringPiece& name) : name(name.ToString()) {}
- ResourceConfigValue* findValue(const ConfigDescription& config);
- ResourceConfigValue* findValue(const ConfigDescription& config, const StringPiece& product);
- ResourceConfigValue* findOrCreateValue(const ConfigDescription& config,
- const StringPiece& product);
- std::vector<ResourceConfigValue*> findAllValues(const ConfigDescription& config);
- std::vector<ResourceConfigValue*> findValuesIf(
- const std::function<bool(ResourceConfigValue*)>& f);
+ ResourceConfigValue* FindValue(const ConfigDescription& config);
+ ResourceConfigValue* FindValue(const ConfigDescription& config,
+ const StringPiece& product);
+ ResourceConfigValue* FindOrCreateValue(const ConfigDescription& config,
+ const StringPiece& product);
+ std::vector<ResourceConfigValue*> findAllValues(
+ const ConfigDescription& config);
+ std::vector<ResourceConfigValue*> FindValuesIf(
+ const std::function<bool(ResourceConfigValue*)>& f);
-
-private:
- DISALLOW_COPY_AND_ASSIGN(ResourceEntry);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceEntry);
};
/**
@@ -123,58 +126,53 @@
* for this type.
*/
class ResourceTableType {
-public:
- /**
- * The logical type of resource (string, drawable, layout, etc.).
- */
- const ResourceType type;
+ public:
+ /**
+ * The logical type of resource (string, drawable, layout, etc.).
+ */
+ const ResourceType type;
- /**
- * The type ID for this resource.
- */
- Maybe<uint8_t> id;
+ /**
+ * The type ID for this resource.
+ */
+ Maybe<uint8_t> id;
- /**
- * Whether this type is public (and must maintain the same
- * type ID across builds).
- */
- Symbol symbolStatus;
+ /**
+ * Whether this type is public (and must maintain the same
+ * type ID across builds).
+ */
+ Symbol symbol_status;
- /**
- * List of resources for this type.
- */
- std::vector<std::unique_ptr<ResourceEntry>> entries;
+ /**
+ * List of resources for this type.
+ */
+ std::vector<std::unique_ptr<ResourceEntry>> entries;
- explicit ResourceTableType(const ResourceType type) : type(type) { }
+ explicit ResourceTableType(const ResourceType type) : type(type) {}
- ResourceEntry* findEntry(const StringPiece& name);
- ResourceEntry* findOrCreateEntry(const StringPiece& name);
+ ResourceEntry* FindEntry(const StringPiece& name);
+ ResourceEntry* FindOrCreateEntry(const StringPiece& name);
-private:
- DISALLOW_COPY_AND_ASSIGN(ResourceTableType);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceTableType);
};
-enum class PackageType {
- System,
- Vendor,
- App,
- Dynamic
-};
+enum class PackageType { System, Vendor, App, Dynamic };
class ResourceTablePackage {
-public:
- PackageType type = PackageType::App;
- Maybe<uint8_t> id;
- std::string name;
+ public:
+ PackageType type = PackageType::App;
+ Maybe<uint8_t> id;
+ std::string name;
- std::vector<std::unique_ptr<ResourceTableType>> types;
+ std::vector<std::unique_ptr<ResourceTableType>> types;
- ResourceTablePackage() = default;
- ResourceTableType* findType(ResourceType type);
- ResourceTableType* findOrCreateType(const ResourceType type);
+ ResourceTablePackage() = default;
+ ResourceTableType* FindType(ResourceType type);
+ ResourceTableType* FindOrCreateType(const ResourceType type);
-private:
- DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
};
/**
@@ -182,140 +180,129 @@
* flattened into a binary resource table (resources.arsc).
*/
class ResourceTable {
-public:
- ResourceTable() = default;
+ public:
+ ResourceTable() = default;
- enum class CollisionResult {
- kKeepOriginal,
- kConflict,
- kTakeNew
- };
+ enum class CollisionResult { kKeepOriginal, kConflict, kTakeNew };
- using CollisionResolverFunc = std::function<CollisionResult(Value*, Value*)>;
+ using CollisionResolverFunc = std::function<CollisionResult(Value*, Value*)>;
- /**
- * When a collision of resources occurs, this method decides which value to keep.
- */
- static CollisionResult resolveValueCollision(Value* existing, Value* incoming);
+ /**
+ * When a collision of resources occurs, this method decides which value to
+ * keep.
+ */
+ static CollisionResult ResolveValueCollision(Value* existing,
+ Value* incoming);
- bool addResource(const ResourceNameRef& name,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag);
+ bool AddResource(const ResourceNameRef& name, const ConfigDescription& config,
+ const StringPiece& product, std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- bool addResource(const ResourceNameRef& name,
- const ResourceId& resId,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag);
+ bool AddResource(const ResourceNameRef& name, const ResourceId& res_id,
+ const ConfigDescription& config, const StringPiece& product,
+ std::unique_ptr<Value> value, IDiagnostics* diag);
- bool addFileReference(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Source& source,
- const StringPiece& path,
- IDiagnostics* diag);
+ bool AddFileReference(const ResourceNameRef& name,
+ const ConfigDescription& config, const Source& source,
+ const StringPiece& path, IDiagnostics* diag);
- bool addFileReferenceAllowMangled(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Source& source,
- const StringPiece& path,
- io::IFile* file,
- IDiagnostics* diag);
-
- /**
- * Same as addResource, but doesn't verify the validity of the name. This is used
- * when loading resources from an existing binary resource table that may have mangled
- * names.
- */
- bool addResourceAllowMangled(const ResourceNameRef& name,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag);
-
- bool addResourceAllowMangled(const ResourceNameRef& name,
- const ResourceId& id,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag);
-
- bool setSymbolState(const ResourceNameRef& name,
- const ResourceId& resId,
- const Symbol& symbol,
- IDiagnostics* diag);
-
- bool setSymbolStateAllowMangled(const ResourceNameRef& name,
- const ResourceId& resId,
- const Symbol& symbol,
+ bool AddFileReferenceAllowMangled(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source,
+ const StringPiece& path, io::IFile* file,
IDiagnostics* diag);
- struct SearchResult {
- ResourceTablePackage* package;
- ResourceTableType* type;
- ResourceEntry* entry;
- };
+ /**
+ * Same as AddResource, but doesn't verify the validity of the name. This is
+ * used
+ * when loading resources from an existing binary resource table that may have
+ * mangled
+ * names.
+ */
+ bool AddResourceAllowMangled(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- Maybe<SearchResult> findResource(const ResourceNameRef& name);
+ bool AddResourceAllowMangled(const ResourceNameRef& name,
+ const ResourceId& id,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- /**
- * The string pool used by this resource table. Values that reference strings must use
- * this pool to create their strings.
- *
- * NOTE: `stringPool` must come before `packages` so that it is destroyed after.
- * When `string pool` references are destroyed (as they will be when `packages`
- * is destroyed), they decrement a refCount, which would cause invalid
- * memory access if the pool was already destroyed.
- */
- StringPool stringPool;
+ bool SetSymbolState(const ResourceNameRef& name, const ResourceId& res_id,
+ const Symbol& symbol, IDiagnostics* diag);
- /**
- * The list of packages in this table, sorted alphabetically by package name.
- */
- std::vector<std::unique_ptr<ResourceTablePackage>> packages;
+ bool SetSymbolStateAllowMangled(const ResourceNameRef& name,
+ const ResourceId& res_id,
+ const Symbol& symbol, IDiagnostics* diag);
- /**
- * Returns the package struct with the given name, or nullptr if such a package does not
- * exist. The empty string is a valid package and typically is used to represent the
- * 'current' package before it is known to the ResourceTable.
- */
- ResourceTablePackage* findPackage(const StringPiece& name);
+ struct SearchResult {
+ ResourceTablePackage* package;
+ ResourceTableType* type;
+ ResourceEntry* entry;
+ };
- ResourceTablePackage* findPackageById(uint8_t id);
+ Maybe<SearchResult> FindResource(const ResourceNameRef& name);
- ResourceTablePackage* createPackage(const StringPiece& name, Maybe<uint8_t> id = {});
+ /**
+ * The string pool used by this resource table. Values that reference strings
+ * must use
+ * this pool to create their strings.
+ *
+ * NOTE: `string_pool` must come before `packages` so that it is destroyed
+ * after.
+ * When `string_pool` references are destroyed (as they will be when
+ * `packages`
+ * is destroyed), they decrement a refCount, which would cause invalid
+ * memory access if the pool was already destroyed.
+ */
+ StringPool string_pool;
-private:
- ResourceTablePackage* findOrCreatePackage(const StringPiece& name);
+ /**
+ * The list of packages in this table, sorted alphabetically by package name.
+ */
+ std::vector<std::unique_ptr<ResourceTablePackage>> packages;
- bool addResourceImpl(const ResourceNameRef& name,
- const ResourceId& resId,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- const char* validChars,
- const CollisionResolverFunc& conflictResolver,
- IDiagnostics* diag);
+ /**
+ * Returns the package struct with the given name, or nullptr if such a
+ * package does not
+ * exist. The empty string is a valid package and typically is used to
+ * represent the
+ * 'current' package before it is known to the ResourceTable.
+ */
+ ResourceTablePackage* FindPackage(const StringPiece& name);
- bool addFileReferenceImpl(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Source& source,
- const StringPiece& path,
- io::IFile* file,
- const char* validChars,
- IDiagnostics* diag);
+ ResourceTablePackage* FindPackageById(uint8_t id);
- bool setSymbolStateImpl(const ResourceNameRef& name,
- const ResourceId& resId,
- const Symbol& symbol,
- const char* validChars,
+ ResourceTablePackage* CreatePackage(const StringPiece& name,
+ Maybe<uint8_t> id = {});
+
+ private:
+ ResourceTablePackage* FindOrCreatePackage(const StringPiece& name);
+
+ bool AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
+ const ConfigDescription& config,
+ const StringPiece& product, std::unique_ptr<Value> value,
+ const char* valid_chars,
+ const CollisionResolverFunc& conflict_resolver,
+ IDiagnostics* diag);
+
+ bool AddFileReferenceImpl(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source, const StringPiece& path,
+ io::IFile* file, const char* valid_chars,
IDiagnostics* diag);
- DISALLOW_COPY_AND_ASSIGN(ResourceTable);
+ bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
+ const Symbol& symbol, const char* valid_chars,
+ IDiagnostics* diag);
+
+ DISALLOW_COPY_AND_ASSIGN(ResourceTable);
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_RESOURCE_TABLE_H
+#endif // AAPT_RESOURCE_TABLE_H
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 4db40a6..cb3699a0 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "Diagnostics.h"
#include "ResourceTable.h"
+#include "Diagnostics.h"
#include "ResourceValues.h"
#include "test/Test.h"
#include "util/Util.h"
@@ -27,124 +27,113 @@
namespace aapt {
TEST(ResourceTableTest, FailToAddResourceWithBadName) {
- ResourceTable table;
+ ResourceTable table;
- EXPECT_FALSE(table.addResource(
- test::parseNameOrDie("android:id/hey,there"),
- ConfigDescription{}, "",
- test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
- test::getDiagnostics()));
+ EXPECT_FALSE(table.AddResource(
+ test::ParseNameOrDie("android:id/hey,there"), ConfigDescription{}, "",
+ test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(),
+ test::GetDiagnostics()));
- EXPECT_FALSE(table.addResource(
- test::parseNameOrDie("android:id/hey:there"),
- ConfigDescription{}, "",
- test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
- test::getDiagnostics()));
+ EXPECT_FALSE(table.AddResource(
+ test::ParseNameOrDie("android:id/hey:there"), ConfigDescription{}, "",
+ test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(),
+ test::GetDiagnostics()));
}
TEST(ResourceTableTest, AddOneResource) {
- ResourceTable table;
+ ResourceTable table;
- EXPECT_TRUE(table.addResource(
- test::parseNameOrDie("android:attr/id"),
- ConfigDescription{}, "",
- test::ValueBuilder<Id>().setSource("test/path/file.xml", 23u).build(),
- test::getDiagnostics()));
+ EXPECT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:attr/id"), ConfigDescription{}, "",
+ test::ValueBuilder<Id>().SetSource("test/path/file.xml", 23u).Build(),
+ test::GetDiagnostics()));
- ASSERT_NE(nullptr, test::getValue<Id>(&table, "android:attr/id"));
+ ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:attr/id"));
}
TEST(ResourceTableTest, AddMultipleResources) {
- ResourceTable table;
+ ResourceTable table;
- ConfigDescription config;
- ConfigDescription languageConfig;
- memcpy(languageConfig.language, "pl", sizeof(languageConfig.language));
+ ConfigDescription config;
+ ConfigDescription language_config;
+ memcpy(language_config.language, "pl", sizeof(language_config.language));
- EXPECT_TRUE(table.addResource(
- test::parseNameOrDie("android:attr/layout_width"),
- config, "",
- test::ValueBuilder<Id>().setSource("test/path/file.xml", 10u).build(),
- test::getDiagnostics()));
+ EXPECT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:attr/layout_width"), config, "",
+ test::ValueBuilder<Id>().SetSource("test/path/file.xml", 10u).Build(),
+ test::GetDiagnostics()));
- EXPECT_TRUE(table.addResource(
- test::parseNameOrDie("android:attr/id"),
- config, "",
- test::ValueBuilder<Id>().setSource("test/path/file.xml", 12u).build(),
- test::getDiagnostics()));
+ EXPECT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:attr/id"), config, "",
+ test::ValueBuilder<Id>().SetSource("test/path/file.xml", 12u).Build(),
+ test::GetDiagnostics()));
- EXPECT_TRUE(table.addResource(
- test::parseNameOrDie("android:string/ok"),
- config, "",
- test::ValueBuilder<Id>().setSource("test/path/file.xml", 14u).build(),
- test::getDiagnostics()));
+ EXPECT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/ok"), config, "",
+ test::ValueBuilder<Id>().SetSource("test/path/file.xml", 14u).Build(),
+ test::GetDiagnostics()));
- EXPECT_TRUE(table.addResource(
- test::parseNameOrDie("android:string/ok"),
- languageConfig, "",
- test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
- .setSource("test/path/file.xml", 20u)
- .build(),
- test::getDiagnostics()));
+ EXPECT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/ok"), language_config, "",
+ test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
+ .SetSource("test/path/file.xml", 20u)
+ .Build(),
+ test::GetDiagnostics()));
- ASSERT_NE(nullptr, test::getValue<Id>(&table, "android:attr/layout_width"));
- ASSERT_NE(nullptr, test::getValue<Id>(&table, "android:attr/id"));
- ASSERT_NE(nullptr, test::getValue<Id>(&table, "android:string/ok"));
- ASSERT_NE(nullptr, test::getValueForConfig<BinaryPrimitive>(&table, "android:string/ok",
- languageConfig));
+ ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:attr/layout_width"));
+ ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:attr/id"));
+ ASSERT_NE(nullptr, test::GetValue<Id>(&table, "android:string/ok"));
+ ASSERT_NE(nullptr, test::GetValueForConfig<BinaryPrimitive>(
+ &table, "android:string/ok", language_config));
}
TEST(ResourceTableTest, OverrideWeakResourceValue) {
- ResourceTable table;
+ ResourceTable table;
- ASSERT_TRUE(table.addResource(
- test::parseNameOrDie("android:attr/foo"),
- ConfigDescription{}, "",
- util::make_unique<Attribute>(true),
- test::getDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
+ util::make_unique<Attribute>(true), test::GetDiagnostics()));
- Attribute* attr = test::getValue<Attribute>(&table, "android:attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_TRUE(attr->isWeak());
+ Attribute* attr = test::GetValue<Attribute>(&table, "android:attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_TRUE(attr->IsWeak());
- ASSERT_TRUE(table.addResource(
- test::parseNameOrDie("android:attr/foo"),
- ConfigDescription{}, "",
- util::make_unique<Attribute>(false),
- test::getDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
+ util::make_unique<Attribute>(false), test::GetDiagnostics()));
- attr = test::getValue<Attribute>(&table, "android:attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_FALSE(attr->isWeak());
+ attr = test::GetValue<Attribute>(&table, "android:attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_FALSE(attr->IsWeak());
}
TEST(ResourceTableTest, ProductVaryingValues) {
- ResourceTable table;
+ ResourceTable table;
- EXPECT_TRUE(table.addResource(test::parseNameOrDie("android:string/foo"),
- test::parseConfigOrDie("land"), "tablet",
- util::make_unique<Id>(),
- test::getDiagnostics()));
- EXPECT_TRUE(table.addResource(test::parseNameOrDie("android:string/foo"),
- test::parseConfigOrDie("land"), "phone",
- util::make_unique<Id>(),
- test::getDiagnostics()));
+ EXPECT_TRUE(table.AddResource(test::ParseNameOrDie("android:string/foo"),
+ test::ParseConfigOrDie("land"), "tablet",
+ util::make_unique<Id>(),
+ test::GetDiagnostics()));
+ EXPECT_TRUE(table.AddResource(test::ParseNameOrDie("android:string/foo"),
+ test::ParseConfigOrDie("land"), "phone",
+ util::make_unique<Id>(),
+ test::GetDiagnostics()));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/foo",
- test::parseConfigOrDie("land"),
- "tablet"));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/foo",
- test::parseConfigOrDie("land"),
- "phone"));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
+ &table, "android:string/foo",
+ test::ParseConfigOrDie("land"), "tablet"));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
+ &table, "android:string/foo",
+ test::ParseConfigOrDie("land"), "phone"));
- Maybe<ResourceTable::SearchResult> sr = table.findResource(
- test::parseNameOrDie("android:string/foo"));
- AAPT_ASSERT_TRUE(sr);
- std::vector<ResourceConfigValue*> values = sr.value().entry->findAllValues(
- test::parseConfigOrDie("land"));
- ASSERT_EQ(2u, values.size());
- EXPECT_EQ(std::string("phone"), values[0]->product);
- EXPECT_EQ(std::string("tablet"), values[1]->product);
+ Maybe<ResourceTable::SearchResult> sr =
+ table.FindResource(test::ParseNameOrDie("android:string/foo"));
+ AAPT_ASSERT_TRUE(sr);
+ std::vector<ResourceConfigValue*> values =
+ sr.value().entry->findAllValues(test::ParseConfigOrDie("land"));
+ ASSERT_EQ(2u, values.size());
+ EXPECT_EQ(std::string("phone"), values[0]->product);
+ EXPECT_EQ(std::string("tablet"), values[1]->product);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 73a194e..fce9b33 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -14,201 +14,211 @@
* limitations under the License.
*/
-#include "NameMangler.h"
#include "ResourceUtils.h"
+
+#include <sstream>
+
+#include "androidfw/ResourceTypes.h"
+
+#include "NameMangler.h"
#include "SdkConstants.h"
#include "flatten/ResourceTypeExtensions.h"
#include "util/Files.h"
#include "util/Util.h"
-#include <androidfw/ResourceTypes.h>
-#include <sstream>
-
namespace aapt {
namespace ResourceUtils {
-Maybe<ResourceName> toResourceName(const android::ResTable::resource_name& nameIn) {
- ResourceName nameOut;
- if (!nameIn.package) {
- return {};
- }
+Maybe<ResourceName> ToResourceName(
+ const android::ResTable::resource_name& name_in) {
+ ResourceName name_out;
+ if (!name_in.package) {
+ return {};
+ }
- nameOut.package = util::utf16ToUtf8(StringPiece16(nameIn.package, nameIn.packageLen));
+ name_out.package =
+ util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
- const ResourceType* type;
- if (nameIn.type) {
- type = parseResourceType(util::utf16ToUtf8(StringPiece16(nameIn.type, nameIn.typeLen)));
- } else if (nameIn.type8) {
- type = parseResourceType(StringPiece(nameIn.type8, nameIn.typeLen));
- } else {
- return {};
- }
+ const ResourceType* type;
+ if (name_in.type) {
+ type = ParseResourceType(
+ util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
+ } else if (name_in.type8) {
+ type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
+ } else {
+ return {};
+ }
- if (!type) {
- return {};
- }
+ if (!type) {
+ return {};
+ }
- nameOut.type = *type;
+ name_out.type = *type;
- if (nameIn.name) {
- nameOut.entry = util::utf16ToUtf8(StringPiece16(nameIn.name, nameIn.nameLen));
- } else if (nameIn.name8) {
- nameOut.entry = StringPiece(nameIn.name8, nameIn.nameLen).toString();
- } else {
- return {};
- }
- return nameOut;
+ if (name_in.name) {
+ name_out.entry =
+ util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen));
+ } else if (name_in.name8) {
+ name_out.entry = StringPiece(name_in.name8, name_in.nameLen).ToString();
+ } else {
+ return {};
+ }
+ return name_out;
}
-bool extractResourceName(const StringPiece& str, StringPiece* outPackage,
- StringPiece* outType, StringPiece* outEntry) {
- bool hasPackageSeparator = false;
- bool hasTypeSeparator = false;
- const char* start = str.data();
- const char* end = start + str.size();
- const char* current = start;
- while (current != end) {
- if (outType->size() == 0 && *current == '/') {
- hasTypeSeparator = true;
- outType->assign(start, current - start);
- start = current + 1;
- } else if (outPackage->size() == 0 && *current == ':') {
- hasPackageSeparator = true;
- outPackage->assign(start, current - start);
- start = current + 1;
- }
- current++;
+bool ExtractResourceName(const StringPiece& str, StringPiece* out_package,
+ StringPiece* out_type, StringPiece* out_entry) {
+ bool has_package_separator = false;
+ bool has_type_separator = false;
+ const char* start = str.data();
+ const char* end = start + str.size();
+ const char* current = start;
+ while (current != end) {
+ if (out_type->size() == 0 && *current == '/') {
+ has_type_separator = true;
+ out_type->assign(start, current - start);
+ start = current + 1;
+ } else if (out_package->size() == 0 && *current == ':') {
+ has_package_separator = true;
+ out_package->assign(start, current - start);
+ start = current + 1;
}
- outEntry->assign(start, end - start);
+ current++;
+ }
+ out_entry->assign(start, end - start);
- return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty());
+ return !(has_package_separator && out_package->empty()) &&
+ !(has_type_separator && out_type->empty());
}
-bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef, bool* outPrivate) {
- if (str.empty()) {
- return false;
+bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
+ bool* out_private) {
+ if (str.empty()) {
+ return false;
+ }
+
+ size_t offset = 0;
+ bool priv = false;
+ if (str.data()[0] == '*') {
+ priv = true;
+ offset = 1;
+ }
+
+ StringPiece package;
+ StringPiece type;
+ StringPiece entry;
+ if (!ExtractResourceName(str.substr(offset, str.size() - offset), &package,
+ &type, &entry)) {
+ return false;
+ }
+
+ const ResourceType* parsed_type = ParseResourceType(type);
+ if (!parsed_type) {
+ return false;
+ }
+
+ if (entry.empty()) {
+ return false;
+ }
+
+ if (out_ref) {
+ out_ref->package = package;
+ out_ref->type = *parsed_type;
+ out_ref->entry = entry;
+ }
+
+ if (out_private) {
+ *out_private = priv;
+ }
+ return true;
+}
+
+bool ParseReference(const StringPiece& str, ResourceNameRef* out_ref,
+ bool* out_create, bool* out_private) {
+ StringPiece trimmed_str(util::TrimWhitespace(str));
+ if (trimmed_str.empty()) {
+ return false;
+ }
+
+ bool create = false;
+ bool priv = false;
+ if (trimmed_str.data()[0] == '@') {
+ size_t offset = 1;
+ if (trimmed_str.data()[1] == '+') {
+ create = true;
+ offset += 1;
}
- size_t offset = 0;
- bool priv = false;
- if (str.data()[0] == '*') {
- priv = true;
- offset = 1;
+ ResourceNameRef name;
+ if (!ParseResourceName(
+ trimmed_str.substr(offset, trimmed_str.size() - offset), &name,
+ &priv)) {
+ return false;
}
+ if (create && priv) {
+ return false;
+ }
+
+ if (create && name.type != ResourceType::kId) {
+ return false;
+ }
+
+ if (out_ref) {
+ *out_ref = name;
+ }
+
+ if (out_create) {
+ *out_create = create;
+ }
+
+ if (out_private) {
+ *out_private = priv;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool IsReference(const StringPiece& str) {
+ return ParseReference(str, nullptr, nullptr, nullptr);
+}
+
+bool ParseAttributeReference(const StringPiece& str, ResourceNameRef* out_ref) {
+ StringPiece trimmed_str(util::TrimWhitespace(str));
+ if (trimmed_str.empty()) {
+ return false;
+ }
+
+ if (*trimmed_str.data() == '?') {
StringPiece package;
StringPiece type;
StringPiece entry;
- if (!extractResourceName(str.substr(offset, str.size() - offset), &package, &type, &entry)) {
- return false;
+ if (!ExtractResourceName(trimmed_str.substr(1, trimmed_str.size() - 1),
+ &package, &type, &entry)) {
+ return false;
}
- const ResourceType* parsedType = parseResourceType(type);
- if (!parsedType) {
- return false;
+ if (!type.empty() && type != "attr") {
+ return false;
}
if (entry.empty()) {
- return false;
+ return false;
}
- if (outRef) {
- outRef->package = package;
- outRef->type = *parsedType;
- outRef->entry = entry;
- }
-
- if (outPrivate) {
- *outPrivate = priv;
+ if (out_ref) {
+ out_ref->package = package;
+ out_ref->type = ResourceType::kAttr;
+ out_ref->entry = entry;
}
return true;
+ }
+ return false;
}
-bool parseReference(const StringPiece& str, ResourceNameRef* outRef, bool* outCreate,
- bool* outPrivate) {
- StringPiece trimmedStr(util::trimWhitespace(str));
- if (trimmedStr.empty()) {
- return false;
- }
-
- bool create = false;
- bool priv = false;
- if (trimmedStr.data()[0] == '@') {
- size_t offset = 1;
- if (trimmedStr.data()[1] == '+') {
- create = true;
- offset += 1;
- }
-
- ResourceNameRef name;
- if (!parseResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset),
- &name, &priv)) {
- return false;
- }
-
- if (create && priv) {
- return false;
- }
-
- if (create && name.type != ResourceType::kId) {
- return false;
- }
-
- if (outRef) {
- *outRef = name;
- }
-
- if (outCreate) {
- *outCreate = create;
- }
-
- if (outPrivate) {
- *outPrivate = priv;
- }
- return true;
- }
- return false;
-}
-
-bool isReference(const StringPiece& str) {
- return parseReference(str, nullptr, nullptr, nullptr);
-}
-
-bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) {
- StringPiece trimmedStr(util::trimWhitespace(str));
- if (trimmedStr.empty()) {
- return false;
- }
-
- if (*trimmedStr.data() == '?') {
- StringPiece package;
- StringPiece type;
- StringPiece entry;
- if (!extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1),
- &package, &type, &entry)) {
- return false;
- }
-
- if (!type.empty() && type != "attr") {
- return false;
- }
-
- if (entry.empty()) {
- return false;
- }
-
- if (outRef) {
- outRef->package = package;
- outRef->type = ResourceType::kAttr;
- outRef->entry = entry;
- }
- return true;
- }
- return false;
-}
-
-bool isAttributeReference(const StringPiece& str) {
- return parseAttributeReference(str, nullptr);
+bool IsAttributeReference(const StringPiece& str) {
+ return ParseAttributeReference(str, nullptr);
}
/*
@@ -219,463 +229,473 @@
* <[*]package>:[style/]<entry>
* [[*]package:style/]<entry>
*/
-Maybe<Reference> parseStyleParentReference(const StringPiece& str, std::string* outError) {
- if (str.empty()) {
- return {};
- }
-
- StringPiece name = str;
-
- bool hasLeadingIdentifiers = false;
- bool privateRef = false;
-
- // Skip over these identifiers. A style's parent is a normal reference.
- if (name.data()[0] == '@' || name.data()[0] == '?') {
- hasLeadingIdentifiers = true;
- name = name.substr(1, name.size() - 1);
- }
-
- if (name.data()[0] == '*') {
- privateRef = true;
- name = name.substr(1, name.size() - 1);
- }
-
- ResourceNameRef ref;
- ref.type = ResourceType::kStyle;
-
- StringPiece typeStr;
- extractResourceName(name, &ref.package, &typeStr, &ref.entry);
- if (!typeStr.empty()) {
- // If we have a type, make sure it is a Style.
- const ResourceType* parsedType = parseResourceType(typeStr);
- if (!parsedType || *parsedType != ResourceType::kStyle) {
- std::stringstream err;
- err << "invalid resource type '" << typeStr << "' for parent of style";
- *outError = err.str();
- return {};
- }
- }
-
- if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
- std::stringstream err;
- err << "invalid parent reference '" << str << "'";
- *outError = err.str();
- return {};
- }
-
- Reference result(ref);
- result.privateReference = privateRef;
- return result;
-}
-
-Maybe<Reference> parseXmlAttributeName(const StringPiece& str) {
- StringPiece trimmedStr = util::trimWhitespace(str);
- const char* start = trimmedStr.data();
- const char* const end = start + trimmedStr.size();
- const char* p = start;
-
- Reference ref;
- if (p != end && *p == '*') {
- ref.privateReference = true;
- start++;
- p++;
- }
-
- StringPiece package;
- StringPiece name;
- while (p != end) {
- if (*p == ':') {
- package = StringPiece(start, p - start);
- name = StringPiece(p + 1, end - (p + 1));
- break;
- }
- p++;
- }
-
- ref.name = ResourceName(package.toString(), ResourceType::kAttr,
- name.empty() ? trimmedStr.toString() : name.toString());
- return Maybe<Reference>(std::move(ref));
-}
-
-std::unique_ptr<Reference> tryParseReference(const StringPiece& str, bool* outCreate) {
- ResourceNameRef ref;
- bool privateRef = false;
- if (parseReference(str, &ref, outCreate, &privateRef)) {
- std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
- value->privateReference = privateRef;
- return value;
- }
-
- if (parseAttributeReference(str, &ref)) {
- if (outCreate) {
- *outCreate = false;
- }
- return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
- }
+Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
+ std::string* out_error) {
+ if (str.empty()) {
return {};
-}
+ }
-std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str) {
- StringPiece trimmedStr(util::trimWhitespace(str));
- android::Res_value value = { };
- if (trimmedStr == "@null") {
- // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
- // Instead we set the data type to TYPE_REFERENCE with a value of 0.
- value.dataType = android::Res_value::TYPE_REFERENCE;
- } else if (trimmedStr == "@empty") {
- // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
- value.dataType = android::Res_value::TYPE_NULL;
- value.data = android::Res_value::DATA_NULL_EMPTY;
- } else {
- return {};
- }
- return util::make_unique<BinaryPrimitive>(value);
-}
+ StringPiece name = str;
-std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
- const StringPiece& str) {
- StringPiece trimmedStr(util::trimWhitespace(str));
- for (const Attribute::Symbol& symbol : enumAttr->symbols) {
- // Enum symbols are stored as @package:id/symbol resources,
- // so we need to match against the 'entry' part of the identifier.
- const ResourceName& enumSymbolResourceName = symbol.symbol.name.value();
- if (trimmedStr == enumSymbolResourceName.entry) {
- android::Res_value value = { };
- value.dataType = android::Res_value::TYPE_INT_DEC;
- value.data = symbol.value;
- return util::make_unique<BinaryPrimitive>(value);
- }
+ bool has_leading_identifiers = false;
+ bool private_ref = false;
+
+ // Skip over these identifiers. A style's parent is a normal reference.
+ if (name.data()[0] == '@' || name.data()[0] == '?') {
+ has_leading_identifiers = true;
+ name = name.substr(1, name.size() - 1);
+ }
+
+ if (name.data()[0] == '*') {
+ private_ref = true;
+ name = name.substr(1, name.size() - 1);
+ }
+
+ ResourceNameRef ref;
+ ref.type = ResourceType::kStyle;
+
+ StringPiece type_str;
+ ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
+ if (!type_str.empty()) {
+ // If we have a type, make sure it is a Style.
+ const ResourceType* parsed_type = ParseResourceType(type_str);
+ if (!parsed_type || *parsed_type != ResourceType::kStyle) {
+ std::stringstream err;
+ err << "invalid resource type '" << type_str << "' for parent of style";
+ *out_error = err.str();
+ return {};
}
+ }
+
+ if (!has_leading_identifiers && ref.package.empty() && !type_str.empty()) {
+ std::stringstream err;
+ err << "invalid parent reference '" << str << "'";
+ *out_error = err.str();
return {};
+ }
+
+ Reference result(ref);
+ result.private_reference = private_ref;
+ return result;
}
-std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr,
+Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
+ StringPiece trimmed_str = util::TrimWhitespace(str);
+ const char* start = trimmed_str.data();
+ const char* const end = start + trimmed_str.size();
+ const char* p = start;
+
+ Reference ref;
+ if (p != end && *p == '*') {
+ ref.private_reference = true;
+ start++;
+ p++;
+ }
+
+ StringPiece package;
+ StringPiece name;
+ while (p != end) {
+ if (*p == ':') {
+ package = StringPiece(start, p - start);
+ name = StringPiece(p + 1, end - (p + 1));
+ break;
+ }
+ p++;
+ }
+
+ ref.name =
+ ResourceName(package.ToString(), ResourceType::kAttr,
+ name.empty() ? trimmed_str.ToString() : name.ToString());
+ return Maybe<Reference>(std::move(ref));
+}
+
+std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
+ bool* out_create) {
+ ResourceNameRef ref;
+ bool private_ref = false;
+ if (ParseReference(str, &ref, out_create, &private_ref)) {
+ std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
+ value->private_reference = private_ref;
+ return value;
+ }
+
+ if (ParseAttributeReference(str, &ref)) {
+ if (out_create) {
+ *out_create = false;
+ }
+ return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
+ }
+ return {};
+}
+
+std::unique_ptr<BinaryPrimitive> TryParseNullOrEmpty(const StringPiece& str) {
+ StringPiece trimmed_str(util::TrimWhitespace(str));
+ android::Res_value value = {};
+ if (trimmed_str == "@null") {
+ // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
+ // Instead we set the data type to TYPE_REFERENCE with a value of 0.
+ value.dataType = android::Res_value::TYPE_REFERENCE;
+ } else if (trimmed_str == "@empty") {
+ // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
+ value.dataType = android::Res_value::TYPE_NULL;
+ value.data = android::Res_value::DATA_NULL_EMPTY;
+ } else {
+ return {};
+ }
+ return util::make_unique<BinaryPrimitive>(value);
+}
+
+std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
const StringPiece& str) {
- android::Res_value flags = { };
- flags.dataType = android::Res_value::TYPE_INT_HEX;
- flags.data = 0u;
-
- if (util::trimWhitespace(str).empty()) {
- // Empty string is a valid flag (0).
- return util::make_unique<BinaryPrimitive>(flags);
+ StringPiece trimmed_str(util::TrimWhitespace(str));
+ for (const Attribute::Symbol& symbol : enum_attr->symbols) {
+ // Enum symbols are stored as @package:id/symbol resources,
+ // so we need to match against the 'entry' part of the identifier.
+ const ResourceName& enum_symbol_resource_name = symbol.symbol.name.value();
+ if (trimmed_str == enum_symbol_resource_name.entry) {
+ android::Res_value value = {};
+ value.dataType = android::Res_value::TYPE_INT_DEC;
+ value.data = symbol.value;
+ return util::make_unique<BinaryPrimitive>(value);
}
+ }
+ return {};
+}
- for (StringPiece part : util::tokenize(str, '|')) {
- StringPiece trimmedPart = util::trimWhitespace(part);
+std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* flag_attr,
+ const StringPiece& str) {
+ android::Res_value flags = {};
+ flags.dataType = android::Res_value::TYPE_INT_HEX;
+ flags.data = 0u;
- bool flagSet = false;
- for (const Attribute::Symbol& symbol : flagAttr->symbols) {
- // Flag symbols are stored as @package:id/symbol resources,
- // so we need to match against the 'entry' part of the identifier.
- const ResourceName& flagSymbolResourceName = symbol.symbol.name.value();
- if (trimmedPart == flagSymbolResourceName.entry) {
- flags.data |= symbol.value;
- flagSet = true;
- break;
- }
- }
-
- if (!flagSet) {
- return {};
- }
- }
+ if (util::TrimWhitespace(str).empty()) {
+ // Empty string is a valid flag (0).
return util::make_unique<BinaryPrimitive>(flags);
+ }
+
+ for (StringPiece part : util::Tokenize(str, '|')) {
+ StringPiece trimmed_part = util::TrimWhitespace(part);
+
+ bool flag_set = false;
+ for (const Attribute::Symbol& symbol : flag_attr->symbols) {
+ // Flag symbols are stored as @package:id/symbol resources,
+ // so we need to match against the 'entry' part of the identifier.
+ const ResourceName& flag_symbol_resource_name =
+ symbol.symbol.name.value();
+ if (trimmed_part == flag_symbol_resource_name.entry) {
+ flags.data |= symbol.value;
+ flag_set = true;
+ break;
+ }
+ }
+
+ if (!flag_set) {
+ return {};
+ }
+ }
+ return util::make_unique<BinaryPrimitive>(flags);
}
-static uint32_t parseHex(char c, bool* outError) {
- if (c >= '0' && c <= '9') {
- return c - '0';
- } else if (c >= 'a' && c <= 'f') {
- return c - 'a' + 0xa;
- } else if (c >= 'A' && c <= 'F') {
- return c - 'A' + 0xa;
+static uint32_t ParseHex(char c, bool* out_error) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 0xa;
+ } else if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 0xa;
+ } else {
+ *out_error = true;
+ return 0xffffffffu;
+ }
+}
+
+std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str) {
+ StringPiece color_str(util::TrimWhitespace(str));
+ const char* start = color_str.data();
+ const size_t len = color_str.size();
+ if (len == 0 || start[0] != '#') {
+ return {};
+ }
+
+ android::Res_value value = {};
+ bool error = false;
+ if (len == 4) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
+ value.data = 0xff000000u;
+ value.data |= ParseHex(start[1], &error) << 20;
+ value.data |= ParseHex(start[1], &error) << 16;
+ value.data |= ParseHex(start[2], &error) << 12;
+ value.data |= ParseHex(start[2], &error) << 8;
+ value.data |= ParseHex(start[3], &error) << 4;
+ value.data |= ParseHex(start[3], &error);
+ } else if (len == 5) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
+ value.data |= ParseHex(start[1], &error) << 28;
+ value.data |= ParseHex(start[1], &error) << 24;
+ value.data |= ParseHex(start[2], &error) << 20;
+ value.data |= ParseHex(start[2], &error) << 16;
+ value.data |= ParseHex(start[3], &error) << 12;
+ value.data |= ParseHex(start[3], &error) << 8;
+ value.data |= ParseHex(start[4], &error) << 4;
+ value.data |= ParseHex(start[4], &error);
+ } else if (len == 7) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
+ value.data = 0xff000000u;
+ value.data |= ParseHex(start[1], &error) << 20;
+ value.data |= ParseHex(start[2], &error) << 16;
+ value.data |= ParseHex(start[3], &error) << 12;
+ value.data |= ParseHex(start[4], &error) << 8;
+ value.data |= ParseHex(start[5], &error) << 4;
+ value.data |= ParseHex(start[6], &error);
+ } else if (len == 9) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
+ value.data |= ParseHex(start[1], &error) << 28;
+ value.data |= ParseHex(start[2], &error) << 24;
+ value.data |= ParseHex(start[3], &error) << 20;
+ value.data |= ParseHex(start[4], &error) << 16;
+ value.data |= ParseHex(start[5], &error) << 12;
+ value.data |= ParseHex(start[6], &error) << 8;
+ value.data |= ParseHex(start[7], &error) << 4;
+ value.data |= ParseHex(start[8], &error);
+ } else {
+ return {};
+ }
+ return error ? std::unique_ptr<BinaryPrimitive>()
+ : util::make_unique<BinaryPrimitive>(value);
+}
+
+Maybe<bool> ParseBool(const StringPiece& str) {
+ StringPiece trimmed_str(util::TrimWhitespace(str));
+ if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") {
+ return Maybe<bool>(true);
+ } else if (trimmed_str == "false" || trimmed_str == "FALSE" ||
+ trimmed_str == "False") {
+ return Maybe<bool>(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> ParseResourceId(const StringPiece& str) {
+ StringPiece trimmed_str(util::TrimWhitespace(str));
+
+ std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
+ android::Res_value value;
+ if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
+ if (value.dataType == android::Res_value::TYPE_INT_HEX) {
+ ResourceId id(value.data);
+ if (id.is_valid()) {
+ return id;
+ }
+ }
+ }
+ return {};
+}
+
+Maybe<int> ParseSdkVersion(const StringPiece& str) {
+ StringPiece trimmed_str(util::TrimWhitespace(str));
+
+ std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
+ android::Res_value value;
+ if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
+ return static_cast<int>(value.data);
+ }
+
+ // Try parsing the code name.
+ std::pair<StringPiece, int> entry = GetDevelopmentSdkCodeNameAndVersion();
+ if (entry.first == trimmed_str) {
+ return entry.second;
+ }
+ return {};
+}
+
+std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) {
+ if (Maybe<bool> maybe_result = ParseBool(str)) {
+ android::Res_value value = {};
+ value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
+
+ if (maybe_result.value()) {
+ value.data = 0xffffffffu;
} else {
- *outError = true;
- return 0xffffffffu;
- }
-}
-
-std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str) {
- StringPiece colorStr(util::trimWhitespace(str));
- const char* start = colorStr.data();
- const size_t len = colorStr.size();
- if (len == 0 || start[0] != '#') {
- return {};
- }
-
- android::Res_value value = { };
- bool error = false;
- if (len == 4) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
- value.data = 0xff000000u;
- value.data |= parseHex(start[1], &error) << 20;
- value.data |= parseHex(start[1], &error) << 16;
- value.data |= parseHex(start[2], &error) << 12;
- value.data |= parseHex(start[2], &error) << 8;
- value.data |= parseHex(start[3], &error) << 4;
- value.data |= parseHex(start[3], &error);
- } else if (len == 5) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
- value.data |= parseHex(start[1], &error) << 28;
- value.data |= parseHex(start[1], &error) << 24;
- value.data |= parseHex(start[2], &error) << 20;
- value.data |= parseHex(start[2], &error) << 16;
- value.data |= parseHex(start[3], &error) << 12;
- value.data |= parseHex(start[3], &error) << 8;
- value.data |= parseHex(start[4], &error) << 4;
- value.data |= parseHex(start[4], &error);
- } else if (len == 7) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
- value.data = 0xff000000u;
- value.data |= parseHex(start[1], &error) << 20;
- value.data |= parseHex(start[2], &error) << 16;
- value.data |= parseHex(start[3], &error) << 12;
- value.data |= parseHex(start[4], &error) << 8;
- value.data |= parseHex(start[5], &error) << 4;
- value.data |= parseHex(start[6], &error);
- } else if (len == 9) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
- value.data |= parseHex(start[1], &error) << 28;
- value.data |= parseHex(start[2], &error) << 24;
- value.data |= parseHex(start[3], &error) << 20;
- value.data |= parseHex(start[4], &error) << 16;
- value.data |= parseHex(start[5], &error) << 12;
- value.data |= parseHex(start[6], &error) << 8;
- value.data |= parseHex(start[7], &error) << 4;
- value.data |= parseHex(start[8], &error);
- } else {
- return {};
- }
- return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value);
-}
-
-Maybe<bool> parseBool(const StringPiece& str) {
- StringPiece trimmedStr(util::trimWhitespace(str));
- if (trimmedStr == "true" || trimmedStr == "TRUE" || trimmedStr == "True") {
- return Maybe<bool>(true);
- } else if (trimmedStr == "false" || trimmedStr == "FALSE" || trimmedStr == "False") {
- return Maybe<bool>(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> parseResourceId(const StringPiece& str) {
- StringPiece trimmedStr(util::trimWhitespace(str));
-
- std::u16string str16 = util::utf8ToUtf16(trimmedStr);
- android::Res_value value;
- if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
- if (value.dataType == android::Res_value::TYPE_INT_HEX) {
- ResourceId id(value.data);
- if (id.isValid()) {
- return id;
- }
- }
- }
- return {};
-}
-
-Maybe<int> parseSdkVersion(const StringPiece& str) {
- StringPiece trimmedStr(util::trimWhitespace(str));
-
- std::u16string str16 = util::utf8ToUtf16(trimmedStr);
- android::Res_value value;
- if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
- return static_cast<int>(value.data);
- }
-
- // Try parsing the code name.
- std::pair<StringPiece, int> entry = getDevelopmentSdkCodeNameAndVersion();
- if (entry.first == trimmedStr) {
- return entry.second;
- }
- return {};
-}
-
-std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str) {
- if (Maybe<bool> maybeResult = parseBool(str)) {
- android::Res_value value = {};
- value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
-
- if (maybeResult.value()) {
- value.data = 0xffffffffu;
- } else {
- value.data = 0;
- }
- return util::make_unique<BinaryPrimitive>(value);
- }
- return {};
-}
-
-std::unique_ptr<BinaryPrimitive> tryParseInt(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 = 0;
}
return util::make_unique<BinaryPrimitive>(value);
+ }
+ return {};
}
-std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str) {
- std::u16string str16 = util::utf8ToUtf16(str);
- android::Res_value value;
- if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
- return {};
- }
- return util::make_unique<BinaryPrimitive>(value);
+std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) {
+ std::u16string str16 = util::Utf8ToUtf16(str);
+ android::Res_value value;
+ if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
+ return {};
+ }
+ return util::make_unique<BinaryPrimitive>(value);
}
-uint32_t androidTypeToAttributeTypeMask(uint16_t type) {
- switch (type) {
+std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) {
+ std::u16string str16 = util::Utf8ToUtf16(str);
+ android::Res_value value;
+ if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
+ return {};
+ }
+ return util::make_unique<BinaryPrimitive>(value);
+}
+
+uint32_t AndroidTypeToAttributeTypeMask(uint16_t type) {
+ switch (type) {
case android::Res_value::TYPE_NULL:
case android::Res_value::TYPE_REFERENCE:
case android::Res_value::TYPE_ATTRIBUTE:
case android::Res_value::TYPE_DYNAMIC_REFERENCE:
- return android::ResTable_map::TYPE_REFERENCE;
+ return android::ResTable_map::TYPE_REFERENCE;
case android::Res_value::TYPE_STRING:
- return android::ResTable_map::TYPE_STRING;
+ return android::ResTable_map::TYPE_STRING;
case android::Res_value::TYPE_FLOAT:
- return android::ResTable_map::TYPE_FLOAT;
+ return android::ResTable_map::TYPE_FLOAT;
case android::Res_value::TYPE_DIMENSION:
- return android::ResTable_map::TYPE_DIMENSION;
+ return android::ResTable_map::TYPE_DIMENSION;
case android::Res_value::TYPE_FRACTION:
- return android::ResTable_map::TYPE_FRACTION;
+ return android::ResTable_map::TYPE_FRACTION;
case android::Res_value::TYPE_INT_DEC:
case android::Res_value::TYPE_INT_HEX:
- return android::ResTable_map::TYPE_INTEGER | android::ResTable_map::TYPE_ENUM
- | android::ResTable_map::TYPE_FLAGS;
+ return android::ResTable_map::TYPE_INTEGER |
+ android::ResTable_map::TYPE_ENUM |
+ android::ResTable_map::TYPE_FLAGS;
case android::Res_value::TYPE_INT_BOOLEAN:
- return android::ResTable_map::TYPE_BOOLEAN;
+ return android::ResTable_map::TYPE_BOOLEAN;
case android::Res_value::TYPE_INT_COLOR_ARGB8:
case android::Res_value::TYPE_INT_COLOR_RGB8:
case android::Res_value::TYPE_INT_COLOR_ARGB4:
case android::Res_value::TYPE_INT_COLOR_RGB4:
- return android::ResTable_map::TYPE_COLOR;
+ return android::ResTable_map::TYPE_COLOR;
default:
- return 0;
- };
+ return 0;
+ };
}
-std::unique_ptr<Item> tryParseItemForAttribute(
- const StringPiece& value,
- uint32_t typeMask,
- const std::function<void(const ResourceName&)>& onCreateReference) {
- std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
- if (nullOrEmpty) {
- return std::move(nullOrEmpty);
- }
+std::unique_ptr<Item> TryParseItemForAttribute(
+ const StringPiece& value, uint32_t type_mask,
+ const std::function<void(const ResourceName&)>& on_create_reference) {
+ std::unique_ptr<BinaryPrimitive> null_or_empty = TryParseNullOrEmpty(value);
+ if (null_or_empty) {
+ return std::move(null_or_empty);
+ }
- bool create = false;
- std::unique_ptr<Reference> reference = tryParseReference(value, &create);
- if (reference) {
- if (create && onCreateReference) {
- onCreateReference(reference->name.value());
- }
- return std::move(reference);
+ bool create = false;
+ std::unique_ptr<Reference> reference = TryParseReference(value, &create);
+ if (reference) {
+ if (create && on_create_reference) {
+ on_create_reference(reference->name.value());
}
+ return std::move(reference);
+ }
- if (typeMask & android::ResTable_map::TYPE_COLOR) {
- // Try parsing this as a color.
- std::unique_ptr<BinaryPrimitive> color = tryParseColor(value);
- if (color) {
- return std::move(color);
- }
+ if (type_mask & android::ResTable_map::TYPE_COLOR) {
+ // Try parsing this as a color.
+ std::unique_ptr<BinaryPrimitive> color = TryParseColor(value);
+ if (color) {
+ return std::move(color);
}
+ }
- if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
- // Try parsing this as a boolean.
- std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value);
- if (boolean) {
- return std::move(boolean);
- }
+ if (type_mask & android::ResTable_map::TYPE_BOOLEAN) {
+ // Try parsing this as a boolean.
+ std::unique_ptr<BinaryPrimitive> boolean = TryParseBool(value);
+ if (boolean) {
+ return std::move(boolean);
}
+ }
- if (typeMask & android::ResTable_map::TYPE_INTEGER) {
- // Try parsing this as an integer.
- std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value);
- if (integer) {
- return std::move(integer);
- }
+ if (type_mask & android::ResTable_map::TYPE_INTEGER) {
+ // Try parsing this as an integer.
+ std::unique_ptr<BinaryPrimitive> integer = TryParseInt(value);
+ if (integer) {
+ return std::move(integer);
}
+ }
- const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT
- | android::ResTable_map::TYPE_DIMENSION | android::ResTable_map::TYPE_FRACTION;
- if (typeMask & floatMask) {
- // Try parsing this as a float.
- std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value);
- if (floatingPoint) {
- if (typeMask & androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) {
- return std::move(floatingPoint);
- }
- }
+ const uint32_t float_mask = android::ResTable_map::TYPE_FLOAT |
+ android::ResTable_map::TYPE_DIMENSION |
+ android::ResTable_map::TYPE_FRACTION;
+ if (type_mask & float_mask) {
+ // Try parsing this as a float.
+ std::unique_ptr<BinaryPrimitive> floating_point = TryParseFloat(value);
+ if (floating_point) {
+ if (type_mask &
+ AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
+ return std::move(floating_point);
+ }
}
- return {};
+ }
+ return {};
}
/**
* We successively try to parse the string as a resource type that the Attribute
* allows.
*/
-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 = tryParseItemForAttribute(str, typeMask, onCreateReference);
- if (value) {
- return value;
- }
+std::unique_ptr<Item> TryParseItemForAttribute(
+ const StringPiece& str, const Attribute* attr,
+ const std::function<void(const ResourceName&)>& on_create_reference) {
+ const uint32_t type_mask = attr->type_mask;
+ std::unique_ptr<Item> value =
+ TryParseItemForAttribute(str, type_mask, on_create_reference);
+ if (value) {
+ return value;
+ }
- if (typeMask & android::ResTable_map::TYPE_ENUM) {
- // Try parsing this as an enum.
- std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str);
- if (enumValue) {
- return std::move(enumValue);
- }
+ if (type_mask & android::ResTable_map::TYPE_ENUM) {
+ // Try parsing this as an enum.
+ std::unique_ptr<BinaryPrimitive> enum_value = TryParseEnumSymbol(attr, str);
+ if (enum_value) {
+ return std::move(enum_value);
}
+ }
- if (typeMask & android::ResTable_map::TYPE_FLAGS) {
- // Try parsing this as a flag.
- std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str);
- if (flagValue) {
- return std::move(flagValue);
- }
+ if (type_mask & android::ResTable_map::TYPE_FLAGS) {
+ // Try parsing this as a flag.
+ std::unique_ptr<BinaryPrimitive> flag_value = TryParseFlagSymbol(attr, str);
+ if (flag_value) {
+ return std::move(flag_value);
}
- return {};
+ }
+ return {};
}
-std::string buildResourceFileName(const ResourceFile& resFile, const NameMangler* mangler) {
- std::stringstream out;
- out << "res/" << resFile.name.type;
- if (resFile.config != ConfigDescription{}) {
- out << "-" << resFile.config;
- }
- out << "/";
+std::string BuildResourceFileName(const ResourceFile& res_file,
+ const NameMangler* mangler) {
+ std::stringstream out;
+ out << "res/" << res_file.name.type;
+ if (res_file.config != ConfigDescription{}) {
+ out << "-" << res_file.config;
+ }
+ out << "/";
- if (mangler && mangler->shouldMangle(resFile.name.package)) {
- out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
- } else {
- out << resFile.name.entry;
- }
- out << file::getExtension(resFile.source.path);
- return out.str();
+ if (mangler && mangler->ShouldMangle(res_file.name.package)) {
+ out << NameMangler::MangleEntry(res_file.name.package, res_file.name.entry);
+ } else {
+ out << res_file.name.entry;
+ }
+ out << file::GetExtension(res_file.source.path);
+ return out.str();
}
-} // namespace ResourceUtils
-} // namespace aapt
+} // namespace ResourceUtils
+} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 555203b..9766f6a 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -17,14 +17,14 @@
#ifndef AAPT_RESOURCEUTILS_H
#define AAPT_RESOURCEUTILS_H
+#include <functional>
+#include <memory>
+
#include "NameMangler.h"
#include "Resource.h"
#include "ResourceValues.h"
#include "util/StringPiece.h"
-#include <functional>
-#include <memory>
-
namespace aapt {
namespace ResourceUtils {
@@ -37,164 +37,183 @@
* individual extracted piece to verify that the pieces are valid.
* Returns false if there was no package but a ':' was present.
*/
-bool extractResourceName(const StringPiece& str, StringPiece* outPackage,
- StringPiece* outType, StringPiece* outEntry);
+bool ExtractResourceName(const StringPiece& str, StringPiece* out_package,
+ StringPiece* out_type, StringPiece* out_entry);
/**
- * Returns true if the string was parsed as a resource name ([*][package:]type/name), with
- * `outResource` set to the parsed resource name and `outPrivate` set to true if a '*' prefix
- * was present.
+ * Returns true if the string was parsed as a resource name
+ * ([*][package:]type/name), with
+ * `out_resource` set to the parsed resource name and `out_private` set to true
+ * if a '*' prefix was present.
*/
-bool parseResourceName(const StringPiece& str, ResourceNameRef* outResource,
- bool* outPrivate = nullptr);
+bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_resource,
+ bool* out_private = nullptr);
/*
- * Returns true if the string was parsed as a reference (@[+][package:]type/name), with
- * `outReference` set to the parsed reference.
+ * Returns true if the string was parsed as a reference
+ * (@[+][package:]type/name), with
+ * `out_reference` set to the parsed reference.
*
- * If '+' was present in the reference, `outCreate` is set to true.
- * If '*' was present in the reference, `outPrivate` is set to true.
+ * If '+' was present in the reference, `out_create` is set to true.
+ * If '*' was present in the reference, `out_private` is set to true.
*/
-bool parseReference(const StringPiece& str, ResourceNameRef* outReference,
- bool* outCreate = nullptr, bool* outPrivate = nullptr);
+bool ParseReference(const StringPiece& str, ResourceNameRef* out_reference,
+ bool* out_create = nullptr, bool* out_private = nullptr);
/*
- * Returns true if the string is in the form of a resource reference (@[+][package:]type/name).
+ * Returns true if the string is in the form of a resource reference
+ * (@[+][package:]type/name).
*/
-bool isReference(const StringPiece& str);
+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.
+ * Returns true if the string was parsed as an attribute reference
+ * (?[package:][type/]name),
+ * with `out_reference` set to the parsed reference.
*/
-bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outReference);
+bool ParseAttributeReference(const StringPiece& str,
+ ResourceNameRef* out_reference);
/**
- * Returns true if the string is in the form of an attribute reference(?[package:][type/]name).
+ * Returns true if the string is in the form of an attribute
+ * reference(?[package:][type/]name).
*/
-bool isAttributeReference(const StringPiece& str);
+bool IsAttributeReference(const StringPiece& str);
/**
* Convert an android::ResTable::resource_name to an aapt::ResourceName struct.
*/
-Maybe<ResourceName> toResourceName(const android::ResTable::resource_name& name);
+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.
+ * Returns a boolean value if the string is equal to TRUE, true, True, FALSE,
+ * false, or False.
*/
-Maybe<bool> parseBool(const StringPiece& str);
+Maybe<bool> ParseBool(const StringPiece& str);
/**
* Returns a uint32_t if the string is an integer.
*/
-Maybe<uint32_t> parseInt(const StringPiece& str);
+Maybe<uint32_t> ParseInt(const StringPiece& str);
/**
* Returns an ID if it the string represented a valid ID.
*/
-Maybe<ResourceId> parseResourceId(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> parseSdkVersion(const StringPiece& str);
+Maybe<int> ParseSdkVersion(const StringPiece& str);
/*
- * Returns a Reference, or None Maybe instance if the string `str` was parsed as a
+ * Returns a Reference, or None Maybe instance if the string `str` was parsed as
+ * a
* valid reference to a style.
- * The format for a style parent is slightly more flexible than a normal reference:
+ * The format for a style parent is slightly more flexible than a normal
+ * reference:
*
* @[package:]style/<entry> or
* ?[package:]style/<entry> or
* <package>:[style/]<entry>
*/
-Maybe<Reference> parseStyleParentReference(const StringPiece& str, std::string* outError);
+Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
+ std::string* out_error);
/*
- * Returns a Reference if the string `str` was parsed as a valid XML attribute name.
+ * Returns a Reference if the string `str` was parsed as a valid XML attribute
+ * name.
* The valid format for an XML attribute name is:
*
* package:entry
*/
-Maybe<Reference> parseXmlAttributeName(const StringPiece& str);
+Maybe<Reference> ParseXmlAttributeName(const StringPiece& str);
/*
- * Returns a Reference object if the string was parsed as a resource or attribute reference,
- * ( @[+][package:]type/name | ?[package:]type/name ) setting outCreate to true if
+ * Returns a Reference object if the string was parsed as a resource or
+ * attribute reference,
+ * ( @[+][package:]type/name | ?[package:]type/name ) setting outCreate to true
+ * if
* the '+' was present in the string.
*/
-std::unique_ptr<Reference> tryParseReference(const StringPiece& str, bool* outCreate = nullptr);
+std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
+ bool* out_create = nullptr);
/*
- * Returns a BinaryPrimitve object representing @null or @empty if the string was parsed
- * as one.
+ * Returns a BinaryPrimitve object representing @null or @empty if the string
+ * was parsed as one.
*/
-std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str);
+std::unique_ptr<BinaryPrimitive> TryParseNullOrEmpty(const StringPiece& str);
/*
* Returns a BinaryPrimitve object representing a color if the string was parsed
* as one.
*/
-std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str);
+std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing a boolean if the string was parsed
- * as one.
+ * Returns a BinaryPrimitve object representing a boolean if the string was
+ * parsed as one.
*/
-std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str);
+std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing an integer if the string was parsed
- * as one.
+ * Returns a BinaryPrimitve object representing an integer if the string was
+ * parsed as one.
*/
-std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece& str);
+std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str);
/*
* Returns a BinaryPrimitve object representing a floating point number
* (float, dimension, etc) if the string was parsed as one.
*/
-std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str);
+std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing an enum symbol if the string was parsed
- * as one.
+ * Returns a BinaryPrimitve object representing an enum symbol if the string was
+ * parsed as one.
*/
-std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
+std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing a flag symbol if the string was parsed
- * as one.
+ * Returns a BinaryPrimitve object representing a flag symbol if the string was
+ * parsed as one.
*/
-std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* enumAttr,
+std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* enum_attr,
const StringPiece& str);
/*
- * Try to convert a string to an Item for the given attribute. The attribute will
+ * Try to convert a string to an Item for the given attribute. The attribute
+ * will
* restrict what values the string can be converted to.
- * The callback function onCreateReference is called when the parsed item is a
+ * The callback function on_create_reference is called when the parsed item is a
* reference to an ID that must be created (@+id/foo).
*/
-std::unique_ptr<Item> tryParseItemForAttribute(
- const StringPiece& value, const Attribute* attr,
- const std::function<void(const ResourceName&)>& onCreateReference = {});
+std::unique_ptr<Item> TryParseItemForAttribute(
+ const StringPiece& value, const Attribute* attr,
+ const std::function<void(const ResourceName&)>& on_create_reference = {});
-std::unique_ptr<Item> tryParseItemForAttribute(
- const StringPiece& value, uint32_t typeMask,
- const std::function<void(const ResourceName&)>& onCreateReference = {});
+std::unique_ptr<Item> TryParseItemForAttribute(
+ const StringPiece& value, uint32_t type_mask,
+ const std::function<void(const ResourceName&)>& on_create_reference = {});
-uint32_t androidTypeToAttributeTypeMask(uint16_t type);
+uint32_t AndroidTypeToAttributeTypeMask(uint16_t type);
/**
- * Returns a string path suitable for use within an APK. The path will look like:
+ * Returns a string path suitable for use within an APK. The path will look
+ * like:
*
* res/type[-config]/<name>.<ext>
*
- * Then name may be mangled if a NameMangler is supplied (can be nullptr) and the package
+ * Then name may be mangled if a NameMangler is supplied (can be nullptr) and
+ * the package
* requires mangling.
*/
-std::string buildResourceFileName(const ResourceFile& resFile, const NameMangler* mangler);
+std::string BuildResourceFileName(const ResourceFile& res_file,
+ const NameMangler* mangler = nullptr);
-} // namespace ResourceUtils
-} // namespace aapt
+} // namespace ResourceUtils
+} // namespace aapt
#endif /* AAPT_RESOURCEUTILS_H */
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 894cfcf..f9c500b 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -14,179 +14,191 @@
* limitations under the License.
*/
-#include "Resource.h"
#include "ResourceUtils.h"
+
+#include "Resource.h"
#include "test/Test.h"
namespace aapt {
TEST(ResourceUtilsTest, ParseBool) {
- 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"));
+ 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) {
- ResourceNameRef actual;
- bool actualPriv = false;
- EXPECT_TRUE(ResourceUtils::parseResourceName("android:color/foo", &actual, &actualPriv));
- EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
- EXPECT_FALSE(actualPriv);
+ ResourceNameRef actual;
+ bool actual_priv = false;
+ EXPECT_TRUE(ResourceUtils::ParseResourceName("android:color/foo", &actual,
+ &actual_priv));
+ EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
+ EXPECT_FALSE(actual_priv);
- EXPECT_TRUE(ResourceUtils::parseResourceName("color/foo", &actual, &actualPriv));
- EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, "foo"), actual);
- EXPECT_FALSE(actualPriv);
+ EXPECT_TRUE(
+ ResourceUtils::ParseResourceName("color/foo", &actual, &actual_priv));
+ EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, "foo"), actual);
+ EXPECT_FALSE(actual_priv);
- EXPECT_TRUE(ResourceUtils::parseResourceName("*android:color/foo", &actual, &actualPriv));
- EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
- EXPECT_TRUE(actualPriv);
+ EXPECT_TRUE(ResourceUtils::ParseResourceName("*android:color/foo", &actual,
+ &actual_priv));
+ EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
+ EXPECT_TRUE(actual_priv);
- EXPECT_FALSE(ResourceUtils::parseResourceName(StringPiece(), &actual, &actualPriv));
+ EXPECT_FALSE(
+ ResourceUtils::ParseResourceName(StringPiece(), &actual, &actual_priv));
}
TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) {
- ResourceNameRef expected({}, ResourceType::kColor, "foo");
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceUtils::parseReference("@color/foo", &actual, &create, &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_FALSE(privateRef);
+ ResourceNameRef expected({}, ResourceType::kColor, "foo");
+ ResourceNameRef actual;
+ bool create = false;
+ bool private_ref = false;
+ EXPECT_TRUE(ResourceUtils::ParseReference("@color/foo", &actual, &create,
+ &private_ref));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParseReferenceWithPackage) {
- ResourceNameRef expected("android", ResourceType::kColor, "foo");
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceUtils::parseReference("@android:color/foo", &actual, &create,
- &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_FALSE(privateRef);
+ ResourceNameRef expected("android", ResourceType::kColor, "foo");
+ ResourceNameRef actual;
+ bool create = false;
+ bool private_ref = false;
+ EXPECT_TRUE(ResourceUtils::ParseReference("@android:color/foo", &actual,
+ &create, &private_ref));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) {
- ResourceNameRef expected("android", ResourceType::kColor, "foo");
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceUtils::parseReference("\t @android:color/foo\n \n\t", &actual,
- &create, &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_FALSE(privateRef);
+ ResourceNameRef expected("android", ResourceType::kColor, "foo");
+ ResourceNameRef actual;
+ bool create = false;
+ bool private_ref = false;
+ EXPECT_TRUE(ResourceUtils::ParseReference("\t @android:color/foo\n \n\t",
+ &actual, &create, &private_ref));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParseAutoCreateIdReference) {
- ResourceNameRef expected("android", ResourceType::kId, "foo");
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceUtils::parseReference("@+android:id/foo", &actual, &create,
- &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_TRUE(create);
- EXPECT_FALSE(privateRef);
+ ResourceNameRef expected("android", ResourceType::kId, "foo");
+ ResourceNameRef actual;
+ bool create = false;
+ bool private_ref = false;
+ EXPECT_TRUE(ResourceUtils::ParseReference("@+android:id/foo", &actual,
+ &create, &private_ref));
+ EXPECT_EQ(expected, actual);
+ EXPECT_TRUE(create);
+ EXPECT_FALSE(private_ref);
}
TEST(ResourceUtilsTest, ParsePrivateReference) {
- ResourceNameRef expected("android", ResourceType::kId, "foo");
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceUtils::parseReference("@*android:id/foo", &actual, &create,
- &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_TRUE(privateRef);
+ ResourceNameRef expected("android", ResourceType::kId, "foo");
+ ResourceNameRef actual;
+ bool create = false;
+ bool private_ref = false;
+ EXPECT_TRUE(ResourceUtils::ParseReference("@*android:id/foo", &actual,
+ &create, &private_ref));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_TRUE(private_ref);
}
TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) {
- bool create = false;
- bool privateRef = false;
- ResourceNameRef actual;
- EXPECT_FALSE(ResourceUtils::parseReference("@+android:color/foo", &actual, &create,
- &privateRef));
+ bool create = false;
+ bool private_ref = false;
+ ResourceNameRef actual;
+ EXPECT_FALSE(ResourceUtils::ParseReference("@+android:color/foo", &actual,
+ &create, &private_ref));
}
TEST(ResourceUtilsTest, ParseAttributeReferences) {
- EXPECT_TRUE(ResourceUtils::isAttributeReference("?android"));
- EXPECT_TRUE(ResourceUtils::isAttributeReference("?android:foo"));
- EXPECT_TRUE(ResourceUtils::isAttributeReference("?attr/foo"));
- EXPECT_TRUE(ResourceUtils::isAttributeReference("?android:attr/foo"));
+ EXPECT_TRUE(ResourceUtils::IsAttributeReference("?android"));
+ EXPECT_TRUE(ResourceUtils::IsAttributeReference("?android:foo"));
+ EXPECT_TRUE(ResourceUtils::IsAttributeReference("?attr/foo"));
+ EXPECT_TRUE(ResourceUtils::IsAttributeReference("?android:attr/foo"));
}
TEST(ResourceUtilsTest, FailParseIncompleteReference) {
- EXPECT_FALSE(ResourceUtils::isAttributeReference("?style/foo"));
- EXPECT_FALSE(ResourceUtils::isAttributeReference("?android:style/foo"));
- EXPECT_FALSE(ResourceUtils::isAttributeReference("?android:"));
- EXPECT_FALSE(ResourceUtils::isAttributeReference("?android:attr/"));
- EXPECT_FALSE(ResourceUtils::isAttributeReference("?:attr/"));
- EXPECT_FALSE(ResourceUtils::isAttributeReference("?:attr/foo"));
- EXPECT_FALSE(ResourceUtils::isAttributeReference("?:/"));
- EXPECT_FALSE(ResourceUtils::isAttributeReference("?:/foo"));
- EXPECT_FALSE(ResourceUtils::isAttributeReference("?attr/"));
- EXPECT_FALSE(ResourceUtils::isAttributeReference("?/foo"));
+ EXPECT_FALSE(ResourceUtils::IsAttributeReference("?style/foo"));
+ EXPECT_FALSE(ResourceUtils::IsAttributeReference("?android:style/foo"));
+ EXPECT_FALSE(ResourceUtils::IsAttributeReference("?android:"));
+ EXPECT_FALSE(ResourceUtils::IsAttributeReference("?android:attr/"));
+ EXPECT_FALSE(ResourceUtils::IsAttributeReference("?:attr/"));
+ EXPECT_FALSE(ResourceUtils::IsAttributeReference("?:attr/foo"));
+ EXPECT_FALSE(ResourceUtils::IsAttributeReference("?:/"));
+ EXPECT_FALSE(ResourceUtils::IsAttributeReference("?:/foo"));
+ EXPECT_FALSE(ResourceUtils::IsAttributeReference("?attr/"));
+ EXPECT_FALSE(ResourceUtils::IsAttributeReference("?/foo"));
}
TEST(ResourceUtilsTest, ParseStyleParentReference) {
- const ResourceName kAndroidStyleFooName("android", ResourceType::kStyle, "foo");
- const ResourceName kStyleFooName({}, ResourceType::kStyle, "foo");
+ const ResourceName kAndroidStyleFooName("android", ResourceType::kStyle,
+ "foo");
+ const ResourceName kStyleFooName({}, ResourceType::kStyle, "foo");
- std::string errStr;
- Maybe<Reference> ref = ResourceUtils::parseStyleParentReference("@android:style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ std::string err_str;
+ Maybe<Reference> ref =
+ ResourceUtils::ParseStyleParentReference("@android:style/foo", &err_str);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("@style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+ ref = ResourceUtils::ParseStyleParentReference("@style/foo", &err_str);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("?android:style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ref =
+ ResourceUtils::ParseStyleParentReference("?android:style/foo", &err_str);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("?style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+ ref = ResourceUtils::ParseStyleParentReference("?style/foo", &err_str);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("android:style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ref = ResourceUtils::ParseStyleParentReference("android:style/foo", &err_str);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("android:foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ref = ResourceUtils::ParseStyleParentReference("android:foo", &err_str);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("@android:foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ref = ResourceUtils::ParseStyleParentReference("@android:foo", &err_str);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+ ref = ResourceUtils::ParseStyleParentReference("foo", &err_str);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("*android:style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- EXPECT_TRUE(ref.value().privateReference);
+ ref =
+ ResourceUtils::ParseStyleParentReference("*android:style/foo", &err_str);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ EXPECT_TRUE(ref.value().private_reference);
}
TEST(ResourceUtilsTest, ParseEmptyFlag) {
- std::unique_ptr<Attribute> attr = test::AttributeBuilder(false)
- .setTypeMask(android::ResTable_map::TYPE_FLAGS)
- .addItem("one", 0x01)
- .addItem("two", 0x02)
- .build();
+ std::unique_ptr<Attribute> attr =
+ test::AttributeBuilder(false)
+ .SetTypeMask(android::ResTable_map::TYPE_FLAGS)
+ .AddItem("one", 0x01)
+ .AddItem("two", 0x02)
+ .Build();
- std::unique_ptr<BinaryPrimitive> result = ResourceUtils::tryParseFlagSymbol(attr.get(), "");
- ASSERT_NE(nullptr, result);
- EXPECT_EQ(0u, result->value.data);
+ std::unique_ptr<BinaryPrimitive> result =
+ ResourceUtils::TryParseFlagSymbol(attr.get(), "");
+ ASSERT_NE(nullptr, result);
+ EXPECT_EQ(0u, result->value.data);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 492155d..7956ad8 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -14,757 +14,760 @@
* limitations under the License.
*/
-#include "Resource.h"
-#include "ResourceUtils.h"
#include "ResourceValues.h"
-#include "ValueVisitor.h"
-#include "util/Util.h"
#include <algorithm>
-#include <androidfw/ResourceTypes.h>
#include <limits>
#include <set>
+#include "androidfw/ResourceTypes.h"
+
+#include "Resource.h"
+#include "ResourceUtils.h"
+#include "ValueVisitor.h"
+#include "util/Util.h"
+
namespace aapt {
template <typename Derived>
-void BaseValue<Derived>::accept(RawValueVisitor* visitor) {
- visitor->visit(static_cast<Derived*>(this));
+void BaseValue<Derived>::Accept(RawValueVisitor* visitor) {
+ visitor->Visit(static_cast<Derived*>(this));
}
template <typename Derived>
-void BaseItem<Derived>::accept(RawValueVisitor* visitor) {
- visitor->visit(static_cast<Derived*>(this));
+void BaseItem<Derived>::Accept(RawValueVisitor* visitor) {
+ visitor->Visit(static_cast<Derived*>(this));
}
-RawString::RawString(const StringPool::Ref& ref) : value(ref) {
-}
+RawString::RawString(const StringPool::Ref& ref) : value(ref) {}
-bool RawString::equals(const Value* value) const {
- const RawString* other = valueCast<RawString>(value);
- if (!other) {
- return false;
- }
- return *this->value == *other->value;
-}
-
-RawString* RawString::clone(StringPool* newPool) const {
- RawString* rs = new RawString(newPool->makeRef(*value));
- rs->mComment = mComment;
- rs->mSource = mSource;
- return rs;
-}
-
-bool RawString::flatten(android::Res_value* outValue) const {
- outValue->dataType = android::Res_value::TYPE_STRING;
- outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
- return true;
-}
-
-void RawString::print(std::ostream* out) const {
- *out << "(raw string) " << *value;
-}
-
-Reference::Reference() : referenceType(Type::kResource) {
-}
-
-Reference::Reference(const ResourceNameRef& n, Type t) :
- name(n.toResourceName()), referenceType(t) {
-}
-
-Reference::Reference(const ResourceId& i, Type type) : id(i), referenceType(type) {
-}
-
-Reference::Reference(const ResourceNameRef& n, const ResourceId& i) :
- name(n.toResourceName()), id(i), referenceType(Type::kResource) {
-}
-
-bool Reference::equals(const Value* value) const {
- const Reference* other = valueCast<Reference>(value);
- if (!other) {
- return false;
- }
- return referenceType == other->referenceType && privateReference == other->privateReference &&
- id == other->id && name == other->name;
-}
-
-bool Reference::flatten(android::Res_value* outValue) const {
- outValue->dataType = (referenceType == Reference::Type::kResource) ?
- android::Res_value::TYPE_REFERENCE : android::Res_value::TYPE_ATTRIBUTE;
- outValue->data = util::hostToDevice32(id ? id.value().id : 0);
- return true;
-}
-
-Reference* Reference::clone(StringPool* /*newPool*/) const {
- return new Reference(*this);
-}
-
-void Reference::print(std::ostream* out) const {
- *out << "(reference) ";
- if (referenceType == Reference::Type::kResource) {
- *out << "@";
- if (privateReference) {
- *out << "*";
- }
- } else {
- *out << "?";
- }
-
- if (name) {
- *out << name.value();
- }
-
- if (id && !Res_INTERNALID(id.value().id)) {
- *out << " " << id.value();
- }
-}
-
-bool Id::equals(const Value* value) const {
- return valueCast<Id>(value) != nullptr;
-}
-
-bool Id::flatten(android::Res_value* out) const {
- out->dataType = android::Res_value::TYPE_INT_BOOLEAN;
- out->data = util::hostToDevice32(0);
- return true;
-}
-
-Id* Id::clone(StringPool* /*newPool*/) const {
- return new Id(*this);
-}
-
-void Id::print(std::ostream* out) const {
- *out << "(id)";
-}
-
-String::String(const StringPool::Ref& ref) : value(ref) {
-}
-
-bool String::equals(const Value* value) const {
- const String* other = valueCast<String>(value);
- if (!other) {
- return false;
- }
- return *this->value == *other->value;
-}
-
-bool String::flatten(android::Res_value* outValue) const {
- // Verify that our StringPool index is within encode-able limits.
- if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
- return false;
- }
-
- outValue->dataType = android::Res_value::TYPE_STRING;
- outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
- return true;
-}
-
-String* String::clone(StringPool* newPool) const {
- String* str = new String(newPool->makeRef(*value));
- str->mComment = mComment;
- str->mSource = mSource;
- return str;
-}
-
-void String::print(std::ostream* out) const {
- *out << "(string) \"" << *value << "\"";
-}
-
-StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {
-}
-
-bool StyledString::equals(const Value* value) const {
- const StyledString* other = valueCast<StyledString>(value);
- if (!other) {
- return false;
- }
-
- if (*this->value->str == *other->value->str) {
- const std::vector<StringPool::Span>& spansA = this->value->spans;
- const std::vector<StringPool::Span>& spansB = other->value->spans;
- return std::equal(spansA.begin(), spansA.end(), spansB.begin(),
- [](const StringPool::Span& a, const StringPool::Span& b) -> bool {
- return *a.name == *b.name && a.firstChar == b.firstChar && a.lastChar == b.lastChar;
- });
- }
+bool RawString::Equals(const Value* value) const {
+ const RawString* other = ValueCast<RawString>(value);
+ if (!other) {
return false;
+ }
+ return *this->value == *other->value;
}
-bool StyledString::flatten(android::Res_value* outValue) const {
- if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
- return false;
+RawString* RawString::Clone(StringPool* new_pool) const {
+ RawString* rs = new RawString(new_pool->MakeRef(*value));
+ rs->comment_ = comment_;
+ rs->source_ = source_;
+ return rs;
+}
+
+bool RawString::Flatten(android::Res_value* out_value) const {
+ out_value->dataType = android::Res_value::TYPE_STRING;
+ out_value->data = util::HostToDevice32(static_cast<uint32_t>(value.index()));
+ return true;
+}
+
+void RawString::Print(std::ostream* out) const {
+ *out << "(raw string) " << *value;
+}
+
+Reference::Reference() : reference_type(Type::kResource) {}
+
+Reference::Reference(const ResourceNameRef& n, Type t)
+ : name(n.ToResourceName()), reference_type(t) {}
+
+Reference::Reference(const ResourceId& i, Type type)
+ : id(i), reference_type(type) {}
+
+Reference::Reference(const ResourceNameRef& n, const ResourceId& i)
+ : name(n.ToResourceName()), id(i), reference_type(Type::kResource) {}
+
+bool Reference::Equals(const Value* value) const {
+ const Reference* other = ValueCast<Reference>(value);
+ if (!other) {
+ return false;
+ }
+ return reference_type == other->reference_type &&
+ private_reference == other->private_reference && id == other->id &&
+ name == other->name;
+}
+
+bool Reference::Flatten(android::Res_value* out_value) const {
+ out_value->dataType = (reference_type == Reference::Type::kResource)
+ ? android::Res_value::TYPE_REFERENCE
+ : android::Res_value::TYPE_ATTRIBUTE;
+ out_value->data = util::HostToDevice32(id ? id.value().id : 0);
+ return true;
+}
+
+Reference* Reference::Clone(StringPool* /*new_pool*/) const {
+ return new Reference(*this);
+}
+
+void Reference::Print(std::ostream* out) const {
+ *out << "(reference) ";
+ if (reference_type == Reference::Type::kResource) {
+ *out << "@";
+ if (private_reference) {
+ *out << "*";
}
+ } else {
+ *out << "?";
+ }
- outValue->dataType = android::Res_value::TYPE_STRING;
- outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
- return true;
+ if (name) {
+ *out << name.value();
+ }
+
+ if (id && !Res_INTERNALID(id.value().id)) {
+ *out << " " << id.value();
+ }
}
-StyledString* StyledString::clone(StringPool* newPool) const {
- StyledString* str = new StyledString(newPool->makeRef(value));
- str->mComment = mComment;
- str->mSource = mSource;
- return str;
+bool Id::Equals(const Value* value) const {
+ return ValueCast<Id>(value) != nullptr;
}
-void StyledString::print(std::ostream* out) const {
- *out << "(styled string) \"" << *value->str << "\"";
- for (const StringPool::Span& span : value->spans) {
- *out << " "<< *span.name << ":" << span.firstChar << "," << span.lastChar;
- }
+bool Id::Flatten(android::Res_value* out) const {
+ out->dataType = android::Res_value::TYPE_INT_BOOLEAN;
+ out->data = util::HostToDevice32(0);
+ return true;
}
-FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {
+Id* Id::Clone(StringPool* /*new_pool*/) const { return new Id(*this); }
+
+void Id::Print(std::ostream* out) const { *out << "(id)"; }
+
+String::String(const StringPool::Ref& ref) : value(ref) {}
+
+bool String::Equals(const Value* value) const {
+ const String* other = ValueCast<String>(value);
+ if (!other) {
+ return false;
+ }
+ return *this->value == *other->value;
}
-bool FileReference::equals(const Value* value) const {
- const FileReference* other = valueCast<FileReference>(value);
- if (!other) {
- return false;
- }
- return *path == *other->path;
+bool String::Flatten(android::Res_value* out_value) const {
+ // Verify that our StringPool index is within encode-able limits.
+ if (value.index() > std::numeric_limits<uint32_t>::max()) {
+ return false;
+ }
+
+ out_value->dataType = android::Res_value::TYPE_STRING;
+ out_value->data = util::HostToDevice32(static_cast<uint32_t>(value.index()));
+ return true;
}
-bool FileReference::flatten(android::Res_value* outValue) const {
- if (path.getIndex() > std::numeric_limits<uint32_t>::max()) {
- return false;
- }
-
- outValue->dataType = android::Res_value::TYPE_STRING;
- outValue->data = util::hostToDevice32(static_cast<uint32_t>(path.getIndex()));
- return true;
+String* String::Clone(StringPool* new_pool) const {
+ String* str = new String(new_pool->MakeRef(*value));
+ str->comment_ = comment_;
+ str->source_ = source_;
+ return str;
}
-FileReference* FileReference::clone(StringPool* newPool) const {
- FileReference* fr = new FileReference(newPool->makeRef(*path));
- fr->file = file;
- fr->mComment = mComment;
- fr->mSource = mSource;
- return fr;
+void String::Print(std::ostream* out) const {
+ *out << "(string) \"" << *value << "\"";
}
-void FileReference::print(std::ostream* out) const {
- *out << "(file) " << *path;
+StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {}
+
+bool StyledString::Equals(const Value* value) const {
+ const StyledString* other = ValueCast<StyledString>(value);
+ if (!other) {
+ return false;
+ }
+
+ if (*this->value->str == *other->value->str) {
+ const std::vector<StringPool::Span>& spans_a = this->value->spans;
+ const std::vector<StringPool::Span>& spans_b = other->value->spans;
+ return std::equal(
+ spans_a.begin(), spans_a.end(), spans_b.begin(),
+ [](const StringPool::Span& a, const StringPool::Span& b) -> bool {
+ return *a.name == *b.name && a.first_char == b.first_char &&
+ a.last_char == b.last_char;
+ });
+ }
+ return false;
}
-BinaryPrimitive::BinaryPrimitive(const android::Res_value& val) : value(val) {
+bool StyledString::Flatten(android::Res_value* out_value) const {
+ if (value.index() > std::numeric_limits<uint32_t>::max()) {
+ return false;
+ }
+
+ out_value->dataType = android::Res_value::TYPE_STRING;
+ out_value->data = util::HostToDevice32(static_cast<uint32_t>(value.index()));
+ return true;
}
+StyledString* StyledString::Clone(StringPool* new_pool) const {
+ StyledString* str = new StyledString(new_pool->MakeRef(value));
+ str->comment_ = comment_;
+ str->source_ = source_;
+ return str;
+}
+
+void StyledString::Print(std::ostream* out) const {
+ *out << "(styled string) \"" << *value->str << "\"";
+ for (const StringPool::Span& span : value->spans) {
+ *out << " " << *span.name << ":" << span.first_char << ","
+ << span.last_char;
+ }
+}
+
+FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {}
+
+bool FileReference::Equals(const Value* value) const {
+ const FileReference* other = ValueCast<FileReference>(value);
+ if (!other) {
+ return false;
+ }
+ return *path == *other->path;
+}
+
+bool FileReference::Flatten(android::Res_value* out_value) const {
+ if (path.index() > std::numeric_limits<uint32_t>::max()) {
+ return false;
+ }
+
+ out_value->dataType = android::Res_value::TYPE_STRING;
+ out_value->data = util::HostToDevice32(static_cast<uint32_t>(path.index()));
+ return true;
+}
+
+FileReference* FileReference::Clone(StringPool* new_pool) const {
+ FileReference* fr = new FileReference(new_pool->MakeRef(*path));
+ fr->file = file;
+ fr->comment_ = comment_;
+ fr->source_ = source_;
+ return fr;
+}
+
+void FileReference::Print(std::ostream* out) const {
+ *out << "(file) " << *path;
+}
+
+BinaryPrimitive::BinaryPrimitive(const android::Res_value& val) : value(val) {}
+
BinaryPrimitive::BinaryPrimitive(uint8_t dataType, uint32_t data) {
- value.dataType = dataType;
- value.data = data;
+ value.dataType = dataType;
+ value.data = data;
}
-bool BinaryPrimitive::equals(const Value* value) const {
- const BinaryPrimitive* other = valueCast<BinaryPrimitive>(value);
- if (!other) {
- return false;
- }
- return this->value.dataType == other->value.dataType && this->value.data == other->value.data;
+bool BinaryPrimitive::Equals(const Value* value) const {
+ const BinaryPrimitive* other = ValueCast<BinaryPrimitive>(value);
+ if (!other) {
+ return false;
+ }
+ return this->value.dataType == other->value.dataType &&
+ this->value.data == other->value.data;
}
-bool BinaryPrimitive::flatten(android::Res_value* outValue) const {
- outValue->dataType = value.dataType;
- outValue->data = util::hostToDevice32(value.data);
- return true;
+bool BinaryPrimitive::Flatten(android::Res_value* out_value) const {
+ out_value->dataType = value.dataType;
+ out_value->data = util::HostToDevice32(value.data);
+ return true;
}
-BinaryPrimitive* BinaryPrimitive::clone(StringPool* /*newPool*/) const {
- return new BinaryPrimitive(*this);
+BinaryPrimitive* BinaryPrimitive::Clone(StringPool* /*new_pool*/) const {
+ return new BinaryPrimitive(*this);
}
-void BinaryPrimitive::print(std::ostream* out) const {
- switch (value.dataType) {
- case android::Res_value::TYPE_NULL:
- *out << "(null)";
- break;
- case android::Res_value::TYPE_INT_DEC:
- *out << "(integer) " << static_cast<int32_t>(value.data);
- break;
- case android::Res_value::TYPE_INT_HEX:
- *out << "(integer) 0x" << std::hex << value.data << std::dec;
- break;
- case android::Res_value::TYPE_INT_BOOLEAN:
- *out << "(boolean) " << (value.data != 0 ? "true" : "false");
- break;
- case android::Res_value::TYPE_INT_COLOR_ARGB8:
- case android::Res_value::TYPE_INT_COLOR_RGB8:
- case android::Res_value::TYPE_INT_COLOR_ARGB4:
- case android::Res_value::TYPE_INT_COLOR_RGB4:
- *out << "(color) #" << std::hex << value.data << std::dec;
- break;
- default:
- *out << "(unknown 0x" << std::hex << (int) value.dataType << ") 0x"
- << std::hex << value.data << std::dec;
- break;
- }
+void BinaryPrimitive::Print(std::ostream* out) const {
+ switch (value.dataType) {
+ case android::Res_value::TYPE_NULL:
+ *out << "(null)";
+ break;
+ case android::Res_value::TYPE_INT_DEC:
+ *out << "(integer) " << static_cast<int32_t>(value.data);
+ break;
+ case android::Res_value::TYPE_INT_HEX:
+ *out << "(integer) 0x" << std::hex << value.data << std::dec;
+ break;
+ case android::Res_value::TYPE_INT_BOOLEAN:
+ *out << "(boolean) " << (value.data != 0 ? "true" : "false");
+ break;
+ case android::Res_value::TYPE_INT_COLOR_ARGB8:
+ case android::Res_value::TYPE_INT_COLOR_RGB8:
+ case android::Res_value::TYPE_INT_COLOR_ARGB4:
+ case android::Res_value::TYPE_INT_COLOR_RGB4:
+ *out << "(color) #" << std::hex << value.data << std::dec;
+ break;
+ default:
+ *out << "(unknown 0x" << std::hex << (int)value.dataType << ") 0x"
+ << std::hex << value.data << std::dec;
+ break;
+ }
}
-Attribute::Attribute(bool w, uint32_t t) :
- typeMask(t),
- minInt(std::numeric_limits<int32_t>::min()),
- maxInt(std::numeric_limits<int32_t>::max()) {
- mWeak = w;
+Attribute::Attribute(bool w, uint32_t t)
+ : type_mask(t),
+ min_int(std::numeric_limits<int32_t>::min()),
+ max_int(std::numeric_limits<int32_t>::max()) {
+ weak_ = w;
}
template <typename T>
T* addPointer(T& val) {
- return &val;
+ return &val;
}
-bool Attribute::equals(const Value* value) const {
- const Attribute* other = valueCast<Attribute>(value);
- if (!other) {
- return false;
- }
+bool Attribute::Equals(const Value* value) const {
+ const Attribute* other = ValueCast<Attribute>(value);
+ if (!other) {
+ return false;
+ }
- if (symbols.size() != other->symbols.size()) {
- return false;
- }
+ if (symbols.size() != other->symbols.size()) {
+ return false;
+ }
- if (typeMask != other->typeMask || minInt != other->minInt || maxInt != other->maxInt) {
- return false;
- }
+ if (type_mask != other->type_mask || min_int != other->min_int ||
+ max_int != other->max_int) {
+ return false;
+ }
- std::vector<const Symbol*> sortedA;
- std::transform(symbols.begin(), symbols.end(),
- std::back_inserter(sortedA), addPointer<const Symbol>);
- std::sort(sortedA.begin(), sortedA.end(), [](const Symbol* a, const Symbol* b) -> bool {
- return a->symbol.name < b->symbol.name;
- });
+ std::vector<const Symbol*> sorted_a;
+ std::transform(symbols.begin(), symbols.end(), std::back_inserter(sorted_a),
+ addPointer<const Symbol>);
+ std::sort(sorted_a.begin(), sorted_a.end(),
+ [](const Symbol* a, const Symbol* b) -> bool {
+ return a->symbol.name < b->symbol.name;
+ });
- std::vector<const Symbol*> sortedB;
- std::transform(other->symbols.begin(), other->symbols.end(),
- std::back_inserter(sortedB), addPointer<const Symbol>);
- std::sort(sortedB.begin(), sortedB.end(), [](const Symbol* a, const Symbol* b) -> bool {
- return a->symbol.name < b->symbol.name;
- });
+ std::vector<const Symbol*> sorted_b;
+ std::transform(other->symbols.begin(), other->symbols.end(),
+ std::back_inserter(sorted_b), addPointer<const Symbol>);
+ std::sort(sorted_b.begin(), sorted_b.end(),
+ [](const Symbol* a, const Symbol* b) -> bool {
+ return a->symbol.name < b->symbol.name;
+ });
- return std::equal(sortedA.begin(), sortedA.end(), sortedB.begin(),
- [](const Symbol* a, const Symbol* b) -> bool {
- return a->symbol.equals(&b->symbol) && a->value == b->value;
- });
+ return std::equal(sorted_a.begin(), sorted_a.end(), sorted_b.begin(),
+ [](const Symbol* a, const Symbol* b) -> bool {
+ return a->symbol.Equals(&b->symbol) &&
+ a->value == b->value;
+ });
}
-Attribute* Attribute::clone(StringPool* /*newPool*/) const {
- return new Attribute(*this);
+Attribute* Attribute::Clone(StringPool* /*new_pool*/) const {
+ return new Attribute(*this);
}
-void Attribute::printMask(std::ostream* out) const {
- if (typeMask == android::ResTable_map::TYPE_ANY) {
- *out << "any";
- return;
- }
+void Attribute::PrintMask(std::ostream* out) const {
+ if (type_mask == android::ResTable_map::TYPE_ANY) {
+ *out << "any";
+ return;
+ }
- bool set = false;
- if ((typeMask & android::ResTable_map::TYPE_REFERENCE) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "reference";
- }
-
- if ((typeMask & android::ResTable_map::TYPE_STRING) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "string";
- }
-
- if ((typeMask & android::ResTable_map::TYPE_INTEGER) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "integer";
- }
-
- if ((typeMask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "boolean";
- }
-
- if ((typeMask & android::ResTable_map::TYPE_COLOR) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "color";
- }
-
- if ((typeMask & android::ResTable_map::TYPE_FLOAT) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "float";
- }
-
- if ((typeMask & android::ResTable_map::TYPE_DIMENSION) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "dimension";
- }
-
- if ((typeMask & android::ResTable_map::TYPE_FRACTION) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "fraction";
- }
-
- if ((typeMask & android::ResTable_map::TYPE_ENUM) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "enum";
- }
-
- if ((typeMask & android::ResTable_map::TYPE_FLAGS) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "flags";
- }
-}
-
-void Attribute::print(std::ostream* out) const {
- *out << "(attr) ";
- printMask(out);
-
- if (!symbols.empty()) {
- *out << " [" << util::joiner(symbols, ", ") << "]";
- }
-
- if (minInt != std::numeric_limits<int32_t>::min()) {
- *out << " min=" << minInt;
- }
-
- if (maxInt != std::numeric_limits<int32_t>::max()) {
- *out << " max=" << maxInt;
- }
-
- if (isWeak()) {
- *out << " [weak]";
- }
-}
-
-static void buildAttributeMismatchMessage(DiagMessage* msg, const Attribute* attr,
- const Item* value) {
- *msg << "expected";
- if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
- *msg << " boolean";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_COLOR) {
- *msg << " color";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
- *msg << " dimension";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_ENUM) {
- *msg << " enum";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
- *msg << " flags";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_FLOAT) {
- *msg << " float";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
- *msg << " fraction";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_INTEGER) {
- *msg << " integer";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
- *msg << " reference";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
- *msg << " string";
- }
-
- *msg << " but got " << *value;
-}
-
-bool Attribute::matches(const Item* item, DiagMessage* outMsg) const {
- android::Res_value val = {};
- item->flatten(&val);
-
- // Always allow references.
- const uint32_t mask = typeMask | android::ResTable_map::TYPE_REFERENCE;
- if (!(mask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
- if (outMsg) {
- buildAttributeMismatchMessage(outMsg, this, item);
- }
- return false;
-
- } else if (ResourceUtils::androidTypeToAttributeTypeMask(val.dataType) &
- android::ResTable_map::TYPE_INTEGER) {
- if (static_cast<int32_t>(util::deviceToHost32(val.data)) < minInt) {
- if (outMsg) {
- *outMsg << *item << " is less than minimum integer " << minInt;
- }
- return false;
- } else if (static_cast<int32_t>(util::deviceToHost32(val.data)) > maxInt) {
- if (outMsg) {
- *outMsg << *item << " is greater than maximum integer " << maxInt;
- }
- return false;
- }
- }
- return true;
-}
-
-bool Style::equals(const Value* value) const {
- const Style* other = valueCast<Style>(value);
- if (!other) {
- return false;
- }
- if (bool(parent) != bool(other->parent) ||
- (parent && other->parent && !parent.value().equals(&other->parent.value()))) {
- return false;
- }
-
- if (entries.size() != other->entries.size()) {
- return false;
- }
-
- std::vector<const Entry*> sortedA;
- std::transform(entries.begin(), entries.end(),
- std::back_inserter(sortedA), addPointer<const Entry>);
- std::sort(sortedA.begin(), sortedA.end(), [](const Entry* a, const Entry* b) -> bool {
- return a->key.name < b->key.name;
- });
-
- std::vector<const Entry*> sortedB;
- std::transform(other->entries.begin(), other->entries.end(),
- std::back_inserter(sortedB), addPointer<const Entry>);
- std::sort(sortedB.begin(), sortedB.end(), [](const Entry* a, const Entry* b) -> bool {
- return a->key.name < b->key.name;
- });
-
- return std::equal(sortedA.begin(), sortedA.end(), sortedB.begin(),
- [](const Entry* a, const Entry* b) -> bool {
- return a->key.equals(&b->key) && a->value->equals(b->value.get());
- });
-}
-
-Style* Style::clone(StringPool* newPool) const {
- Style* style = new Style();
- style->parent = parent;
- style->parentInferred = parentInferred;
- style->mComment = mComment;
- style->mSource = mSource;
- for (auto& entry : entries) {
- style->entries.push_back(Entry{
- entry.key,
- std::unique_ptr<Item>(entry.value->clone(newPool))
- });
- }
- return style;
-}
-
-void Style::print(std::ostream* out) const {
- *out << "(style) ";
- if (parent && parent.value().name) {
- if (parent.value().privateReference) {
- *out << "*";
- }
- *out << parent.value().name.value();
- }
- *out << " ["
- << util::joiner(entries, ", ")
- << "]";
-}
-
-static ::std::ostream& operator<<(::std::ostream& out, const Style::Entry& value) {
- if (value.key.name) {
- out << value.key.name.value();
- } else if (value.key.id) {
- out << value.key.id.value();
+ bool set = false;
+ if ((type_mask & android::ResTable_map::TYPE_REFERENCE) != 0) {
+ if (!set) {
+ set = true;
} else {
- out << "???";
+ *out << "|";
}
- out << " = ";
- value.value->print(&out);
- return out;
+ *out << "reference";
+ }
+
+ if ((type_mask & android::ResTable_map::TYPE_STRING) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
+ }
+ *out << "string";
+ }
+
+ if ((type_mask & android::ResTable_map::TYPE_INTEGER) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
+ }
+ *out << "integer";
+ }
+
+ if ((type_mask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
+ }
+ *out << "boolean";
+ }
+
+ if ((type_mask & android::ResTable_map::TYPE_COLOR) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
+ }
+ *out << "color";
+ }
+
+ if ((type_mask & android::ResTable_map::TYPE_FLOAT) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
+ }
+ *out << "float";
+ }
+
+ if ((type_mask & android::ResTable_map::TYPE_DIMENSION) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
+ }
+ *out << "dimension";
+ }
+
+ if ((type_mask & android::ResTable_map::TYPE_FRACTION) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
+ }
+ *out << "fraction";
+ }
+
+ if ((type_mask & android::ResTable_map::TYPE_ENUM) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
+ }
+ *out << "enum";
+ }
+
+ if ((type_mask & android::ResTable_map::TYPE_FLAGS) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
+ }
+ *out << "flags";
+ }
}
-bool Array::equals(const Value* value) const {
- const Array* other = valueCast<Array>(value);
- if (!other) {
- return false;
- }
+void Attribute::Print(std::ostream* out) const {
+ *out << "(attr) ";
+ PrintMask(out);
- if (items.size() != other->items.size()) {
- return false;
- }
+ if (!symbols.empty()) {
+ *out << " [" << util::Joiner(symbols, ", ") << "]";
+ }
- return std::equal(items.begin(), items.end(), other->items.begin(),
- [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
- return a->equals(b.get());
- });
+ if (min_int != std::numeric_limits<int32_t>::min()) {
+ *out << " min=" << min_int;
+ }
+
+ if (max_int != std::numeric_limits<int32_t>::max()) {
+ *out << " max=" << max_int;
+ }
+
+ if (IsWeak()) {
+ *out << " [weak]";
+ }
}
-Array* Array::clone(StringPool* newPool) const {
- Array* array = new Array();
- array->mComment = mComment;
- array->mSource = mSource;
- for (auto& item : items) {
- array->items.emplace_back(std::unique_ptr<Item>(item->clone(newPool)));
- }
- return array;
+static void BuildAttributeMismatchMessage(DiagMessage* msg,
+ const Attribute* attr,
+ const Item* value) {
+ *msg << "expected";
+ if (attr->type_mask & android::ResTable_map::TYPE_BOOLEAN) {
+ *msg << " boolean";
+ }
+
+ if (attr->type_mask & android::ResTable_map::TYPE_COLOR) {
+ *msg << " color";
+ }
+
+ if (attr->type_mask & android::ResTable_map::TYPE_DIMENSION) {
+ *msg << " dimension";
+ }
+
+ if (attr->type_mask & android::ResTable_map::TYPE_ENUM) {
+ *msg << " enum";
+ }
+
+ if (attr->type_mask & android::ResTable_map::TYPE_FLAGS) {
+ *msg << " flags";
+ }
+
+ if (attr->type_mask & android::ResTable_map::TYPE_FLOAT) {
+ *msg << " float";
+ }
+
+ if (attr->type_mask & android::ResTable_map::TYPE_FRACTION) {
+ *msg << " fraction";
+ }
+
+ if (attr->type_mask & android::ResTable_map::TYPE_INTEGER) {
+ *msg << " integer";
+ }
+
+ if (attr->type_mask & android::ResTable_map::TYPE_REFERENCE) {
+ *msg << " reference";
+ }
+
+ if (attr->type_mask & android::ResTable_map::TYPE_STRING) {
+ *msg << " string";
+ }
+
+ *msg << " but got " << *value;
}
-void Array::print(std::ostream* out) const {
- *out << "(array) ["
- << util::joiner(items, ", ")
- << "]";
+bool Attribute::Matches(const Item* item, DiagMessage* out_msg) const {
+ android::Res_value val = {};
+ item->Flatten(&val);
+
+ // Always allow references.
+ const uint32_t mask = type_mask | android::ResTable_map::TYPE_REFERENCE;
+ if (!(mask & ResourceUtils::AndroidTypeToAttributeTypeMask(val.dataType))) {
+ if (out_msg) {
+ BuildAttributeMismatchMessage(out_msg, this, item);
+ }
+ return false;
+
+ } else if (ResourceUtils::AndroidTypeToAttributeTypeMask(val.dataType) &
+ android::ResTable_map::TYPE_INTEGER) {
+ if (static_cast<int32_t>(util::DeviceToHost32(val.data)) < min_int) {
+ if (out_msg) {
+ *out_msg << *item << " is less than minimum integer " << min_int;
+ }
+ return false;
+ } else if (static_cast<int32_t>(util::DeviceToHost32(val.data)) > max_int) {
+ if (out_msg) {
+ *out_msg << *item << " is greater than maximum integer " << max_int;
+ }
+ return false;
+ }
+ }
+ return true;
}
-bool Plural::equals(const Value* value) const {
- const Plural* other = valueCast<Plural>(value);
- if (!other) {
- return false;
- }
+bool Style::Equals(const Value* value) const {
+ const Style* other = ValueCast<Style>(value);
+ if (!other) {
+ return false;
+ }
+ if (bool(parent) != bool(other->parent) ||
+ (parent && other->parent &&
+ !parent.value().Equals(&other->parent.value()))) {
+ return false;
+ }
- if (values.size() != other->values.size()) {
- return false;
- }
+ if (entries.size() != other->entries.size()) {
+ return false;
+ }
- return std::equal(values.begin(), values.end(), other->values.begin(),
- [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
- if (bool(a) != bool(b)) {
- return false;
- }
- return bool(a) == bool(b) || a->equals(b.get());
- });
+ std::vector<const Entry*> sorted_a;
+ std::transform(entries.begin(), entries.end(), std::back_inserter(sorted_a),
+ addPointer<const Entry>);
+ std::sort(sorted_a.begin(), sorted_a.end(),
+ [](const Entry* a, const Entry* b) -> bool {
+ return a->key.name < b->key.name;
+ });
+
+ std::vector<const Entry*> sorted_b;
+ std::transform(other->entries.begin(), other->entries.end(),
+ std::back_inserter(sorted_b), addPointer<const Entry>);
+ std::sort(sorted_b.begin(), sorted_b.end(),
+ [](const Entry* a, const Entry* b) -> bool {
+ return a->key.name < b->key.name;
+ });
+
+ return std::equal(sorted_a.begin(), sorted_a.end(), sorted_b.begin(),
+ [](const Entry* a, const Entry* b) -> bool {
+ return a->key.Equals(&b->key) &&
+ a->value->Equals(b->value.get());
+ });
}
-Plural* Plural::clone(StringPool* newPool) const {
- Plural* p = new Plural();
- p->mComment = mComment;
- p->mSource = mSource;
- const size_t count = values.size();
- for (size_t i = 0; i < count; i++) {
- if (values[i]) {
- p->values[i] = std::unique_ptr<Item>(values[i]->clone(newPool));
- }
- }
- return p;
+Style* Style::Clone(StringPool* new_pool) const {
+ Style* style = new Style();
+ style->parent = parent;
+ style->parent_inferred = parent_inferred;
+ style->comment_ = comment_;
+ style->source_ = source_;
+ for (auto& entry : entries) {
+ style->entries.push_back(
+ Entry{entry.key, std::unique_ptr<Item>(entry.value->Clone(new_pool))});
+ }
+ return style;
}
-void Plural::print(std::ostream* out) const {
- *out << "(plural)";
- if (values[Zero]) {
- *out << " zero=" << *values[Zero];
+void Style::Print(std::ostream* out) const {
+ *out << "(style) ";
+ if (parent && parent.value().name) {
+ if (parent.value().private_reference) {
+ *out << "*";
}
-
- if (values[One]) {
- *out << " one=" << *values[One];
- }
-
- if (values[Two]) {
- *out << " two=" << *values[Two];
- }
-
- if (values[Few]) {
- *out << " few=" << *values[Few];
- }
-
- if (values[Many]) {
- *out << " many=" << *values[Many];
- }
+ *out << parent.value().name.value();
+ }
+ *out << " [" << util::Joiner(entries, ", ") << "]";
}
-static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Item>& item) {
- return out << *item;
+static ::std::ostream& operator<<(::std::ostream& out,
+ const Style::Entry& value) {
+ if (value.key.name) {
+ out << value.key.name.value();
+ } else if (value.key.id) {
+ out << value.key.id.value();
+ } else {
+ out << "???";
+ }
+ out << " = ";
+ value.value->Print(&out);
+ return out;
}
-bool Styleable::equals(const Value* value) const {
- const Styleable* other = valueCast<Styleable>(value);
- if (!other) {
- return false;
+bool Array::Equals(const Value* value) const {
+ const Array* other = ValueCast<Array>(value);
+ if (!other) {
+ return false;
+ }
+
+ if (items.size() != other->items.size()) {
+ return false;
+ }
+
+ return std::equal(items.begin(), items.end(), other->items.begin(),
+ [](const std::unique_ptr<Item>& a,
+ const std::unique_ptr<Item>& b) -> bool {
+ return a->Equals(b.get());
+ });
+}
+
+Array* Array::Clone(StringPool* new_pool) const {
+ Array* array = new Array();
+ array->comment_ = comment_;
+ array->source_ = source_;
+ for (auto& item : items) {
+ array->items.emplace_back(std::unique_ptr<Item>(item->Clone(new_pool)));
+ }
+ return array;
+}
+
+void Array::Print(std::ostream* out) const {
+ *out << "(array) [" << util::Joiner(items, ", ") << "]";
+}
+
+bool Plural::Equals(const Value* value) const {
+ const Plural* other = ValueCast<Plural>(value);
+ if (!other) {
+ return false;
+ }
+
+ if (values.size() != other->values.size()) {
+ return false;
+ }
+
+ return std::equal(values.begin(), values.end(), other->values.begin(),
+ [](const std::unique_ptr<Item>& a,
+ const std::unique_ptr<Item>& b) -> bool {
+ if (bool(a) != bool(b)) {
+ return false;
+ }
+ return bool(a) == bool(b) || a->Equals(b.get());
+ });
+}
+
+Plural* Plural::Clone(StringPool* new_pool) const {
+ Plural* p = new Plural();
+ p->comment_ = comment_;
+ p->source_ = source_;
+ const size_t count = values.size();
+ for (size_t i = 0; i < count; i++) {
+ if (values[i]) {
+ p->values[i] = std::unique_ptr<Item>(values[i]->Clone(new_pool));
}
-
- if (entries.size() != other->entries.size()) {
- return false;
- }
-
- return std::equal(entries.begin(), entries.end(), other->entries.begin(),
- [](const Reference& a, const Reference& b) -> bool {
- return a.equals(&b);
- });
+ }
+ return p;
}
-Styleable* Styleable::clone(StringPool* /*newPool*/) const {
- return new Styleable(*this);
+void Plural::Print(std::ostream* out) const {
+ *out << "(plural)";
+ if (values[Zero]) {
+ *out << " zero=" << *values[Zero];
+ }
+
+ if (values[One]) {
+ *out << " one=" << *values[One];
+ }
+
+ if (values[Two]) {
+ *out << " two=" << *values[Two];
+ }
+
+ if (values[Few]) {
+ *out << " few=" << *values[Few];
+ }
+
+ if (values[Many]) {
+ *out << " many=" << *values[Many];
+ }
}
-void Styleable::print(std::ostream* out) const {
- *out << "(styleable) " << " ["
- << util::joiner(entries, ", ")
- << "]";
+static ::std::ostream& operator<<(::std::ostream& out,
+ const std::unique_ptr<Item>& item) {
+ return out << *item;
+}
+
+bool Styleable::Equals(const Value* value) const {
+ const Styleable* other = ValueCast<Styleable>(value);
+ if (!other) {
+ return false;
+ }
+
+ if (entries.size() != other->entries.size()) {
+ return false;
+ }
+
+ return std::equal(entries.begin(), entries.end(), other->entries.begin(),
+ [](const Reference& a, const Reference& b) -> bool {
+ return a.Equals(&b);
+ });
+}
+
+Styleable* Styleable::Clone(StringPool* /*new_pool*/) const {
+ return new Styleable(*this);
+}
+
+void Styleable::Print(std::ostream* out) const {
+ *out << "(styleable) "
+ << " [" << util::Joiner(entries, ", ") << "]";
}
bool operator<(const Reference& a, const Reference& b) {
- int cmp = a.name.valueOrDefault({}).compare(b.name.valueOrDefault({}));
- if (cmp != 0) return cmp < 0;
- return a.id < b.id;
+ int cmp = a.name.value_or_default({}).compare(b.name.value_or_default({}));
+ if (cmp != 0) return cmp < 0;
+ return a.id < b.id;
}
bool operator==(const Reference& a, const Reference& b) {
- return a.name == b.name && a.id == b.id;
+ return a.name == b.name && a.id == b.id;
}
bool operator!=(const Reference& a, const Reference& b) {
- return a.name != b.name || a.id != b.id;
+ return a.name != b.name || a.id != b.id;
}
struct NameOnlyComparator {
- bool operator()(const Reference& a, const Reference& b) const {
- return a.name < b.name;
- }
+ bool operator()(const Reference& a, const Reference& b) const {
+ return a.name < b.name;
+ }
};
-void Styleable::mergeWith(Styleable* other) {
- // Compare only names, because some References may already have their IDs assigned
- // (framework IDs that don't change).
- std::set<Reference, NameOnlyComparator> references;
- references.insert(entries.begin(), entries.end());
- references.insert(other->entries.begin(), other->entries.end());
- entries.clear();
- entries.reserve(references.size());
- entries.insert(entries.end(), references.begin(), references.end());
+void Styleable::MergeWith(Styleable* other) {
+ // Compare only names, because some References may already have their IDs
+ // assigned
+ // (framework IDs that don't change).
+ std::set<Reference, NameOnlyComparator> references;
+ references.insert(entries.begin(), entries.end());
+ references.insert(other->entries.begin(), other->entries.end());
+ entries.clear();
+ entries.reserve(references.size());
+ entries.insert(entries.end(), references.begin(), references.end());
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 5e5d1f3..ea73615 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -17,17 +17,18 @@
#ifndef AAPT_RESOURCE_VALUES_H
#define AAPT_RESOURCE_VALUES_H
+#include <array>
+#include <ostream>
+#include <vector>
+
+#include "androidfw/ResourceTypes.h"
+
#include "Diagnostics.h"
#include "Resource.h"
#include "StringPool.h"
#include "io/File.h"
#include "util/Maybe.h"
-#include <array>
-#include <androidfw/ResourceTypes.h>
-#include <ostream>
-#include <vector>
-
namespace aapt {
struct RawValueVisitor;
@@ -40,84 +41,65 @@
* but it is the simplest strategy.
*/
struct Value {
- virtual ~Value() = default;
+ virtual ~Value() = default;
- /**
- * Whether this value is weak and can be overridden without
- * warning or error. Default is false.
- */
- bool isWeak() const {
- return mWeak;
- }
+ /**
+ * Whether this value is weak and can be overridden without
+ * warning or error. Default is false.
+ */
+ bool IsWeak() const { return weak_; }
- void setWeak(bool val) {
- mWeak = val;
- }
+ void SetWeak(bool val) { weak_ = val; }
- // Whether the value is marked as translateable.
- // This does not persist when flattened.
- // It is only used during compilation phase.
- void setTranslateable(bool val) {
- mTranslateable = val;
- }
+ // Whether the value is marked as translateable.
+ // This does not persist when flattened.
+ // It is only used during compilation phase.
+ void SetTranslateable(bool val) { translateable_ = val; }
- // Default true.
- bool isTranslateable() const {
- return mTranslateable;
- }
+ // Default true.
+ bool IsTranslateable() const { return translateable_; }
- /**
- * Returns the source where this value was defined.
- */
- const Source& getSource() const {
- return mSource;
- }
+ /**
+ * Returns the source where this value was defined.
+ */
+ const Source& GetSource() const { return source_; }
- void setSource(const Source& source) {
- mSource = source;
- }
+ void SetSource(const Source& source) { source_ = source; }
- void setSource(Source&& source) {
- mSource = std::move(source);
- }
+ void SetSource(Source&& source) { source_ = std::move(source); }
- /**
- * Returns the comment that was associated with this resource.
- */
- const std::string& getComment() const {
- return mComment;
- }
+ /**
+ * Returns the comment that was associated with this resource.
+ */
+ const std::string& GetComment() const { return comment_; }
- void setComment(const StringPiece& str) {
- mComment = str.toString();
- }
+ void SetComment(const StringPiece& str) { comment_ = str.ToString(); }
- void setComment(std::string&& str) {
- mComment = std::move(str);
- }
+ void SetComment(std::string&& str) { comment_ = std::move(str); }
- virtual bool equals(const Value* value) const = 0;
+ virtual bool Equals(const Value* value) const = 0;
- /**
- * Calls the appropriate overload of ValueVisitor.
- */
- virtual void accept(RawValueVisitor* visitor) = 0;
+ /**
+ * Calls the appropriate overload of ValueVisitor.
+ */
+ virtual void Accept(RawValueVisitor* visitor) = 0;
- /**
- * Clone the value.
- */
- virtual Value* clone(StringPool* newPool) const = 0;
+ /**
+ * Clone the value. new_pool is the new StringPool that
+ * any resources with strings should use when copying their string.
+ */
+ virtual Value* Clone(StringPool* new_pool) const = 0;
- /**
- * Human readable printout of this value.
- */
- virtual void print(std::ostream* out) const = 0;
+ /**
+ * Human readable printout of this value.
+ */
+ virtual void Print(std::ostream* out) const = 0;
-protected:
- Source mSource;
- std::string mComment;
- bool mWeak = false;
- bool mTranslateable = true;
+ protected:
+ Source source_;
+ std::string comment_;
+ bool weak_ = false;
+ bool translateable_ = true;
};
/**
@@ -125,23 +107,24 @@
*/
template <typename Derived>
struct BaseValue : public Value {
- void accept(RawValueVisitor* visitor) override;
+ void Accept(RawValueVisitor* visitor) override;
};
/**
* A resource item with a single value. This maps to android::ResTable_entry.
*/
struct Item : public Value {
- /**
- * Clone the Item.
- */
- virtual Item* clone(StringPool* newPool) const override = 0;
+ /**
+ * Clone the Item.
+ */
+ virtual Item* Clone(StringPool* new_pool) const override = 0;
- /**
- * Fills in an android::Res_value structure with this Item's binary representation.
- * Returns false if an error occurred.
- */
- virtual bool flatten(android::Res_value* outValue) const = 0;
+ /**
+ * Fills in an android::Res_value structure with this Item's binary
+ * representation.
+ * Returns false if an error occurred.
+ */
+ virtual bool Flatten(android::Res_value* out_value) const = 0;
};
/**
@@ -149,35 +132,37 @@
*/
template <typename Derived>
struct BaseItem : public Item {
- void accept(RawValueVisitor* visitor) override;
+ void Accept(RawValueVisitor* visitor) override;
};
/**
- * A reference to another resource. This maps to android::Res_value::TYPE_REFERENCE.
+ * A reference to another resource. This maps to
+ * android::Res_value::TYPE_REFERENCE.
*
- * A reference can be symbolic (with the name set to a valid resource name) or be
+ * A reference can be symbolic (with the name set to a valid resource name) or
+ * be
* numeric (the id is set to a valid resource ID).
*/
struct Reference : public BaseItem<Reference> {
- enum class Type {
- kResource,
- kAttribute,
- };
+ enum class Type {
+ kResource,
+ kAttribute,
+ };
- Maybe<ResourceName> name;
- Maybe<ResourceId> id;
- Reference::Type referenceType;
- bool privateReference = false;
+ Maybe<ResourceName> name;
+ Maybe<ResourceId> id;
+ Reference::Type reference_type;
+ bool private_reference = false;
- Reference();
- explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
- explicit Reference(const ResourceId& i, Type type = Type::kResource);
- explicit Reference(const ResourceNameRef& n, const ResourceId& i);
+ Reference();
+ explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
+ explicit Reference(const ResourceId& i, Type type = Type::kResource);
+ Reference(const ResourceNameRef& n, const ResourceId& i);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- Reference* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool Equals(const Value* value) const override;
+ bool Flatten(android::Res_value* out_value) const override;
+ Reference* Clone(StringPool* new_pool) const override;
+ void Print(std::ostream* out) const override;
};
bool operator<(const Reference&, const Reference&);
@@ -187,11 +172,11 @@
* An ID resource. Has no real value, just a place holder.
*/
struct Id : public BaseItem<Id> {
- Id() { mWeak = true; }
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* out) const override;
- Id* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ Id() { weak_ = true; }
+ bool Equals(const Value* value) const override;
+ bool Flatten(android::Res_value* out) const override;
+ Id* Clone(StringPool* new_pool) const override;
+ void Print(std::ostream* out) const override;
};
/**
@@ -200,164 +185,157 @@
* end up in the final resource table.
*/
struct RawString : public BaseItem<RawString> {
- StringPool::Ref value;
+ StringPool::Ref value;
- explicit RawString(const StringPool::Ref& ref);
+ explicit RawString(const StringPool::Ref& ref);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- RawString* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool Equals(const Value* value) const override;
+ bool Flatten(android::Res_value* out_value) const override;
+ RawString* Clone(StringPool* new_pool) const override;
+ void Print(std::ostream* out) const override;
};
struct String : public BaseItem<String> {
- StringPool::Ref value;
+ StringPool::Ref value;
- explicit String(const StringPool::Ref& ref);
+ explicit String(const StringPool::Ref& ref);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- String* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool Equals(const Value* value) const override;
+ bool Flatten(android::Res_value* out_value) const override;
+ String* Clone(StringPool* new_pool) const override;
+ void Print(std::ostream* out) const override;
};
struct StyledString : public BaseItem<StyledString> {
- StringPool::StyleRef value;
+ StringPool::StyleRef value;
- explicit StyledString(const StringPool::StyleRef& ref);
+ explicit StyledString(const StringPool::StyleRef& ref);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- StyledString* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool Equals(const Value* value) const override;
+ bool Flatten(android::Res_value* out_value) const override;
+ StyledString* Clone(StringPool* new_pool) const override;
+ void Print(std::ostream* out) const override;
};
struct FileReference : public BaseItem<FileReference> {
- StringPool::Ref path;
+ StringPool::Ref path;
- /**
- * A handle to the file object from which this file can be read.
- */
- io::IFile* file = nullptr;
+ /**
+ * A handle to the file object from which this file can be read.
+ */
+ io::IFile* file = nullptr;
- FileReference() = default;
- explicit FileReference(const StringPool::Ref& path);
+ FileReference() = default;
+ explicit FileReference(const StringPool::Ref& path);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- FileReference* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool Equals(const Value* value) const override;
+ bool Flatten(android::Res_value* out_value) const override;
+ FileReference* Clone(StringPool* new_pool) const override;
+ void Print(std::ostream* out) const override;
};
/**
* Represents any other android::Res_value.
*/
struct BinaryPrimitive : public BaseItem<BinaryPrimitive> {
- android::Res_value value;
+ android::Res_value value;
- BinaryPrimitive() = default;
- explicit BinaryPrimitive(const android::Res_value& val);
- BinaryPrimitive(uint8_t dataType, uint32_t data);
+ BinaryPrimitive() = default;
+ explicit BinaryPrimitive(const android::Res_value& val);
+ BinaryPrimitive(uint8_t dataType, uint32_t data);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- BinaryPrimitive* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool Equals(const Value* value) const override;
+ bool Flatten(android::Res_value* out_value) const override;
+ BinaryPrimitive* Clone(StringPool* new_pool) const override;
+ void Print(std::ostream* out) const override;
};
struct Attribute : public BaseValue<Attribute> {
- struct Symbol {
- Reference symbol;
- uint32_t value;
- };
+ struct Symbol {
+ Reference symbol;
+ uint32_t value;
+ };
- uint32_t typeMask;
- int32_t minInt;
- int32_t maxInt;
- std::vector<Symbol> symbols;
+ uint32_t type_mask;
+ int32_t min_int;
+ int32_t max_int;
+ std::vector<Symbol> symbols;
- explicit Attribute(bool w, uint32_t t = 0u);
+ explicit Attribute(bool w, uint32_t t = 0u);
- bool equals(const Value* value) const override;
- Attribute* clone(StringPool* newPool) const override;
- void printMask(std::ostream* out) const;
- void print(std::ostream* out) const override;
- bool matches(const Item* item, DiagMessage* outMsg) const;
+ bool Equals(const Value* value) const override;
+ Attribute* Clone(StringPool* new_pool) const override;
+ void PrintMask(std::ostream* out) const;
+ void Print(std::ostream* out) const override;
+ bool Matches(const Item* item, DiagMessage* out_msg) const;
};
struct Style : public BaseValue<Style> {
- struct Entry {
- Reference key;
- std::unique_ptr<Item> value;
- };
+ struct Entry {
+ Reference key;
+ std::unique_ptr<Item> value;
+ };
- Maybe<Reference> parent;
+ Maybe<Reference> parent;
- /**
- * If set to true, the parent was auto inferred from the
- * style's name.
- */
- bool parentInferred = false;
+ /**
+ * If set to true, the parent was auto inferred from the
+ * style's name.
+ */
+ bool parent_inferred = false;
- std::vector<Entry> entries;
+ std::vector<Entry> entries;
- bool equals(const Value* value) const override;
- Style* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool Equals(const Value* value) const override;
+ Style* Clone(StringPool* new_pool) const override;
+ void Print(std::ostream* out) const override;
};
struct Array : public BaseValue<Array> {
- std::vector<std::unique_ptr<Item>> items;
+ std::vector<std::unique_ptr<Item>> items;
- bool equals(const Value* value) const override;
- Array* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool Equals(const Value* value) const override;
+ Array* Clone(StringPool* new_pool) const override;
+ void Print(std::ostream* out) const override;
};
struct Plural : public BaseValue<Plural> {
- enum {
- Zero = 0,
- One,
- Two,
- Few,
- Many,
- Other,
- Count
- };
+ enum { Zero = 0, One, Two, Few, Many, Other, Count };
- std::array<std::unique_ptr<Item>, Count> values;
+ std::array<std::unique_ptr<Item>, Count> values;
- bool equals(const Value* value) const override;
- Plural* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool Equals(const Value* value) const override;
+ Plural* Clone(StringPool* new_pool) const override;
+ void Print(std::ostream* out) const override;
};
struct Styleable : public BaseValue<Styleable> {
- std::vector<Reference> entries;
+ std::vector<Reference> entries;
- bool equals(const Value* value) const override;
- Styleable* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
- void mergeWith(Styleable* styleable);
+ bool Equals(const Value* value) const override;
+ Styleable* Clone(StringPool* newPool) const override;
+ void Print(std::ostream* out) const override;
+ void MergeWith(Styleable* styleable);
};
/**
* Stream operator for printing Value objects.
*/
inline ::std::ostream& operator<<(::std::ostream& out, const Value& value) {
- value.print(&out);
- return out;
+ value.Print(&out);
+ return out;
}
-inline ::std::ostream& operator<<(::std::ostream& out, const Attribute::Symbol& s) {
- if (s.symbol.name) {
- out << s.symbol.name.value().entry;
- } else {
- out << "???";
- }
- return out << "=" << s.value;
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const Attribute::Symbol& s) {
+ if (s.symbol.name) {
+ out << s.symbol.name.value().entry;
+ } else {
+ out << "???";
+ }
+ return out << "=" << s.value;
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_RESOURCE_VALUES_H
+#endif // AAPT_RESOURCE_VALUES_H
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
index 06cddc7..720ab91 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -15,101 +15,102 @@
*/
#include "Resource.h"
+
#include "test/Test.h"
namespace aapt {
TEST(ResourceTypeTest, ParseResourceTypes) {
- const ResourceType* type = parseResourceType("anim");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kAnim);
+ const ResourceType* type = ParseResourceType("anim");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kAnim);
- type = parseResourceType("animator");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kAnimator);
+ type = ParseResourceType("animator");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kAnimator);
- type = parseResourceType("array");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kArray);
+ type = ParseResourceType("array");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kArray);
- type = parseResourceType("attr");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kAttr);
+ type = ParseResourceType("attr");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kAttr);
- type = parseResourceType("^attr-private");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kAttrPrivate);
+ type = ParseResourceType("^attr-private");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kAttrPrivate);
- type = parseResourceType("bool");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kBool);
+ type = ParseResourceType("bool");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kBool);
- type = parseResourceType("color");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kColor);
+ type = ParseResourceType("color");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kColor);
- type = parseResourceType("dimen");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kDimen);
+ type = ParseResourceType("dimen");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kDimen);
- type = parseResourceType("drawable");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kDrawable);
+ type = ParseResourceType("drawable");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kDrawable);
- type = parseResourceType("fraction");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kFraction);
+ type = ParseResourceType("fraction");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kFraction);
- type = parseResourceType("id");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kId);
+ type = ParseResourceType("id");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kId);
- type = parseResourceType("integer");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kInteger);
+ type = ParseResourceType("integer");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kInteger);
- type = parseResourceType("interpolator");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kInterpolator);
+ type = ParseResourceType("interpolator");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kInterpolator);
- type = parseResourceType("layout");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kLayout);
+ type = ParseResourceType("layout");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kLayout);
- type = parseResourceType("menu");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kMenu);
+ type = ParseResourceType("menu");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kMenu);
- type = parseResourceType("mipmap");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kMipmap);
+ type = ParseResourceType("mipmap");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kMipmap);
- type = parseResourceType("plurals");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kPlurals);
+ type = ParseResourceType("plurals");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kPlurals);
- type = parseResourceType("raw");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kRaw);
+ type = ParseResourceType("raw");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kRaw);
- type = parseResourceType("string");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kString);
+ type = ParseResourceType("string");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kString);
- type = parseResourceType("style");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kStyle);
+ type = ParseResourceType("style");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kStyle);
- type = parseResourceType("transition");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kTransition);
+ type = ParseResourceType("transition");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kTransition);
- type = parseResourceType("xml");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kXml);
+ type = ParseResourceType("xml");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kXml);
- type = parseResourceType("blahaha");
- EXPECT_EQ(type, nullptr);
+ type = ParseResourceType("blahaha");
+ EXPECT_EQ(type, nullptr);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index ccf0383..c7f920a 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -27,719 +27,721 @@
static int sDevelopmentSdkLevel = 26;
static const std::vector<std::pair<uint16_t, size_t>> sAttrIdMap = {
- { 0x021c, 1 },
- { 0x021d, 2 },
- { 0x0269, SDK_CUPCAKE },
- { 0x028d, SDK_DONUT },
- { 0x02ad, SDK_ECLAIR },
- { 0x02b3, SDK_ECLAIR_0_1 },
- { 0x02b5, SDK_ECLAIR_MR1 },
- { 0x02bd, SDK_FROYO },
- { 0x02cb, SDK_GINGERBREAD },
- { 0x0361, SDK_HONEYCOMB },
- { 0x0363, SDK_HONEYCOMB_MR1 },
- { 0x0366, SDK_HONEYCOMB_MR2 },
- { 0x03a6, SDK_ICE_CREAM_SANDWICH },
- { 0x03ae, SDK_JELLY_BEAN },
- { 0x03cc, SDK_JELLY_BEAN_MR1 },
- { 0x03da, SDK_JELLY_BEAN_MR2 },
- { 0x03f1, SDK_KITKAT },
- { 0x03f6, SDK_KITKAT_WATCH },
- { 0x04ce, SDK_LOLLIPOP },
+ {0x021c, 1},
+ {0x021d, 2},
+ {0x0269, SDK_CUPCAKE},
+ {0x028d, SDK_DONUT},
+ {0x02ad, SDK_ECLAIR},
+ {0x02b3, SDK_ECLAIR_0_1},
+ {0x02b5, SDK_ECLAIR_MR1},
+ {0x02bd, SDK_FROYO},
+ {0x02cb, SDK_GINGERBREAD},
+ {0x0361, SDK_HONEYCOMB},
+ {0x0363, SDK_HONEYCOMB_MR1},
+ {0x0366, SDK_HONEYCOMB_MR2},
+ {0x03a6, SDK_ICE_CREAM_SANDWICH},
+ {0x03ae, SDK_JELLY_BEAN},
+ {0x03cc, SDK_JELLY_BEAN_MR1},
+ {0x03da, SDK_JELLY_BEAN_MR2},
+ {0x03f1, SDK_KITKAT},
+ {0x03f6, SDK_KITKAT_WATCH},
+ {0x04ce, SDK_LOLLIPOP},
};
-static bool lessEntryId(const std::pair<uint16_t, size_t>& p, uint16_t entryId) {
- return p.first < entryId;
+static bool less_entry_id(const std::pair<uint16_t, size_t>& p,
+ uint16_t entryId) {
+ return p.first < entryId;
}
-size_t findAttributeSdkLevel(const ResourceId& id) {
- if (id.packageId() != 0x01 && id.typeId() != 0x01) {
- return 0;
- }
- auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(), id.entryId(), lessEntryId);
- if (iter == sAttrIdMap.end()) {
- return SDK_LOLLIPOP_MR1;
- }
- return iter->second;
+size_t FindAttributeSdkLevel(const ResourceId& id) {
+ if (id.package_id() != 0x01 && id.type_id() != 0x01) {
+ return 0;
+ }
+ auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(),
+ id.entry_id(), less_entry_id);
+ if (iter == sAttrIdMap.end()) {
+ return SDK_LOLLIPOP_MR1;
+ }
+ return iter->second;
}
static const std::unordered_map<std::string, size_t> sAttrMap = {
- { "marqueeRepeatLimit", 2 },
- { "windowNoDisplay", 3 },
- { "backgroundDimEnabled", 3 },
- { "inputType", 3 },
- { "isDefault", 3 },
- { "windowDisablePreview", 3 },
- { "privateImeOptions", 3 },
- { "editorExtras", 3 },
- { "settingsActivity", 3 },
- { "fastScrollEnabled", 3 },
- { "reqTouchScreen", 3 },
- { "reqKeyboardType", 3 },
- { "reqHardKeyboard", 3 },
- { "reqNavigation", 3 },
- { "windowSoftInputMode", 3 },
- { "imeFullscreenBackground", 3 },
- { "noHistory", 3 },
- { "headerDividersEnabled", 3 },
- { "footerDividersEnabled", 3 },
- { "candidatesTextStyleSpans", 3 },
- { "smoothScrollbar", 3 },
- { "reqFiveWayNav", 3 },
- { "keyBackground", 3 },
- { "keyTextSize", 3 },
- { "labelTextSize", 3 },
- { "keyTextColor", 3 },
- { "keyPreviewLayout", 3 },
- { "keyPreviewOffset", 3 },
- { "keyPreviewHeight", 3 },
- { "verticalCorrection", 3 },
- { "popupLayout", 3 },
- { "state_long_pressable", 3 },
- { "keyWidth", 3 },
- { "keyHeight", 3 },
- { "horizontalGap", 3 },
- { "verticalGap", 3 },
- { "rowEdgeFlags", 3 },
- { "codes", 3 },
- { "popupKeyboard", 3 },
- { "popupCharacters", 3 },
- { "keyEdgeFlags", 3 },
- { "isModifier", 3 },
- { "isSticky", 3 },
- { "isRepeatable", 3 },
- { "iconPreview", 3 },
- { "keyOutputText", 3 },
- { "keyLabel", 3 },
- { "keyIcon", 3 },
- { "keyboardMode", 3 },
- { "isScrollContainer", 3 },
- { "fillEnabled", 3 },
- { "updatePeriodMillis", 3 },
- { "initialLayout", 3 },
- { "voiceSearchMode", 3 },
- { "voiceLanguageModel", 3 },
- { "voicePromptText", 3 },
- { "voiceLanguage", 3 },
- { "voiceMaxResults", 3 },
- { "bottomOffset", 3 },
- { "topOffset", 3 },
- { "allowSingleTap", 3 },
- { "handle", 3 },
- { "content", 3 },
- { "animateOnClick", 3 },
- { "configure", 3 },
- { "hapticFeedbackEnabled", 3 },
- { "innerRadius", 3 },
- { "thickness", 3 },
- { "sharedUserLabel", 3 },
- { "dropDownWidth", 3 },
- { "dropDownAnchor", 3 },
- { "imeOptions", 3 },
- { "imeActionLabel", 3 },
- { "imeActionId", 3 },
- { "imeExtractEnterAnimation", 3 },
- { "imeExtractExitAnimation", 3 },
- { "tension", 4 },
- { "extraTension", 4 },
- { "anyDensity", 4 },
- { "searchSuggestThreshold", 4 },
- { "includeInGlobalSearch", 4 },
- { "onClick", 4 },
- { "targetSdkVersion", 4 },
- { "maxSdkVersion", 4 },
- { "testOnly", 4 },
- { "contentDescription", 4 },
- { "gestureStrokeWidth", 4 },
- { "gestureColor", 4 },
- { "uncertainGestureColor", 4 },
- { "fadeOffset", 4 },
- { "fadeDuration", 4 },
- { "gestureStrokeType", 4 },
- { "gestureStrokeLengthThreshold", 4 },
- { "gestureStrokeSquarenessThreshold", 4 },
- { "gestureStrokeAngleThreshold", 4 },
- { "eventsInterceptionEnabled", 4 },
- { "fadeEnabled", 4 },
- { "backupAgent", 4 },
- { "allowBackup", 4 },
- { "glEsVersion", 4 },
- { "queryAfterZeroResults", 4 },
- { "dropDownHeight", 4 },
- { "smallScreens", 4 },
- { "normalScreens", 4 },
- { "largeScreens", 4 },
- { "progressBarStyleInverse", 4 },
- { "progressBarStyleSmallInverse", 4 },
- { "progressBarStyleLargeInverse", 4 },
- { "searchSettingsDescription", 4 },
- { "textColorPrimaryInverseDisableOnly", 4 },
- { "autoUrlDetect", 4 },
- { "resizeable", 4 },
- { "required", 5 },
- { "accountType", 5 },
- { "contentAuthority", 5 },
- { "userVisible", 5 },
- { "windowShowWallpaper", 5 },
- { "wallpaperOpenEnterAnimation", 5 },
- { "wallpaperOpenExitAnimation", 5 },
- { "wallpaperCloseEnterAnimation", 5 },
- { "wallpaperCloseExitAnimation", 5 },
- { "wallpaperIntraOpenEnterAnimation", 5 },
- { "wallpaperIntraOpenExitAnimation", 5 },
- { "wallpaperIntraCloseEnterAnimation", 5 },
- { "wallpaperIntraCloseExitAnimation", 5 },
- { "supportsUploading", 5 },
- { "killAfterRestore", 5 },
- { "restoreNeedsApplication", 5 },
- { "smallIcon", 5 },
- { "accountPreferences", 5 },
- { "textAppearanceSearchResultSubtitle", 5 },
- { "textAppearanceSearchResultTitle", 5 },
- { "summaryColumn", 5 },
- { "detailColumn", 5 },
- { "detailSocialSummary", 5 },
- { "thumbnail", 5 },
- { "detachWallpaper", 5 },
- { "finishOnCloseSystemDialogs", 5 },
- { "scrollbarFadeDuration", 5 },
- { "scrollbarDefaultDelayBeforeFade", 5 },
- { "fadeScrollbars", 5 },
- { "colorBackgroundCacheHint", 5 },
- { "dropDownHorizontalOffset", 5 },
- { "dropDownVerticalOffset", 5 },
- { "quickContactBadgeStyleWindowSmall", 6 },
- { "quickContactBadgeStyleWindowMedium", 6 },
- { "quickContactBadgeStyleWindowLarge", 6 },
- { "quickContactBadgeStyleSmallWindowSmall", 6 },
- { "quickContactBadgeStyleSmallWindowMedium", 6 },
- { "quickContactBadgeStyleSmallWindowLarge", 6 },
- { "author", 7 },
- { "autoStart", 7 },
- { "expandableListViewWhiteStyle", 8 },
- { "installLocation", 8 },
- { "vmSafeMode", 8 },
- { "webTextViewStyle", 8 },
- { "restoreAnyVersion", 8 },
- { "tabStripLeft", 8 },
- { "tabStripRight", 8 },
- { "tabStripEnabled", 8 },
- { "logo", 9 },
- { "xlargeScreens", 9 },
- { "immersive", 9 },
- { "overScrollMode", 9 },
- { "overScrollHeader", 9 },
- { "overScrollFooter", 9 },
- { "filterTouchesWhenObscured", 9 },
- { "textSelectHandleLeft", 9 },
- { "textSelectHandleRight", 9 },
- { "textSelectHandle", 9 },
- { "textSelectHandleWindowStyle", 9 },
- { "popupAnimationStyle", 9 },
- { "screenSize", 9 },
- { "screenDensity", 9 },
- { "allContactsName", 11 },
- { "windowActionBar", 11 },
- { "actionBarStyle", 11 },
- { "navigationMode", 11 },
- { "displayOptions", 11 },
- { "subtitle", 11 },
- { "customNavigationLayout", 11 },
- { "hardwareAccelerated", 11 },
- { "measureWithLargestChild", 11 },
- { "animateFirstView", 11 },
- { "dropDownSpinnerStyle", 11 },
- { "actionDropDownStyle", 11 },
- { "actionButtonStyle", 11 },
- { "showAsAction", 11 },
- { "previewImage", 11 },
- { "actionModeBackground", 11 },
- { "actionModeCloseDrawable", 11 },
- { "windowActionModeOverlay", 11 },
- { "valueFrom", 11 },
- { "valueTo", 11 },
- { "valueType", 11 },
- { "propertyName", 11 },
- { "ordering", 11 },
- { "fragment", 11 },
- { "windowActionBarOverlay", 11 },
- { "fragmentOpenEnterAnimation", 11 },
- { "fragmentOpenExitAnimation", 11 },
- { "fragmentCloseEnterAnimation", 11 },
- { "fragmentCloseExitAnimation", 11 },
- { "fragmentFadeEnterAnimation", 11 },
- { "fragmentFadeExitAnimation", 11 },
- { "actionBarSize", 11 },
- { "imeSubtypeLocale", 11 },
- { "imeSubtypeMode", 11 },
- { "imeSubtypeExtraValue", 11 },
- { "splitMotionEvents", 11 },
- { "listChoiceBackgroundIndicator", 11 },
- { "spinnerMode", 11 },
- { "animateLayoutChanges", 11 },
- { "actionBarTabStyle", 11 },
- { "actionBarTabBarStyle", 11 },
- { "actionBarTabTextStyle", 11 },
- { "actionOverflowButtonStyle", 11 },
- { "actionModeCloseButtonStyle", 11 },
- { "titleTextStyle", 11 },
- { "subtitleTextStyle", 11 },
- { "iconifiedByDefault", 11 },
- { "actionLayout", 11 },
- { "actionViewClass", 11 },
- { "activatedBackgroundIndicator", 11 },
- { "state_activated", 11 },
- { "listPopupWindowStyle", 11 },
- { "popupMenuStyle", 11 },
- { "textAppearanceLargePopupMen", 11 },
- { "textAppearanceSmallPopupMen", 11 },
- { "breadCrumbTitle", 11 },
- { "breadCrumbShortTitle", 11 },
- { "listDividerAlertDialog", 11 },
- { "textColorAlertDialogListItem", 11 },
- { "loopViews", 11 },
- { "dialogTheme", 11 },
- { "alertDialogTheme", 11 },
- { "dividerVertical", 11 },
- { "homeAsUpIndicator", 11 },
- { "enterFadeDuration", 11 },
- { "exitFadeDuration", 11 },
- { "selectableItemBackground", 11 },
- { "autoAdvanceViewId", 11 },
- { "useIntrinsicSizeAsMinimum", 11 },
- { "actionModeCutDrawable", 11 },
- { "actionModeCopyDrawable", 11 },
- { "actionModePasteDrawable", 11 },
- { "textEditPasteWindowLayout", 11 },
- { "textEditNoPasteWindowLayout", 11 },
- { "textIsSelectable", 11 },
- { "windowEnableSplitTouch", 11 },
- { "indeterminateProgressStyle", 11 },
- { "progressBarPadding", 11 },
- { "animationResolution", 11 },
- { "state_accelerated", 11 },
- { "baseline", 11 },
- { "homeLayout", 11 },
- { "opacity", 11 },
- { "alpha", 11 },
- { "transformPivotX", 11 },
- { "transformPivotY", 11 },
- { "translationX", 11 },
- { "translationY", 11 },
- { "scaleX", 11 },
- { "scaleY", 11 },
- { "rotation", 11 },
- { "rotationX", 11 },
- { "rotationY", 11 },
- { "showDividers", 11 },
- { "dividerPadding", 11 },
- { "borderlessButtonStyle", 11 },
- { "dividerHorizontal", 11 },
- { "itemPadding", 11 },
- { "buttonBarStyle", 11 },
- { "buttonBarButtonStyle", 11 },
- { "segmentedButtonStyle", 11 },
- { "staticWallpaperPreview", 11 },
- { "allowParallelSyncs", 11 },
- { "isAlwaysSyncable", 11 },
- { "verticalScrollbarPosition", 11 },
- { "fastScrollAlwaysVisible", 11 },
- { "fastScrollThumbDrawable", 11 },
- { "fastScrollPreviewBackgroundLeft", 11 },
- { "fastScrollPreviewBackgroundRight", 11 },
- { "fastScrollTrackDrawable", 11 },
- { "fastScrollOverlayPosition", 11 },
- { "customTokens", 11 },
- { "nextFocusForward", 11 },
- { "firstDayOfWeek", 11 },
- { "showWeekNumber", 11 },
- { "minDate", 11 },
- { "maxDate", 11 },
- { "shownWeekCount", 11 },
- { "selectedWeekBackgroundColor", 11 },
- { "focusedMonthDateColor", 11 },
- { "unfocusedMonthDateColor", 11 },
- { "weekNumberColor", 11 },
- { "weekSeparatorLineColor", 11 },
- { "selectedDateVerticalBar", 11 },
- { "weekDayTextAppearance", 11 },
- { "dateTextAppearance", 11 },
- { "solidColor", 11 },
- { "spinnersShown", 11 },
- { "calendarViewShown", 11 },
- { "state_multiline", 11 },
- { "detailsElementBackground", 11 },
- { "textColorHighlightInverse", 11 },
- { "textColorLinkInverse", 11 },
- { "editTextColor", 11 },
- { "editTextBackground", 11 },
- { "horizontalScrollViewStyle", 11 },
- { "layerType", 11 },
- { "alertDialogIcon", 11 },
- { "windowMinWidthMajor", 11 },
- { "windowMinWidthMinor", 11 },
- { "queryHint", 11 },
- { "fastScrollTextColor", 11 },
- { "largeHeap", 11 },
- { "windowCloseOnTouchOutside", 11 },
- { "datePickerStyle", 11 },
- { "calendarViewStyle", 11 },
- { "textEditSidePasteWindowLayout", 11 },
- { "textEditSideNoPasteWindowLayout", 11 },
- { "actionMenuTextAppearance", 11 },
- { "actionMenuTextColor", 11 },
- { "textCursorDrawable", 12 },
- { "resizeMode", 12 },
- { "requiresSmallestWidthDp", 12 },
- { "compatibleWidthLimitDp", 12 },
- { "largestWidthLimitDp", 12 },
- { "state_hovered", 13 },
- { "state_drag_can_accept", 13 },
- { "state_drag_hovered", 13 },
- { "stopWithTask", 13 },
- { "switchTextOn", 13 },
- { "switchTextOff", 13 },
- { "switchPreferenceStyle", 13 },
- { "switchTextAppearance", 13 },
- { "track", 13 },
- { "switchMinWidth", 13 },
- { "switchPadding", 13 },
- { "thumbTextPadding", 13 },
- { "textSuggestionsWindowStyle", 13 },
- { "textEditSuggestionItemLayout", 13 },
- { "rowCount", 13 },
- { "rowOrderPreserved", 13 },
- { "columnCount", 13 },
- { "columnOrderPreserved", 13 },
- { "useDefaultMargins", 13 },
- { "alignmentMode", 13 },
- { "layout_row", 13 },
- { "layout_rowSpan", 13 },
- { "layout_columnSpan", 13 },
- { "actionModeSelectAllDrawable", 13 },
- { "isAuxiliary", 13 },
- { "accessibilityEventTypes", 13 },
- { "packageNames", 13 },
- { "accessibilityFeedbackType", 13 },
- { "notificationTimeout", 13 },
- { "accessibilityFlags", 13 },
- { "canRetrieveWindowContent", 13 },
- { "listPreferredItemHeightLarge", 13 },
- { "listPreferredItemHeightSmall", 13 },
- { "actionBarSplitStyle", 13 },
- { "actionProviderClass", 13 },
- { "backgroundStacked", 13 },
- { "backgroundSplit", 13 },
- { "textAllCaps", 13 },
- { "colorPressedHighlight", 13 },
- { "colorLongPressedHighlight", 13 },
- { "colorFocusedHighlight", 13 },
- { "colorActivatedHighlight", 13 },
- { "colorMultiSelectHighlight", 13 },
- { "drawableStart", 13 },
- { "drawableEnd", 13 },
- { "actionModeStyle", 13 },
- { "minResizeWidth", 13 },
- { "minResizeHeight", 13 },
- { "actionBarWidgetTheme", 13 },
- { "uiOptions", 13 },
- { "subtypeLocale", 13 },
- { "subtypeExtraValue", 13 },
- { "actionBarDivider", 13 },
- { "actionBarItemBackground", 13 },
- { "actionModeSplitBackground", 13 },
- { "textAppearanceListItem", 13 },
- { "textAppearanceListItemSmall", 13 },
- { "targetDescriptions", 13 },
- { "directionDescriptions", 13 },
- { "overridesImplicitlyEnabledSubtype", 13 },
- { "listPreferredItemPaddingLeft", 13 },
- { "listPreferredItemPaddingRight", 13 },
- { "requiresFadingEdge", 13 },
- { "publicKey", 13 },
- { "parentActivityName", 16 },
- { "isolatedProcess", 16 },
- { "importantForAccessibility", 16 },
- { "keyboardLayout", 16 },
- { "fontFamily", 16 },
- { "mediaRouteButtonStyle", 16 },
- { "mediaRouteTypes", 16 },
- { "supportsRtl", 17 },
- { "textDirection", 17 },
- { "textAlignment", 17 },
- { "layoutDirection", 17 },
- { "paddingStart", 17 },
- { "paddingEnd", 17 },
- { "layout_marginStart", 17 },
- { "layout_marginEnd", 17 },
- { "layout_toStartOf", 17 },
- { "layout_toEndOf", 17 },
- { "layout_alignStart", 17 },
- { "layout_alignEnd", 17 },
- { "layout_alignParentStart", 17 },
- { "layout_alignParentEnd", 17 },
- { "listPreferredItemPaddingStart", 17 },
- { "listPreferredItemPaddingEnd", 17 },
- { "singleUser", 17 },
- { "presentationTheme", 17 },
- { "subtypeId", 17 },
- { "initialKeyguardLayout", 17 },
- { "widgetCategory", 17 },
- { "permissionGroupFlags", 17 },
- { "labelFor", 17 },
- { "permissionFlags", 17 },
- { "checkedTextViewStyle", 17 },
- { "showOnLockScreen", 17 },
- { "format12Hour", 17 },
- { "format24Hour", 17 },
- { "timeZone", 17 },
- { "mipMap", 18 },
- { "mirrorForRtl", 18 },
- { "windowOverscan", 18 },
- { "requiredForAllUsers", 18 },
- { "indicatorStart", 18 },
- { "indicatorEnd", 18 },
- { "childIndicatorStart", 18 },
- { "childIndicatorEnd", 18 },
- { "restrictedAccountType", 18 },
- { "requiredAccountType", 18 },
- { "canRequestTouchExplorationMode", 18 },
- { "canRequestEnhancedWebAccessibility", 18 },
- { "canRequestFilterKeyEvents", 18 },
- { "layoutMode", 18 },
- { "keySet", 19 },
- { "targetId", 19 },
- { "fromScene", 19 },
- { "toScene", 19 },
- { "transition", 19 },
- { "transitionOrdering", 19 },
- { "fadingMode", 19 },
- { "startDelay", 19 },
- { "ssp", 19 },
- { "sspPrefix", 19 },
- { "sspPattern", 19 },
- { "addPrintersActivity", 19 },
- { "vendor", 19 },
- { "category", 19 },
- { "isAsciiCapable", 19 },
- { "autoMirrored", 19 },
- { "supportsSwitchingToNextInputMethod", 19 },
- { "requireDeviceUnlock", 19 },
- { "apduServiceBanner", 19 },
- { "accessibilityLiveRegion", 19 },
- { "windowTranslucentStatus", 19 },
- { "windowTranslucentNavigation", 19 },
- { "advancedPrintOptionsActivity", 19 },
- { "banner", 20 },
- { "windowSwipeToDismiss", 20 },
- { "isGame", 20 },
- { "allowEmbedded", 20 },
- { "setupActivity", 20 },
- { "fastScrollStyle", 21 },
- { "windowContentTransitions", 21 },
- { "windowContentTransitionManager", 21 },
- { "translationZ", 21 },
- { "tintMode", 21 },
- { "controlX1", 21 },
- { "controlY1", 21 },
- { "controlX2", 21 },
- { "controlY2", 21 },
- { "transitionName", 21 },
- { "transitionGroup", 21 },
- { "viewportWidth", 21 },
- { "viewportHeight", 21 },
- { "fillColor", 21 },
- { "pathData", 21 },
- { "strokeColor", 21 },
- { "strokeWidth", 21 },
- { "trimPathStart", 21 },
- { "trimPathEnd", 21 },
- { "trimPathOffset", 21 },
- { "strokeLineCap", 21 },
- { "strokeLineJoin", 21 },
- { "strokeMiterLimit", 21 },
- { "colorControlNormal", 21 },
- { "colorControlActivated", 21 },
- { "colorButtonNormal", 21 },
- { "colorControlHighlight", 21 },
- { "persistableMode", 21 },
- { "titleTextAppearance", 21 },
- { "subtitleTextAppearance", 21 },
- { "slideEdge", 21 },
- { "actionBarTheme", 21 },
- { "textAppearanceListItemSecondary", 21 },
- { "colorPrimary", 21 },
- { "colorPrimaryDark", 21 },
- { "colorAccent", 21 },
- { "nestedScrollingEnabled", 21 },
- { "windowEnterTransition", 21 },
- { "windowExitTransition", 21 },
- { "windowSharedElementEnterTransition", 21 },
- { "windowSharedElementExitTransition", 21 },
- { "windowAllowReturnTransitionOverlap", 21 },
- { "windowAllowEnterTransitionOverlap", 21 },
- { "sessionService", 21 },
- { "stackViewStyle", 21 },
- { "switchStyle", 21 },
- { "elevation", 21 },
- { "excludeId", 21 },
- { "excludeClass", 21 },
- { "hideOnContentScroll", 21 },
- { "actionOverflowMenuStyle", 21 },
- { "documentLaunchMode", 21 },
- { "maxRecents", 21 },
- { "autoRemoveFromRecents", 21 },
- { "stateListAnimator", 21 },
- { "toId", 21 },
- { "fromId", 21 },
- { "reversible", 21 },
- { "splitTrack", 21 },
- { "targetName", 21 },
- { "excludeName", 21 },
- { "matchOrder", 21 },
- { "windowDrawsSystemBarBackgrounds", 21 },
- { "statusBarColor", 21 },
- { "navigationBarColor", 21 },
- { "contentInsetStart", 21 },
- { "contentInsetEnd", 21 },
- { "contentInsetLeft", 21 },
- { "contentInsetRight", 21 },
- { "paddingMode", 21 },
- { "layout_rowWeight", 21 },
- { "layout_columnWeight", 21 },
- { "translateX", 21 },
- { "translateY", 21 },
- { "selectableItemBackgroundBorderless", 21 },
- { "elegantTextHeight", 21 },
- { "searchKeyphraseId", 21 },
- { "searchKeyphrase", 21 },
- { "searchKeyphraseSupportedLocales", 21 },
- { "windowTransitionBackgroundFadeDuration", 21 },
- { "overlapAnchor", 21 },
- { "progressTint", 21 },
- { "progressTintMode", 21 },
- { "progressBackgroundTint", 21 },
- { "progressBackgroundTintMode", 21 },
- { "secondaryProgressTint", 21 },
- { "secondaryProgressTintMode", 21 },
- { "indeterminateTint", 21 },
- { "indeterminateTintMode", 21 },
- { "backgroundTint", 21 },
- { "backgroundTintMode", 21 },
- { "foregroundTint", 21 },
- { "foregroundTintMode", 21 },
- { "buttonTint", 21 },
- { "buttonTintMode", 21 },
- { "thumbTint", 21 },
- { "thumbTintMode", 21 },
- { "fullBackupOnly", 21 },
- { "propertyXName", 21 },
- { "propertyYName", 21 },
- { "relinquishTaskIdentity", 21 },
- { "tileModeX", 21 },
- { "tileModeY", 21 },
- { "actionModeShareDrawable", 21 },
- { "actionModeFindDrawable", 21 },
- { "actionModeWebSearchDrawable", 21 },
- { "transitionVisibilityMode", 21 },
- { "minimumHorizontalAngle", 21 },
- { "minimumVerticalAngle", 21 },
- { "maximumAngle", 21 },
- { "searchViewStyle", 21 },
- { "closeIcon", 21 },
- { "goIcon", 21 },
- { "searchIcon", 21 },
- { "voiceIcon", 21 },
- { "commitIcon", 21 },
- { "suggestionRowLayout", 21 },
- { "queryBackground", 21 },
- { "submitBackground", 21 },
- { "buttonBarPositiveButtonStyle", 21 },
- { "buttonBarNeutralButtonStyle", 21 },
- { "buttonBarNegativeButtonStyle", 21 },
- { "popupElevation", 21 },
- { "actionBarPopupTheme", 21 },
- { "multiArch", 21 },
- { "touchscreenBlocksFocus", 21 },
- { "windowElevation", 21 },
- { "launchTaskBehindTargetAnimation", 21 },
- { "launchTaskBehindSourceAnimation", 21 },
- { "restrictionType", 21 },
- { "dayOfWeekBackground", 21 },
- { "dayOfWeekTextAppearance", 21 },
- { "headerMonthTextAppearance", 21 },
- { "headerDayOfMonthTextAppearance", 21 },
- { "headerYearTextAppearance", 21 },
- { "yearListItemTextAppearance", 21 },
- { "yearListSelectorColor", 21 },
- { "calendarTextColor", 21 },
- { "recognitionService", 21 },
- { "timePickerStyle", 21 },
- { "timePickerDialogTheme", 21 },
- { "headerTimeTextAppearance", 21 },
- { "headerAmPmTextAppearance", 21 },
- { "numbersTextColor", 21 },
- { "numbersBackgroundColor", 21 },
- { "numbersSelectorColor", 21 },
- { "amPmTextColor", 21 },
- { "amPmBackgroundColor", 21 },
- { "searchKeyphraseRecognitionFlags", 21 },
- { "checkMarkTint", 21 },
- { "checkMarkTintMode", 21 },
- { "popupTheme", 21 },
- { "toolbarStyle", 21 },
- { "windowClipToOutline", 21 },
- { "datePickerDialogTheme", 21 },
- { "showText", 21 },
- { "windowReturnTransition", 21 },
- { "windowReenterTransition", 21 },
- { "windowSharedElementReturnTransition", 21 },
- { "windowSharedElementReenterTransition", 21 },
- { "resumeWhilePausing", 21 },
- { "datePickerMode", 21 },
- { "timePickerMode", 21 },
- { "inset", 21 },
- { "letterSpacing", 21 },
- { "fontFeatureSettings", 21 },
- { "outlineProvider", 21 },
- { "contentAgeHint", 21 },
- { "country", 21 },
- { "windowSharedElementsUseOverlay", 21 },
- { "reparent", 21 },
- { "reparentWithOverlay", 21 },
- { "ambientShadowAlpha", 21 },
- { "spotShadowAlpha", 21 },
- { "navigationIcon", 21 },
- { "navigationContentDescription", 21 },
- { "fragmentExitTransition", 21 },
- { "fragmentEnterTransition", 21 },
- { "fragmentSharedElementEnterTransition", 21 },
- { "fragmentReturnTransition", 21 },
- { "fragmentSharedElementReturnTransition", 21 },
- { "fragmentReenterTransition", 21 },
- { "fragmentAllowEnterTransitionOverlap", 21 },
- { "fragmentAllowReturnTransitionOverlap", 21 },
- { "patternPathData", 21 },
- { "strokeAlpha", 21 },
- { "fillAlpha", 21 },
- { "windowActivityTransitions", 21 },
- { "colorEdgeEffect", 21 }
-};
+ {"marqueeRepeatLimit", 2},
+ {"windowNoDisplay", 3},
+ {"backgroundDimEnabled", 3},
+ {"inputType", 3},
+ {"isDefault", 3},
+ {"windowDisablePreview", 3},
+ {"privateImeOptions", 3},
+ {"editorExtras", 3},
+ {"settingsActivity", 3},
+ {"fastScrollEnabled", 3},
+ {"reqTouchScreen", 3},
+ {"reqKeyboardType", 3},
+ {"reqHardKeyboard", 3},
+ {"reqNavigation", 3},
+ {"windowSoftInputMode", 3},
+ {"imeFullscreenBackground", 3},
+ {"noHistory", 3},
+ {"headerDividersEnabled", 3},
+ {"footerDividersEnabled", 3},
+ {"candidatesTextStyleSpans", 3},
+ {"smoothScrollbar", 3},
+ {"reqFiveWayNav", 3},
+ {"keyBackground", 3},
+ {"keyTextSize", 3},
+ {"labelTextSize", 3},
+ {"keyTextColor", 3},
+ {"keyPreviewLayout", 3},
+ {"keyPreviewOffset", 3},
+ {"keyPreviewHeight", 3},
+ {"verticalCorrection", 3},
+ {"popupLayout", 3},
+ {"state_long_pressable", 3},
+ {"keyWidth", 3},
+ {"keyHeight", 3},
+ {"horizontalGap", 3},
+ {"verticalGap", 3},
+ {"rowEdgeFlags", 3},
+ {"codes", 3},
+ {"popupKeyboard", 3},
+ {"popupCharacters", 3},
+ {"keyEdgeFlags", 3},
+ {"isModifier", 3},
+ {"isSticky", 3},
+ {"isRepeatable", 3},
+ {"iconPreview", 3},
+ {"keyOutputText", 3},
+ {"keyLabel", 3},
+ {"keyIcon", 3},
+ {"keyboardMode", 3},
+ {"isScrollContainer", 3},
+ {"fillEnabled", 3},
+ {"updatePeriodMillis", 3},
+ {"initialLayout", 3},
+ {"voiceSearchMode", 3},
+ {"voiceLanguageModel", 3},
+ {"voicePromptText", 3},
+ {"voiceLanguage", 3},
+ {"voiceMaxResults", 3},
+ {"bottomOffset", 3},
+ {"topOffset", 3},
+ {"allowSingleTap", 3},
+ {"handle", 3},
+ {"content", 3},
+ {"animateOnClick", 3},
+ {"configure", 3},
+ {"hapticFeedbackEnabled", 3},
+ {"innerRadius", 3},
+ {"thickness", 3},
+ {"sharedUserLabel", 3},
+ {"dropDownWidth", 3},
+ {"dropDownAnchor", 3},
+ {"imeOptions", 3},
+ {"imeActionLabel", 3},
+ {"imeActionId", 3},
+ {"imeExtractEnterAnimation", 3},
+ {"imeExtractExitAnimation", 3},
+ {"tension", 4},
+ {"extraTension", 4},
+ {"anyDensity", 4},
+ {"searchSuggestThreshold", 4},
+ {"includeInGlobalSearch", 4},
+ {"onClick", 4},
+ {"targetSdkVersion", 4},
+ {"maxSdkVersion", 4},
+ {"testOnly", 4},
+ {"contentDescription", 4},
+ {"gestureStrokeWidth", 4},
+ {"gestureColor", 4},
+ {"uncertainGestureColor", 4},
+ {"fadeOffset", 4},
+ {"fadeDuration", 4},
+ {"gestureStrokeType", 4},
+ {"gestureStrokeLengthThreshold", 4},
+ {"gestureStrokeSquarenessThreshold", 4},
+ {"gestureStrokeAngleThreshold", 4},
+ {"eventsInterceptionEnabled", 4},
+ {"fadeEnabled", 4},
+ {"backupAgent", 4},
+ {"allowBackup", 4},
+ {"glEsVersion", 4},
+ {"queryAfterZeroResults", 4},
+ {"dropDownHeight", 4},
+ {"smallScreens", 4},
+ {"normalScreens", 4},
+ {"largeScreens", 4},
+ {"progressBarStyleInverse", 4},
+ {"progressBarStyleSmallInverse", 4},
+ {"progressBarStyleLargeInverse", 4},
+ {"searchSettingsDescription", 4},
+ {"textColorPrimaryInverseDisableOnly", 4},
+ {"autoUrlDetect", 4},
+ {"resizeable", 4},
+ {"required", 5},
+ {"accountType", 5},
+ {"contentAuthority", 5},
+ {"userVisible", 5},
+ {"windowShowWallpaper", 5},
+ {"wallpaperOpenEnterAnimation", 5},
+ {"wallpaperOpenExitAnimation", 5},
+ {"wallpaperCloseEnterAnimation", 5},
+ {"wallpaperCloseExitAnimation", 5},
+ {"wallpaperIntraOpenEnterAnimation", 5},
+ {"wallpaperIntraOpenExitAnimation", 5},
+ {"wallpaperIntraCloseEnterAnimation", 5},
+ {"wallpaperIntraCloseExitAnimation", 5},
+ {"supportsUploading", 5},
+ {"killAfterRestore", 5},
+ {"restoreNeedsApplication", 5},
+ {"smallIcon", 5},
+ {"accountPreferences", 5},
+ {"textAppearanceSearchResultSubtitle", 5},
+ {"textAppearanceSearchResultTitle", 5},
+ {"summaryColumn", 5},
+ {"detailColumn", 5},
+ {"detailSocialSummary", 5},
+ {"thumbnail", 5},
+ {"detachWallpaper", 5},
+ {"finishOnCloseSystemDialogs", 5},
+ {"scrollbarFadeDuration", 5},
+ {"scrollbarDefaultDelayBeforeFade", 5},
+ {"fadeScrollbars", 5},
+ {"colorBackgroundCacheHint", 5},
+ {"dropDownHorizontalOffset", 5},
+ {"dropDownVerticalOffset", 5},
+ {"quickContactBadgeStyleWindowSmall", 6},
+ {"quickContactBadgeStyleWindowMedium", 6},
+ {"quickContactBadgeStyleWindowLarge", 6},
+ {"quickContactBadgeStyleSmallWindowSmall", 6},
+ {"quickContactBadgeStyleSmallWindowMedium", 6},
+ {"quickContactBadgeStyleSmallWindowLarge", 6},
+ {"author", 7},
+ {"autoStart", 7},
+ {"expandableListViewWhiteStyle", 8},
+ {"installLocation", 8},
+ {"vmSafeMode", 8},
+ {"webTextViewStyle", 8},
+ {"restoreAnyVersion", 8},
+ {"tabStripLeft", 8},
+ {"tabStripRight", 8},
+ {"tabStripEnabled", 8},
+ {"logo", 9},
+ {"xlargeScreens", 9},
+ {"immersive", 9},
+ {"overScrollMode", 9},
+ {"overScrollHeader", 9},
+ {"overScrollFooter", 9},
+ {"filterTouchesWhenObscured", 9},
+ {"textSelectHandleLeft", 9},
+ {"textSelectHandleRight", 9},
+ {"textSelectHandle", 9},
+ {"textSelectHandleWindowStyle", 9},
+ {"popupAnimationStyle", 9},
+ {"screenSize", 9},
+ {"screenDensity", 9},
+ {"allContactsName", 11},
+ {"windowActionBar", 11},
+ {"actionBarStyle", 11},
+ {"navigationMode", 11},
+ {"displayOptions", 11},
+ {"subtitle", 11},
+ {"customNavigationLayout", 11},
+ {"hardwareAccelerated", 11},
+ {"measureWithLargestChild", 11},
+ {"animateFirstView", 11},
+ {"dropDownSpinnerStyle", 11},
+ {"actionDropDownStyle", 11},
+ {"actionButtonStyle", 11},
+ {"showAsAction", 11},
+ {"previewImage", 11},
+ {"actionModeBackground", 11},
+ {"actionModeCloseDrawable", 11},
+ {"windowActionModeOverlay", 11},
+ {"valueFrom", 11},
+ {"valueTo", 11},
+ {"valueType", 11},
+ {"propertyName", 11},
+ {"ordering", 11},
+ {"fragment", 11},
+ {"windowActionBarOverlay", 11},
+ {"fragmentOpenEnterAnimation", 11},
+ {"fragmentOpenExitAnimation", 11},
+ {"fragmentCloseEnterAnimation", 11},
+ {"fragmentCloseExitAnimation", 11},
+ {"fragmentFadeEnterAnimation", 11},
+ {"fragmentFadeExitAnimation", 11},
+ {"actionBarSize", 11},
+ {"imeSubtypeLocale", 11},
+ {"imeSubtypeMode", 11},
+ {"imeSubtypeExtraValue", 11},
+ {"splitMotionEvents", 11},
+ {"listChoiceBackgroundIndicator", 11},
+ {"spinnerMode", 11},
+ {"animateLayoutChanges", 11},
+ {"actionBarTabStyle", 11},
+ {"actionBarTabBarStyle", 11},
+ {"actionBarTabTextStyle", 11},
+ {"actionOverflowButtonStyle", 11},
+ {"actionModeCloseButtonStyle", 11},
+ {"titleTextStyle", 11},
+ {"subtitleTextStyle", 11},
+ {"iconifiedByDefault", 11},
+ {"actionLayout", 11},
+ {"actionViewClass", 11},
+ {"activatedBackgroundIndicator", 11},
+ {"state_activated", 11},
+ {"listPopupWindowStyle", 11},
+ {"popupMenuStyle", 11},
+ {"textAppearanceLargePopupMen", 11},
+ {"textAppearanceSmallPopupMen", 11},
+ {"breadCrumbTitle", 11},
+ {"breadCrumbShortTitle", 11},
+ {"listDividerAlertDialog", 11},
+ {"textColorAlertDialogListItem", 11},
+ {"loopViews", 11},
+ {"dialogTheme", 11},
+ {"alertDialogTheme", 11},
+ {"dividerVertical", 11},
+ {"homeAsUpIndicator", 11},
+ {"enterFadeDuration", 11},
+ {"exitFadeDuration", 11},
+ {"selectableItemBackground", 11},
+ {"autoAdvanceViewId", 11},
+ {"useIntrinsicSizeAsMinimum", 11},
+ {"actionModeCutDrawable", 11},
+ {"actionModeCopyDrawable", 11},
+ {"actionModePasteDrawable", 11},
+ {"textEditPasteWindowLayout", 11},
+ {"textEditNoPasteWindowLayout", 11},
+ {"textIsSelectable", 11},
+ {"windowEnableSplitTouch", 11},
+ {"indeterminateProgressStyle", 11},
+ {"progressBarPadding", 11},
+ {"animationResolution", 11},
+ {"state_accelerated", 11},
+ {"baseline", 11},
+ {"homeLayout", 11},
+ {"opacity", 11},
+ {"alpha", 11},
+ {"transformPivotX", 11},
+ {"transformPivotY", 11},
+ {"translationX", 11},
+ {"translationY", 11},
+ {"scaleX", 11},
+ {"scaleY", 11},
+ {"rotation", 11},
+ {"rotationX", 11},
+ {"rotationY", 11},
+ {"showDividers", 11},
+ {"dividerPadding", 11},
+ {"borderlessButtonStyle", 11},
+ {"dividerHorizontal", 11},
+ {"itemPadding", 11},
+ {"buttonBarStyle", 11},
+ {"buttonBarButtonStyle", 11},
+ {"segmentedButtonStyle", 11},
+ {"staticWallpaperPreview", 11},
+ {"allowParallelSyncs", 11},
+ {"isAlwaysSyncable", 11},
+ {"verticalScrollbarPosition", 11},
+ {"fastScrollAlwaysVisible", 11},
+ {"fastScrollThumbDrawable", 11},
+ {"fastScrollPreviewBackgroundLeft", 11},
+ {"fastScrollPreviewBackgroundRight", 11},
+ {"fastScrollTrackDrawable", 11},
+ {"fastScrollOverlayPosition", 11},
+ {"customTokens", 11},
+ {"nextFocusForward", 11},
+ {"firstDayOfWeek", 11},
+ {"showWeekNumber", 11},
+ {"minDate", 11},
+ {"maxDate", 11},
+ {"shownWeekCount", 11},
+ {"selectedWeekBackgroundColor", 11},
+ {"focusedMonthDateColor", 11},
+ {"unfocusedMonthDateColor", 11},
+ {"weekNumberColor", 11},
+ {"weekSeparatorLineColor", 11},
+ {"selectedDateVerticalBar", 11},
+ {"weekDayTextAppearance", 11},
+ {"dateTextAppearance", 11},
+ {"solidColor", 11},
+ {"spinnersShown", 11},
+ {"calendarViewShown", 11},
+ {"state_multiline", 11},
+ {"detailsElementBackground", 11},
+ {"textColorHighlightInverse", 11},
+ {"textColorLinkInverse", 11},
+ {"editTextColor", 11},
+ {"editTextBackground", 11},
+ {"horizontalScrollViewStyle", 11},
+ {"layerType", 11},
+ {"alertDialogIcon", 11},
+ {"windowMinWidthMajor", 11},
+ {"windowMinWidthMinor", 11},
+ {"queryHint", 11},
+ {"fastScrollTextColor", 11},
+ {"largeHeap", 11},
+ {"windowCloseOnTouchOutside", 11},
+ {"datePickerStyle", 11},
+ {"calendarViewStyle", 11},
+ {"textEditSidePasteWindowLayout", 11},
+ {"textEditSideNoPasteWindowLayout", 11},
+ {"actionMenuTextAppearance", 11},
+ {"actionMenuTextColor", 11},
+ {"textCursorDrawable", 12},
+ {"resizeMode", 12},
+ {"requiresSmallestWidthDp", 12},
+ {"compatibleWidthLimitDp", 12},
+ {"largestWidthLimitDp", 12},
+ {"state_hovered", 13},
+ {"state_drag_can_accept", 13},
+ {"state_drag_hovered", 13},
+ {"stopWithTask", 13},
+ {"switchTextOn", 13},
+ {"switchTextOff", 13},
+ {"switchPreferenceStyle", 13},
+ {"switchTextAppearance", 13},
+ {"track", 13},
+ {"switchMinWidth", 13},
+ {"switchPadding", 13},
+ {"thumbTextPadding", 13},
+ {"textSuggestionsWindowStyle", 13},
+ {"textEditSuggestionItemLayout", 13},
+ {"rowCount", 13},
+ {"rowOrderPreserved", 13},
+ {"columnCount", 13},
+ {"columnOrderPreserved", 13},
+ {"useDefaultMargins", 13},
+ {"alignmentMode", 13},
+ {"layout_row", 13},
+ {"layout_rowSpan", 13},
+ {"layout_columnSpan", 13},
+ {"actionModeSelectAllDrawable", 13},
+ {"isAuxiliary", 13},
+ {"accessibilityEventTypes", 13},
+ {"packageNames", 13},
+ {"accessibilityFeedbackType", 13},
+ {"notificationTimeout", 13},
+ {"accessibilityFlags", 13},
+ {"canRetrieveWindowContent", 13},
+ {"listPreferredItemHeightLarge", 13},
+ {"listPreferredItemHeightSmall", 13},
+ {"actionBarSplitStyle", 13},
+ {"actionProviderClass", 13},
+ {"backgroundStacked", 13},
+ {"backgroundSplit", 13},
+ {"textAllCaps", 13},
+ {"colorPressedHighlight", 13},
+ {"colorLongPressedHighlight", 13},
+ {"colorFocusedHighlight", 13},
+ {"colorActivatedHighlight", 13},
+ {"colorMultiSelectHighlight", 13},
+ {"drawableStart", 13},
+ {"drawableEnd", 13},
+ {"actionModeStyle", 13},
+ {"minResizeWidth", 13},
+ {"minResizeHeight", 13},
+ {"actionBarWidgetTheme", 13},
+ {"uiOptions", 13},
+ {"subtypeLocale", 13},
+ {"subtypeExtraValue", 13},
+ {"actionBarDivider", 13},
+ {"actionBarItemBackground", 13},
+ {"actionModeSplitBackground", 13},
+ {"textAppearanceListItem", 13},
+ {"textAppearanceListItemSmall", 13},
+ {"targetDescriptions", 13},
+ {"directionDescriptions", 13},
+ {"overridesImplicitlyEnabledSubtype", 13},
+ {"listPreferredItemPaddingLeft", 13},
+ {"listPreferredItemPaddingRight", 13},
+ {"requiresFadingEdge", 13},
+ {"publicKey", 13},
+ {"parentActivityName", 16},
+ {"isolatedProcess", 16},
+ {"importantForAccessibility", 16},
+ {"keyboardLayout", 16},
+ {"fontFamily", 16},
+ {"mediaRouteButtonStyle", 16},
+ {"mediaRouteTypes", 16},
+ {"supportsRtl", 17},
+ {"textDirection", 17},
+ {"textAlignment", 17},
+ {"layoutDirection", 17},
+ {"paddingStart", 17},
+ {"paddingEnd", 17},
+ {"layout_marginStart", 17},
+ {"layout_marginEnd", 17},
+ {"layout_toStartOf", 17},
+ {"layout_toEndOf", 17},
+ {"layout_alignStart", 17},
+ {"layout_alignEnd", 17},
+ {"layout_alignParentStart", 17},
+ {"layout_alignParentEnd", 17},
+ {"listPreferredItemPaddingStart", 17},
+ {"listPreferredItemPaddingEnd", 17},
+ {"singleUser", 17},
+ {"presentationTheme", 17},
+ {"subtypeId", 17},
+ {"initialKeyguardLayout", 17},
+ {"widgetCategory", 17},
+ {"permissionGroupFlags", 17},
+ {"labelFor", 17},
+ {"permissionFlags", 17},
+ {"checkedTextViewStyle", 17},
+ {"showOnLockScreen", 17},
+ {"format12Hour", 17},
+ {"format24Hour", 17},
+ {"timeZone", 17},
+ {"mipMap", 18},
+ {"mirrorForRtl", 18},
+ {"windowOverscan", 18},
+ {"requiredForAllUsers", 18},
+ {"indicatorStart", 18},
+ {"indicatorEnd", 18},
+ {"childIndicatorStart", 18},
+ {"childIndicatorEnd", 18},
+ {"restrictedAccountType", 18},
+ {"requiredAccountType", 18},
+ {"canRequestTouchExplorationMode", 18},
+ {"canRequestEnhancedWebAccessibility", 18},
+ {"canRequestFilterKeyEvents", 18},
+ {"layoutMode", 18},
+ {"keySet", 19},
+ {"targetId", 19},
+ {"fromScene", 19},
+ {"toScene", 19},
+ {"transition", 19},
+ {"transitionOrdering", 19},
+ {"fadingMode", 19},
+ {"startDelay", 19},
+ {"ssp", 19},
+ {"sspPrefix", 19},
+ {"sspPattern", 19},
+ {"addPrintersActivity", 19},
+ {"vendor", 19},
+ {"category", 19},
+ {"isAsciiCapable", 19},
+ {"autoMirrored", 19},
+ {"supportsSwitchingToNextInputMethod", 19},
+ {"requireDeviceUnlock", 19},
+ {"apduServiceBanner", 19},
+ {"accessibilityLiveRegion", 19},
+ {"windowTranslucentStatus", 19},
+ {"windowTranslucentNavigation", 19},
+ {"advancedPrintOptionsActivity", 19},
+ {"banner", 20},
+ {"windowSwipeToDismiss", 20},
+ {"isGame", 20},
+ {"allowEmbedded", 20},
+ {"setupActivity", 20},
+ {"fastScrollStyle", 21},
+ {"windowContentTransitions", 21},
+ {"windowContentTransitionManager", 21},
+ {"translationZ", 21},
+ {"tintMode", 21},
+ {"controlX1", 21},
+ {"controlY1", 21},
+ {"controlX2", 21},
+ {"controlY2", 21},
+ {"transitionName", 21},
+ {"transitionGroup", 21},
+ {"viewportWidth", 21},
+ {"viewportHeight", 21},
+ {"fillColor", 21},
+ {"pathData", 21},
+ {"strokeColor", 21},
+ {"strokeWidth", 21},
+ {"trimPathStart", 21},
+ {"trimPathEnd", 21},
+ {"trimPathOffset", 21},
+ {"strokeLineCap", 21},
+ {"strokeLineJoin", 21},
+ {"strokeMiterLimit", 21},
+ {"colorControlNormal", 21},
+ {"colorControlActivated", 21},
+ {"colorButtonNormal", 21},
+ {"colorControlHighlight", 21},
+ {"persistableMode", 21},
+ {"titleTextAppearance", 21},
+ {"subtitleTextAppearance", 21},
+ {"slideEdge", 21},
+ {"actionBarTheme", 21},
+ {"textAppearanceListItemSecondary", 21},
+ {"colorPrimary", 21},
+ {"colorPrimaryDark", 21},
+ {"colorAccent", 21},
+ {"nestedScrollingEnabled", 21},
+ {"windowEnterTransition", 21},
+ {"windowExitTransition", 21},
+ {"windowSharedElementEnterTransition", 21},
+ {"windowSharedElementExitTransition", 21},
+ {"windowAllowReturnTransitionOverlap", 21},
+ {"windowAllowEnterTransitionOverlap", 21},
+ {"sessionService", 21},
+ {"stackViewStyle", 21},
+ {"switchStyle", 21},
+ {"elevation", 21},
+ {"excludeId", 21},
+ {"excludeClass", 21},
+ {"hideOnContentScroll", 21},
+ {"actionOverflowMenuStyle", 21},
+ {"documentLaunchMode", 21},
+ {"maxRecents", 21},
+ {"autoRemoveFromRecents", 21},
+ {"stateListAnimator", 21},
+ {"toId", 21},
+ {"fromId", 21},
+ {"reversible", 21},
+ {"splitTrack", 21},
+ {"targetName", 21},
+ {"excludeName", 21},
+ {"matchOrder", 21},
+ {"windowDrawsSystemBarBackgrounds", 21},
+ {"statusBarColor", 21},
+ {"navigationBarColor", 21},
+ {"contentInsetStart", 21},
+ {"contentInsetEnd", 21},
+ {"contentInsetLeft", 21},
+ {"contentInsetRight", 21},
+ {"paddingMode", 21},
+ {"layout_rowWeight", 21},
+ {"layout_columnWeight", 21},
+ {"translateX", 21},
+ {"translateY", 21},
+ {"selectableItemBackgroundBorderless", 21},
+ {"elegantTextHeight", 21},
+ {"searchKeyphraseId", 21},
+ {"searchKeyphrase", 21},
+ {"searchKeyphraseSupportedLocales", 21},
+ {"windowTransitionBackgroundFadeDuration", 21},
+ {"overlapAnchor", 21},
+ {"progressTint", 21},
+ {"progressTintMode", 21},
+ {"progressBackgroundTint", 21},
+ {"progressBackgroundTintMode", 21},
+ {"secondaryProgressTint", 21},
+ {"secondaryProgressTintMode", 21},
+ {"indeterminateTint", 21},
+ {"indeterminateTintMode", 21},
+ {"backgroundTint", 21},
+ {"backgroundTintMode", 21},
+ {"foregroundTint", 21},
+ {"foregroundTintMode", 21},
+ {"buttonTint", 21},
+ {"buttonTintMode", 21},
+ {"thumbTint", 21},
+ {"thumbTintMode", 21},
+ {"fullBackupOnly", 21},
+ {"propertyXName", 21},
+ {"propertyYName", 21},
+ {"relinquishTaskIdentity", 21},
+ {"tileModeX", 21},
+ {"tileModeY", 21},
+ {"actionModeShareDrawable", 21},
+ {"actionModeFindDrawable", 21},
+ {"actionModeWebSearchDrawable", 21},
+ {"transitionVisibilityMode", 21},
+ {"minimumHorizontalAngle", 21},
+ {"minimumVerticalAngle", 21},
+ {"maximumAngle", 21},
+ {"searchViewStyle", 21},
+ {"closeIcon", 21},
+ {"goIcon", 21},
+ {"searchIcon", 21},
+ {"voiceIcon", 21},
+ {"commitIcon", 21},
+ {"suggestionRowLayout", 21},
+ {"queryBackground", 21},
+ {"submitBackground", 21},
+ {"buttonBarPositiveButtonStyle", 21},
+ {"buttonBarNeutralButtonStyle", 21},
+ {"buttonBarNegativeButtonStyle", 21},
+ {"popupElevation", 21},
+ {"actionBarPopupTheme", 21},
+ {"multiArch", 21},
+ {"touchscreenBlocksFocus", 21},
+ {"windowElevation", 21},
+ {"launchTaskBehindTargetAnimation", 21},
+ {"launchTaskBehindSourceAnimation", 21},
+ {"restrictionType", 21},
+ {"dayOfWeekBackground", 21},
+ {"dayOfWeekTextAppearance", 21},
+ {"headerMonthTextAppearance", 21},
+ {"headerDayOfMonthTextAppearance", 21},
+ {"headerYearTextAppearance", 21},
+ {"yearListItemTextAppearance", 21},
+ {"yearListSelectorColor", 21},
+ {"calendarTextColor", 21},
+ {"recognitionService", 21},
+ {"timePickerStyle", 21},
+ {"timePickerDialogTheme", 21},
+ {"headerTimeTextAppearance", 21},
+ {"headerAmPmTextAppearance", 21},
+ {"numbersTextColor", 21},
+ {"numbersBackgroundColor", 21},
+ {"numbersSelectorColor", 21},
+ {"amPmTextColor", 21},
+ {"amPmBackgroundColor", 21},
+ {"searchKeyphraseRecognitionFlags", 21},
+ {"checkMarkTint", 21},
+ {"checkMarkTintMode", 21},
+ {"popupTheme", 21},
+ {"toolbarStyle", 21},
+ {"windowClipToOutline", 21},
+ {"datePickerDialogTheme", 21},
+ {"showText", 21},
+ {"windowReturnTransition", 21},
+ {"windowReenterTransition", 21},
+ {"windowSharedElementReturnTransition", 21},
+ {"windowSharedElementReenterTransition", 21},
+ {"resumeWhilePausing", 21},
+ {"datePickerMode", 21},
+ {"timePickerMode", 21},
+ {"inset", 21},
+ {"letterSpacing", 21},
+ {"fontFeatureSettings", 21},
+ {"outlineProvider", 21},
+ {"contentAgeHint", 21},
+ {"country", 21},
+ {"windowSharedElementsUseOverlay", 21},
+ {"reparent", 21},
+ {"reparentWithOverlay", 21},
+ {"ambientShadowAlpha", 21},
+ {"spotShadowAlpha", 21},
+ {"navigationIcon", 21},
+ {"navigationContentDescription", 21},
+ {"fragmentExitTransition", 21},
+ {"fragmentEnterTransition", 21},
+ {"fragmentSharedElementEnterTransition", 21},
+ {"fragmentReturnTransition", 21},
+ {"fragmentSharedElementReturnTransition", 21},
+ {"fragmentReenterTransition", 21},
+ {"fragmentAllowEnterTransitionOverlap", 21},
+ {"fragmentAllowReturnTransitionOverlap", 21},
+ {"patternPathData", 21},
+ {"strokeAlpha", 21},
+ {"fillAlpha", 21},
+ {"windowActivityTransitions", 21},
+ {"colorEdgeEffect", 21}};
-size_t findAttributeSdkLevel(const ResourceName& name) {
- if (name.package != "android" && name.type != ResourceType::kAttr) {
- return 0;
- }
+size_t FindAttributeSdkLevel(const ResourceName& name) {
+ if (name.package != "android" && name.type != ResourceType::kAttr) {
+ return 0;
+ }
- auto iter = sAttrMap.find(name.entry);
- if (iter != sAttrMap.end()) {
- return iter->second;
- }
- return SDK_LOLLIPOP_MR1;
+ auto iter = sAttrMap.find(name.entry);
+ if (iter != sAttrMap.end()) {
+ return iter->second;
+ }
+ return SDK_LOLLIPOP_MR1;
}
-std::pair<StringPiece, int> getDevelopmentSdkCodeNameAndVersion() {
- return std::make_pair(StringPiece(sDevelopmentSdkCodeName), sDevelopmentSdkLevel);
+std::pair<StringPiece, int> GetDevelopmentSdkCodeNameAndVersion() {
+ return std::make_pair(StringPiece(sDevelopmentSdkCodeName),
+ sDevelopmentSdkLevel);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index c9dbdca..9b38ecb 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -17,40 +17,40 @@
#ifndef AAPT_SDK_CONSTANTS_H
#define AAPT_SDK_CONSTANTS_H
-#include "Resource.h"
-
#include <utility>
+#include "Resource.h"
+
namespace aapt {
enum {
- SDK_CUPCAKE = 3,
- SDK_DONUT = 4,
- SDK_ECLAIR = 5,
- SDK_ECLAIR_0_1 = 6,
- SDK_ECLAIR_MR1 = 7,
- SDK_FROYO = 8,
- SDK_GINGERBREAD = 9,
- SDK_GINGERBREAD_MR1 = 10,
- SDK_HONEYCOMB = 11,
- SDK_HONEYCOMB_MR1 = 12,
- SDK_HONEYCOMB_MR2 = 13,
- SDK_ICE_CREAM_SANDWICH = 14,
- SDK_ICE_CREAM_SANDWICH_MR1 = 15,
- SDK_JELLY_BEAN = 16,
- SDK_JELLY_BEAN_MR1 = 17,
- SDK_JELLY_BEAN_MR2 = 18,
- SDK_KITKAT = 19,
- SDK_KITKAT_WATCH = 20,
- SDK_LOLLIPOP = 21,
- SDK_LOLLIPOP_MR1 = 22,
- SDK_MARSHMALLOW = 23,
+ SDK_CUPCAKE = 3,
+ SDK_DONUT = 4,
+ SDK_ECLAIR = 5,
+ SDK_ECLAIR_0_1 = 6,
+ SDK_ECLAIR_MR1 = 7,
+ SDK_FROYO = 8,
+ SDK_GINGERBREAD = 9,
+ SDK_GINGERBREAD_MR1 = 10,
+ SDK_HONEYCOMB = 11,
+ SDK_HONEYCOMB_MR1 = 12,
+ SDK_HONEYCOMB_MR2 = 13,
+ SDK_ICE_CREAM_SANDWICH = 14,
+ SDK_ICE_CREAM_SANDWICH_MR1 = 15,
+ SDK_JELLY_BEAN = 16,
+ SDK_JELLY_BEAN_MR1 = 17,
+ SDK_JELLY_BEAN_MR2 = 18,
+ SDK_KITKAT = 19,
+ SDK_KITKAT_WATCH = 20,
+ SDK_LOLLIPOP = 21,
+ SDK_LOLLIPOP_MR1 = 22,
+ SDK_MARSHMALLOW = 23,
};
-size_t findAttributeSdkLevel(const ResourceId& id);
-size_t findAttributeSdkLevel(const ResourceName& name);
-std::pair<StringPiece, int> getDevelopmentSdkCodeNameAndVersion();
+size_t FindAttributeSdkLevel(const ResourceId& id);
+size_t FindAttributeSdkLevel(const ResourceName& name);
+std::pair<StringPiece, int> GetDevelopmentSdkCodeNameAndVersion();
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_SDK_CONSTANTS_H
+#endif // AAPT_SDK_CONSTANTS_H
diff --git a/tools/aapt2/SdkConstants_test.cpp b/tools/aapt2/SdkConstants_test.cpp
index e81f412..716d922 100644
--- a/tools/aapt2/SdkConstants_test.cpp
+++ b/tools/aapt2/SdkConstants_test.cpp
@@ -16,23 +16,23 @@
#include "SdkConstants.h"
-#include <gtest/gtest.h>
+#include "gtest/gtest.h"
namespace aapt {
TEST(SdkConstantsTest, FirstAttributeIsSdk1) {
- EXPECT_EQ(1u, findAttributeSdkLevel(ResourceId(0x01010000)));
+ EXPECT_EQ(1u, FindAttributeSdkLevel(ResourceId(0x01010000)));
}
TEST(SdkConstantsTest, AllAttributesAfterLollipopAreLollipopMR1) {
- EXPECT_EQ(SDK_LOLLIPOP, findAttributeSdkLevel(ResourceId(0x010103f7)));
- EXPECT_EQ(SDK_LOLLIPOP, findAttributeSdkLevel(ResourceId(0x010104ce)));
+ EXPECT_EQ(SDK_LOLLIPOP, FindAttributeSdkLevel(ResourceId(0x010103f7)));
+ EXPECT_EQ(SDK_LOLLIPOP, FindAttributeSdkLevel(ResourceId(0x010104ce)));
- EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104cf)));
- EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104d8)));
+ EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104cf)));
+ EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104d8)));
- EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104d9)));
- EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x0101ffff)));
+ EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104d9)));
+ EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x0101ffff)));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
index 8a1021d..459a8e6 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -17,12 +17,12 @@
#ifndef AAPT_SOURCE_H
#define AAPT_SOURCE_H
-#include "util/Maybe.h"
-#include "util/StringPiece.h"
-
#include <ostream>
#include <string>
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+
namespace aapt {
/**
@@ -30,20 +30,19 @@
* showing errors.
*/
struct Source {
- std::string path;
- Maybe<size_t> line;
+ std::string path;
+ Maybe<size_t> line;
- Source() = default;
+ Source() = default;
- inline Source(const StringPiece& path) : path(path.toString()) { // NOLINT(implicit)
- }
+ inline Source(const StringPiece& path)
+ : path(path.ToString()) { // NOLINT(implicit)
+ }
- inline Source(const StringPiece& path, size_t line) : path(path.toString()), line(line) {
- }
+ inline Source(const StringPiece& path, size_t line)
+ : path(path.ToString()), line(line) {}
- inline Source withLine(size_t line) const {
- return Source(path, line);
- }
+ inline Source WithLine(size_t line) const { return Source(path, line); }
};
//
@@ -51,30 +50,30 @@
//
inline ::std::ostream& operator<<(::std::ostream& out, const Source& source) {
- out << source.path;
- if (source.line) {
- out << ":" << source.line.value();
- }
- return out;
+ out << source.path;
+ if (source.line) {
+ out << ":" << source.line.value();
+ }
+ return out;
}
inline bool operator==(const Source& lhs, const Source& rhs) {
- return lhs.path == rhs.path && lhs.line == rhs.line;
+ return lhs.path == rhs.path && lhs.line == rhs.line;
}
inline bool operator<(const Source& lhs, const Source& rhs) {
- int cmp = lhs.path.compare(rhs.path);
- if (cmp < 0) return true;
- if (cmp > 0) return false;
- if (lhs.line) {
- if (rhs.line) {
- return lhs.line.value() < rhs.line.value();
- }
- return false;
+ int cmp = lhs.path.compare(rhs.path);
+ if (cmp < 0) return true;
+ if (cmp > 0) return false;
+ if (lhs.line) {
+ if (rhs.line) {
+ return lhs.line.value() < rhs.line.value();
}
- return bool(rhs.line);
+ return false;
+ }
+ return bool(rhs.line);
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_SOURCE_H
+#endif // AAPT_SOURCE_H
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index fe4b967..3032829 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -15,399 +15,405 @@
*/
#include "StringPool.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "android-base/logging.h"
+#include "androidfw/ResourceTypes.h"
+
#include "util/BigBuffer.h"
#include "util/StringPiece.h"
#include "util/Util.h"
-#include <algorithm>
-#include <androidfw/ResourceTypes.h>
-#include <memory>
-#include <string>
-
namespace aapt {
-StringPool::Ref::Ref() : mEntry(nullptr) {
+StringPool::Ref::Ref() : entry_(nullptr) {}
+
+StringPool::Ref::Ref(const StringPool::Ref& rhs) : entry_(rhs.entry_) {
+ if (entry_ != nullptr) {
+ entry_->ref_++;
+ }
}
-StringPool::Ref::Ref(const StringPool::Ref& rhs) : mEntry(rhs.mEntry) {
- if (mEntry != nullptr) {
- mEntry->ref++;
- }
-}
-
-StringPool::Ref::Ref(StringPool::Entry* entry) : mEntry(entry) {
- if (mEntry != nullptr) {
- mEntry->ref++;
- }
+StringPool::Ref::Ref(StringPool::Entry* entry) : entry_(entry) {
+ if (entry_ != nullptr) {
+ entry_->ref_++;
+ }
}
StringPool::Ref::~Ref() {
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
+ if (entry_ != nullptr) {
+ entry_->ref_--;
+ }
}
StringPool::Ref& StringPool::Ref::operator=(const StringPool::Ref& rhs) {
- if (rhs.mEntry != nullptr) {
- rhs.mEntry->ref++;
- }
+ if (rhs.entry_ != nullptr) {
+ rhs.entry_->ref_++;
+ }
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
- mEntry = rhs.mEntry;
- return *this;
+ if (entry_ != nullptr) {
+ entry_->ref_--;
+ }
+ entry_ = rhs.entry_;
+ return *this;
}
const std::string* StringPool::Ref::operator->() const {
- return &mEntry->value;
+ return &entry_->value;
}
-const std::string& StringPool::Ref::operator*() const {
- return mEntry->value;
+const std::string& StringPool::Ref::operator*() const { return entry_->value; }
+
+size_t StringPool::Ref::index() const { return entry_->index; }
+
+const StringPool::Context& StringPool::Ref::GetContext() const {
+ return entry_->context;
}
-size_t StringPool::Ref::getIndex() const {
- return mEntry->index;
+StringPool::StyleRef::StyleRef() : entry_(nullptr) {}
+
+StringPool::StyleRef::StyleRef(const StringPool::StyleRef& rhs)
+ : entry_(rhs.entry_) {
+ if (entry_ != nullptr) {
+ entry_->ref_++;
+ }
}
-const StringPool::Context& StringPool::Ref::getContext() const {
- return mEntry->context;
-}
-
-StringPool::StyleRef::StyleRef() : mEntry(nullptr) {
-}
-
-StringPool::StyleRef::StyleRef(const StringPool::StyleRef& rhs) : mEntry(rhs.mEntry) {
- if (mEntry != nullptr) {
- mEntry->ref++;
- }
-}
-
-StringPool::StyleRef::StyleRef(StringPool::StyleEntry* entry) : mEntry(entry) {
- if (mEntry != nullptr) {
- mEntry->ref++;
- }
+StringPool::StyleRef::StyleRef(StringPool::StyleEntry* entry) : entry_(entry) {
+ if (entry_ != nullptr) {
+ entry_->ref_++;
+ }
}
StringPool::StyleRef::~StyleRef() {
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
+ if (entry_ != nullptr) {
+ entry_->ref_--;
+ }
}
-StringPool::StyleRef& StringPool::StyleRef::operator=(const StringPool::StyleRef& rhs) {
- if (rhs.mEntry != nullptr) {
- rhs.mEntry->ref++;
- }
+StringPool::StyleRef& StringPool::StyleRef::operator=(
+ const StringPool::StyleRef& rhs) {
+ if (rhs.entry_ != nullptr) {
+ rhs.entry_->ref_++;
+ }
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
- mEntry = rhs.mEntry;
- return *this;
+ if (entry_ != nullptr) {
+ entry_->ref_--;
+ }
+ entry_ = rhs.entry_;
+ return *this;
}
const StringPool::StyleEntry* StringPool::StyleRef::operator->() const {
- return mEntry;
+ return entry_;
}
const StringPool::StyleEntry& StringPool::StyleRef::operator*() const {
- return *mEntry;
+ return *entry_;
}
-size_t StringPool::StyleRef::getIndex() const {
- return mEntry->str.getIndex();
+size_t StringPool::StyleRef::index() const { return entry_->str.index(); }
+
+const StringPool::Context& StringPool::StyleRef::GetContext() const {
+ return entry_->str.GetContext();
}
-const StringPool::Context& StringPool::StyleRef::getContext() const {
- return mEntry->str.getContext();
+StringPool::Ref StringPool::MakeRef(const StringPiece& str) {
+ return MakeRefImpl(str, Context{}, true);
}
-StringPool::Ref StringPool::makeRef(const StringPiece& str) {
- return makeRefImpl(str, Context{}, true);
+StringPool::Ref StringPool::MakeRef(const StringPiece& str,
+ const Context& context) {
+ return MakeRefImpl(str, context, true);
}
-StringPool::Ref StringPool::makeRef(const StringPiece& str, const Context& context) {
- return makeRefImpl(str, context, true);
-}
-
-StringPool::Ref StringPool::makeRefImpl(const StringPiece& str, const Context& context,
- bool unique) {
- if (unique) {
- auto iter = mIndexedStrings.find(str);
- if (iter != std::end(mIndexedStrings)) {
- return Ref(iter->second);
- }
+StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str,
+ const Context& context, bool unique) {
+ if (unique) {
+ auto iter = indexed_strings_.find(str);
+ if (iter != std::end(indexed_strings_)) {
+ return Ref(iter->second);
}
+ }
- Entry* entry = new Entry();
- entry->value = str.toString();
- entry->context = context;
- entry->index = mStrings.size();
- entry->ref = 0;
- mStrings.emplace_back(entry);
- mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
- return Ref(entry);
+ Entry* entry = new Entry();
+ entry->value = str.ToString();
+ entry->context = context;
+ entry->index = strings_.size();
+ entry->ref_ = 0;
+ strings_.emplace_back(entry);
+ indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
+ return Ref(entry);
}
-StringPool::StyleRef StringPool::makeRef(const StyleString& str) {
- return makeRef(str, Context{});
+StringPool::StyleRef StringPool::MakeRef(const StyleString& str) {
+ return MakeRef(str, Context{});
}
-StringPool::StyleRef StringPool::makeRef(const StyleString& str, const Context& context) {
- Entry* entry = new Entry();
- entry->value = str.str;
- entry->context = context;
- entry->index = mStrings.size();
- entry->ref = 0;
- mStrings.emplace_back(entry);
- mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
+StringPool::StyleRef StringPool::MakeRef(const StyleString& str,
+ const Context& context) {
+ Entry* entry = new Entry();
+ entry->value = str.str;
+ entry->context = context;
+ entry->index = strings_.size();
+ entry->ref_ = 0;
+ strings_.emplace_back(entry);
+ indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
- StyleEntry* styleEntry = new StyleEntry();
- styleEntry->str = Ref(entry);
- for (const aapt::Span& span : str.spans) {
- styleEntry->spans.emplace_back(Span{ makeRef(span.name), span.firstChar, span.lastChar });
+ StyleEntry* style_entry = new StyleEntry();
+ style_entry->str = Ref(entry);
+ for (const aapt::Span& span : str.spans) {
+ style_entry->spans.emplace_back(
+ Span{MakeRef(span.name), span.first_char, span.last_char});
+ }
+ style_entry->ref_ = 0;
+ styles_.emplace_back(style_entry);
+ return StyleRef(style_entry);
+}
+
+StringPool::StyleRef StringPool::MakeRef(const StyleRef& ref) {
+ Entry* entry = new Entry();
+ entry->value = *ref.entry_->str;
+ entry->context = ref.entry_->str.entry_->context;
+ entry->index = strings_.size();
+ entry->ref_ = 0;
+ strings_.emplace_back(entry);
+ indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry));
+
+ StyleEntry* style_entry = new StyleEntry();
+ style_entry->str = Ref(entry);
+ for (const Span& span : ref.entry_->spans) {
+ style_entry->spans.emplace_back(
+ Span{MakeRef(*span.name), span.first_char, span.last_char});
+ }
+ style_entry->ref_ = 0;
+ styles_.emplace_back(style_entry);
+ return StyleRef(style_entry);
+}
+
+void StringPool::Merge(StringPool&& pool) {
+ indexed_strings_.insert(pool.indexed_strings_.begin(),
+ pool.indexed_strings_.end());
+ pool.indexed_strings_.clear();
+ std::move(pool.strings_.begin(), pool.strings_.end(),
+ std::back_inserter(strings_));
+ pool.strings_.clear();
+ std::move(pool.styles_.begin(), pool.styles_.end(),
+ std::back_inserter(styles_));
+ pool.styles_.clear();
+
+ // Assign the indices.
+ const size_t len = strings_.size();
+ for (size_t index = 0; index < len; index++) {
+ strings_[index]->index = index;
+ }
+}
+
+void StringPool::HintWillAdd(size_t stringCount, size_t styleCount) {
+ strings_.reserve(strings_.size() + stringCount);
+ styles_.reserve(styles_.size() + styleCount);
+}
+
+void StringPool::Prune() {
+ const auto iter_end = indexed_strings_.end();
+ auto index_iter = indexed_strings_.begin();
+ while (index_iter != iter_end) {
+ if (index_iter->second->ref_ <= 0) {
+ index_iter = indexed_strings_.erase(index_iter);
+ } else {
+ ++index_iter;
}
- styleEntry->ref = 0;
- mStyles.emplace_back(styleEntry);
- return StyleRef(styleEntry);
+ }
+
+ auto end_iter2 =
+ std::remove_if(strings_.begin(), strings_.end(),
+ [](const std::unique_ptr<Entry>& entry) -> bool {
+ return entry->ref_ <= 0;
+ });
+
+ auto end_iter3 =
+ std::remove_if(styles_.begin(), styles_.end(),
+ [](const std::unique_ptr<StyleEntry>& entry) -> bool {
+ return entry->ref_ <= 0;
+ });
+
+ // Remove the entries at the end or else we'll be accessing
+ // a deleted string from the StyleEntry.
+ strings_.erase(end_iter2, strings_.end());
+ styles_.erase(end_iter3, styles_.end());
+
+ // Reassign the indices.
+ const size_t len = strings_.size();
+ for (size_t index = 0; index < len; index++) {
+ strings_[index]->index = index;
+ }
}
-StringPool::StyleRef StringPool::makeRef(const StyleRef& ref) {
- Entry* entry = new Entry();
- entry->value = *ref.mEntry->str;
- entry->context = ref.mEntry->str.mEntry->context;
- entry->index = mStrings.size();
- entry->ref = 0;
- mStrings.emplace_back(entry);
- mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
+void StringPool::Sort(
+ const std::function<bool(const Entry&, const Entry&)>& cmp) {
+ std::sort(
+ strings_.begin(), strings_.end(),
+ [&cmp](const std::unique_ptr<Entry>& a,
+ const std::unique_ptr<Entry>& b) -> bool { return cmp(*a, *b); });
- StyleEntry* styleEntry = new StyleEntry();
- styleEntry->str = Ref(entry);
- for (const Span& span : ref.mEntry->spans) {
- styleEntry->spans.emplace_back(Span{ makeRef(*span.name), span.firstChar, span.lastChar });
- }
- styleEntry->ref = 0;
- mStyles.emplace_back(styleEntry);
- return StyleRef(styleEntry);
-}
+ // Assign the indices.
+ const size_t len = strings_.size();
+ for (size_t index = 0; index < len; index++) {
+ strings_[index]->index = index;
+ }
-void StringPool::merge(StringPool&& pool) {
- mIndexedStrings.insert(pool.mIndexedStrings.begin(), pool.mIndexedStrings.end());
- pool.mIndexedStrings.clear();
- std::move(pool.mStrings.begin(), pool.mStrings.end(), std::back_inserter(mStrings));
- pool.mStrings.clear();
- std::move(pool.mStyles.begin(), pool.mStyles.end(), std::back_inserter(mStyles));
- pool.mStyles.clear();
-
- // Assign the indices.
- const size_t len = mStrings.size();
- for (size_t index = 0; index < len; index++) {
- mStrings[index]->index = index;
- }
-}
-
-void StringPool::hintWillAdd(size_t stringCount, size_t styleCount) {
- mStrings.reserve(mStrings.size() + stringCount);
- mStyles.reserve(mStyles.size() + styleCount);
-}
-
-void StringPool::prune() {
- const auto iterEnd = std::end(mIndexedStrings);
- auto indexIter = std::begin(mIndexedStrings);
- while (indexIter != iterEnd) {
- if (indexIter->second->ref <= 0) {
- indexIter = mIndexedStrings.erase(indexIter);
- } else {
- ++indexIter;
- }
- }
-
- auto endIter2 = std::remove_if(std::begin(mStrings), std::end(mStrings),
- [](const std::unique_ptr<Entry>& entry) -> bool {
- return entry->ref <= 0;
- }
- );
-
- auto endIter3 = std::remove_if(std::begin(mStyles), std::end(mStyles),
- [](const std::unique_ptr<StyleEntry>& entry) -> bool {
- return entry->ref <= 0;
- }
- );
-
- // Remove the entries at the end or else we'll be accessing
- // a deleted string from the StyleEntry.
- mStrings.erase(endIter2, std::end(mStrings));
- mStyles.erase(endIter3, std::end(mStyles));
-
- // Reassign the indices.
- const size_t len = mStrings.size();
- for (size_t index = 0; index < len; index++) {
- mStrings[index]->index = index;
- }
-}
-
-void StringPool::sort(const std::function<bool(const Entry&, const Entry&)>& cmp) {
- std::sort(std::begin(mStrings), std::end(mStrings),
- [&cmp](const std::unique_ptr<Entry>& a, const std::unique_ptr<Entry>& b) -> bool {
- return cmp(*a, *b);
- }
- );
-
- // Assign the indices.
- const size_t len = mStrings.size();
- for (size_t index = 0; index < len; index++) {
- mStrings[index]->index = index;
- }
-
- // Reorder the styles.
- std::sort(std::begin(mStyles), std::end(mStyles),
+ // Reorder the styles.
+ std::sort(styles_.begin(), styles_.end(),
[](const std::unique_ptr<StyleEntry>& lhs,
const std::unique_ptr<StyleEntry>& rhs) -> bool {
- return lhs->str.getIndex() < rhs->str.getIndex();
- }
- );
+ return lhs->str.index() < rhs->str.index();
+ });
}
template <typename T>
-static T* encodeLength(T* data, size_t length) {
- static_assert(std::is_integral<T>::value, "wat.");
+static T* EncodeLength(T* data, size_t length) {
+ static_assert(std::is_integral<T>::value, "wat.");
- constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
- constexpr size_t kMaxSize = kMask - 1;
- if (length > kMaxSize) {
- *data++ = kMask | (kMaxSize & (length >> (sizeof(T) * 8)));
- }
- *data++ = length;
- return data;
+ constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
+ constexpr size_t kMaxSize = kMask - 1;
+ if (length > kMaxSize) {
+ *data++ = kMask | (kMaxSize & (length >> (sizeof(T) * 8)));
+ }
+ *data++ = length;
+ return data;
}
template <typename T>
-static size_t encodedLengthUnits(size_t length) {
- static_assert(std::is_integral<T>::value, "wat.");
+static size_t EncodedLengthUnits(size_t length) {
+ static_assert(std::is_integral<T>::value, "wat.");
- constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
- constexpr size_t kMaxSize = kMask - 1;
- return length > kMaxSize ? 2 : 1;
+ constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
+ constexpr size_t kMaxSize = kMask - 1;
+ return length > kMaxSize ? 2 : 1;
}
+bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
+ const size_t start_index = out->size();
+ android::ResStringPool_header* header =
+ out->NextBlock<android::ResStringPool_header>();
+ header->header.type = android::RES_STRING_POOL_TYPE;
+ header->header.headerSize = sizeof(*header);
+ header->stringCount = pool.size();
+ if (utf8) {
+ header->flags |= android::ResStringPool_header::UTF8_FLAG;
+ }
-bool StringPool::flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
- const size_t startIndex = out->size();
- android::ResStringPool_header* header = out->nextBlock<android::ResStringPool_header>();
- header->header.type = android::RES_STRING_POOL_TYPE;
- header->header.headerSize = sizeof(*header);
- header->stringCount = pool.size();
+ uint32_t* indices =
+ pool.size() != 0 ? out->NextBlock<uint32_t>(pool.size()) : nullptr;
+
+ uint32_t* style_indices = nullptr;
+ if (!pool.styles_.empty()) {
+ header->styleCount = pool.styles_.back()->str.index() + 1;
+ style_indices = out->NextBlock<uint32_t>(header->styleCount);
+ }
+
+ const size_t before_strings_index = out->size();
+ header->stringsStart = before_strings_index - start_index;
+
+ for (const auto& entry : pool) {
+ *indices = out->size() - before_strings_index;
+ indices++;
+
if (utf8) {
- header->flags |= android::ResStringPool_header::UTF8_FLAG;
+ const std::string& encoded = entry->value;
+ const ssize_t utf16_length = utf8_to_utf16_length(
+ reinterpret_cast<const uint8_t*>(entry->value.data()),
+ entry->value.size());
+ CHECK(utf16_length >= 0);
+
+ const size_t total_size = EncodedLengthUnits<char>(utf16_length) +
+ EncodedLengthUnits<char>(encoded.length()) +
+ encoded.size() + 1;
+
+ char* data = out->NextBlock<char>(total_size);
+
+ // First encode the UTF16 string length.
+ data = EncodeLength(data, utf16_length);
+
+ // Now encode the size of the real UTF8 string.
+ data = EncodeLength(data, encoded.length());
+ strncpy(data, encoded.data(), encoded.size());
+
+ } else {
+ const std::u16string encoded = util::Utf8ToUtf16(entry->value);
+ const ssize_t utf16_length = encoded.size();
+
+ // Total number of 16-bit words to write.
+ const size_t total_size =
+ EncodedLengthUnits<char16_t>(utf16_length) + encoded.size() + 1;
+
+ char16_t* data = out->NextBlock<char16_t>(total_size);
+
+ // Encode the actual UTF16 string length.
+ data = EncodeLength(data, utf16_length);
+ const size_t byte_length = encoded.size() * sizeof(char16_t);
+
+ // NOTE: For some reason, strncpy16(data, entry->value.data(),
+ // entry->value.size()) truncates the string.
+ memcpy(data, encoded.data(), byte_length);
+
+ // The null-terminating character is already here due to the block of data
+ // being set to 0s on allocation.
+ }
+ }
+
+ out->Align4();
+
+ if (!pool.styles_.empty()) {
+ const size_t before_styles_index = out->size();
+ header->stylesStart = before_styles_index - start_index;
+
+ size_t current_index = 0;
+ for (const auto& entry : pool.styles_) {
+ while (entry->str.index() > current_index) {
+ style_indices[current_index++] = out->size() - before_styles_index;
+
+ uint32_t* span_offset = out->NextBlock<uint32_t>();
+ *span_offset = android::ResStringPool_span::END;
+ }
+ style_indices[current_index++] = out->size() - before_styles_index;
+
+ android::ResStringPool_span* span =
+ out->NextBlock<android::ResStringPool_span>(entry->spans.size());
+ for (const auto& s : entry->spans) {
+ span->name.index = s.name.index();
+ span->firstChar = s.first_char;
+ span->lastChar = s.last_char;
+ span++;
+ }
+
+ uint32_t* spanEnd = out->NextBlock<uint32_t>();
+ *spanEnd = android::ResStringPool_span::END;
}
- uint32_t* indices = pool.size() != 0 ? out->nextBlock<uint32_t>(pool.size()) : nullptr;
-
- uint32_t* styleIndices = nullptr;
- if (!pool.mStyles.empty()) {
- header->styleCount = pool.mStyles.back()->str.getIndex() + 1;
- styleIndices = out->nextBlock<uint32_t>(header->styleCount);
- }
-
- const size_t beforeStringsIndex = out->size();
- header->stringsStart = beforeStringsIndex - startIndex;
-
- for (const auto& entry : pool) {
- *indices = out->size() - beforeStringsIndex;
- indices++;
-
- if (utf8) {
- const std::string& encoded = entry->value;
- const ssize_t utf16Length = utf8_to_utf16_length(
- reinterpret_cast<const uint8_t*>(entry->value.data()), entry->value.size());
- assert(utf16Length >= 0);
-
- const size_t totalSize = encodedLengthUnits<char>(utf16Length)
- + encodedLengthUnits<char>(encoded.length())
- + encoded.size() + 1;
-
- char* data = out->nextBlock<char>(totalSize);
-
- // First encode the UTF16 string length.
- data = encodeLength(data, utf16Length);
-
- // Now encode the size of the real UTF8 string.
- data = encodeLength(data, encoded.length());
- strncpy(data, encoded.data(), encoded.size());
-
- } else {
- const std::u16string encoded = util::utf8ToUtf16(entry->value);
- const ssize_t utf16Length = encoded.size();
-
- // Total number of 16-bit words to write.
- const size_t totalSize = encodedLengthUnits<char16_t>(utf16Length) + encoded.size() + 1;
-
- char16_t* data = out->nextBlock<char16_t>(totalSize);
-
- // Encode the actual UTF16 string length.
- data = encodeLength(data, utf16Length);
- const size_t byteLength = encoded.size() * sizeof(char16_t);
-
- // NOTE: For some reason, strncpy16(data, entry->value.data(), entry->value.size())
- // truncates the string.
- memcpy(data, encoded.data(), byteLength);
-
- // The null-terminating character is already here due to the block of data being set
- // to 0s on allocation.
- }
- }
-
- out->align4();
-
- if (!pool.mStyles.empty()) {
- const size_t beforeStylesIndex = out->size();
- header->stylesStart = beforeStylesIndex - startIndex;
-
- size_t currentIndex = 0;
- for (const auto& entry : pool.mStyles) {
- while (entry->str.getIndex() > currentIndex) {
- styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
-
- uint32_t* spanOffset = out->nextBlock<uint32_t>();
- *spanOffset = android::ResStringPool_span::END;
- }
- styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
-
- android::ResStringPool_span* span =
- out->nextBlock<android::ResStringPool_span>(entry->spans.size());
- for (const auto& s : entry->spans) {
- span->name.index = s.name.getIndex();
- span->firstChar = s.firstChar;
- span->lastChar = s.lastChar;
- span++;
- }
-
- uint32_t* spanEnd = out->nextBlock<uint32_t>();
- *spanEnd = android::ResStringPool_span::END;
- }
-
- // The error checking code in the platform looks for an entire
- // ResStringPool_span structure worth of 0xFFFFFFFF at the end
- // of the style block, so fill in the remaining 2 32bit words
- // with 0xFFFFFFFF.
- const size_t paddingLength = sizeof(android::ResStringPool_span)
- - sizeof(android::ResStringPool_span::name);
- uint8_t* padding = out->nextBlock<uint8_t>(paddingLength);
- memset(padding, 0xff, paddingLength);
- out->align4();
- }
- header->header.size = out->size() - startIndex;
- return true;
+ // The error checking code in the platform looks for an entire
+ // ResStringPool_span structure worth of 0xFFFFFFFF at the end
+ // of the style block, so fill in the remaining 2 32bit words
+ // with 0xFFFFFFFF.
+ const size_t padding_length = sizeof(android::ResStringPool_span) -
+ sizeof(android::ResStringPool_span::name);
+ uint8_t* padding = out->NextBlock<uint8_t>(padding_length);
+ memset(padding, 0xff, padding_length);
+ out->Align4();
+ }
+ header->header.size = out->size() - start_index;
+ return true;
}
-bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
- return flatten(out, pool, true);
+bool StringPool::FlattenUtf8(BigBuffer* out, const StringPool& pool) {
+ return Flatten(out, pool, true);
}
-bool StringPool::flattenUtf16(BigBuffer* out, const StringPool& pool) {
- return flatten(out, pool, false);
+bool StringPool::FlattenUtf16(BigBuffer* out, const StringPool& pool) {
+ return Flatten(out, pool, false);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 13545be..a4f556c 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -17,207 +17,218 @@
#ifndef AAPT_STRING_POOL_H
#define AAPT_STRING_POOL_H
-#include "util/BigBuffer.h"
-#include "ConfigDescription.h"
-#include "util/StringPiece.h"
-
#include <functional>
-#include <unordered_map>
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
+#include "ConfigDescription.h"
+#include "util/BigBuffer.h"
+#include "util/StringPiece.h"
+
namespace aapt {
struct Span {
- std::string name;
- uint32_t firstChar;
- uint32_t lastChar;
+ std::string name;
+ uint32_t first_char;
+ uint32_t last_char;
};
struct StyleString {
- std::string str;
- std::vector<Span> spans;
+ std::string str;
+ std::vector<Span> spans;
};
class StringPool {
-public:
- struct Context {
- uint32_t priority;
- ConfigDescription config;
+ public:
+ class Context {
+ public:
+ enum : uint32_t {
+ kStylePriority = 0u,
+ kHighPriority = 1u,
+ kNormalPriority = 0x7fffffffu,
+ kLowPriority = 0xffffffffu,
};
+ uint32_t priority = kNormalPriority;
+ ConfigDescription config;
- class Entry;
+ Context() = default;
+ Context(uint32_t p, const ConfigDescription& c) : priority(p), config(c) {}
+ explicit Context(uint32_t p) : priority(p) {}
+ explicit Context(const ConfigDescription& c)
+ : priority(kNormalPriority), config(c) {}
+ };
- class Ref {
- public:
- Ref();
- Ref(const Ref&);
- ~Ref();
+ class Entry;
- Ref& operator=(const Ref& rhs);
- const std::string* operator->() const;
- const std::string& operator*() const;
+ class Ref {
+ public:
+ Ref();
+ Ref(const Ref&);
+ ~Ref();
- size_t getIndex() const;
- const Context& getContext() const;
+ Ref& operator=(const Ref& rhs);
+ const std::string* operator->() const;
+ const std::string& operator*() const;
- private:
- friend class StringPool;
+ size_t index() const;
+ const Context& GetContext() const;
- explicit Ref(Entry* entry);
+ private:
+ friend class StringPool;
- Entry* mEntry;
- };
+ explicit Ref(Entry* entry);
- class StyleEntry;
+ Entry* entry_;
+ };
- class StyleRef {
- public:
- StyleRef();
- StyleRef(const StyleRef&);
- ~StyleRef();
+ class StyleEntry;
- StyleRef& operator=(const StyleRef& rhs);
- const StyleEntry* operator->() const;
- const StyleEntry& operator*() const;
+ class StyleRef {
+ public:
+ StyleRef();
+ StyleRef(const StyleRef&);
+ ~StyleRef();
- size_t getIndex() const;
- const Context& getContext() const;
+ StyleRef& operator=(const StyleRef& rhs);
+ const StyleEntry* operator->() const;
+ const StyleEntry& operator*() const;
- private:
- friend class StringPool;
+ size_t index() const;
+ const Context& GetContext() const;
- explicit StyleRef(StyleEntry* entry);
+ private:
+ friend class StringPool;
- StyleEntry* mEntry;
- };
+ explicit StyleRef(StyleEntry* entry);
- class Entry {
- public:
- std::string value;
- Context context;
- size_t index;
+ StyleEntry* entry_;
+ };
- private:
- friend class StringPool;
- friend class Ref;
+ class Entry {
+ public:
+ std::string value;
+ Context context;
+ size_t index;
- int ref;
- };
+ private:
+ friend class StringPool;
+ friend class Ref;
- struct Span {
- Ref name;
- uint32_t firstChar;
- uint32_t lastChar;
- };
+ int ref_;
+ };
- class StyleEntry {
- public:
- Ref str;
- std::vector<Span> spans;
+ struct Span {
+ Ref name;
+ uint32_t first_char;
+ uint32_t last_char;
+ };
- private:
- friend class StringPool;
- friend class StyleRef;
+ class StyleEntry {
+ public:
+ Ref str;
+ std::vector<Span> spans;
- int ref;
- };
+ private:
+ friend class StringPool;
+ friend class StyleRef;
- using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator;
+ int ref_;
+ };
- static bool flattenUtf8(BigBuffer* out, const StringPool& pool);
- static bool flattenUtf16(BigBuffer* out, const StringPool& pool);
+ using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator;
- StringPool() = default;
- StringPool(const StringPool&) = delete;
+ static bool FlattenUtf8(BigBuffer* out, const StringPool& pool);
+ static bool FlattenUtf16(BigBuffer* out, const StringPool& pool);
- /**
- * Adds a string to the pool, unless it already exists. Returns
- * a reference to the string in the pool.
- */
- Ref makeRef(const StringPiece& str);
+ StringPool() = default;
+ StringPool(const StringPool&) = delete;
- /**
- * Adds a string to the pool, unless it already exists, with a context
- * object that can be used when sorting the string pool. Returns
- * a reference to the string in the pool.
- */
- Ref makeRef(const StringPiece& str, const Context& context);
+ /**
+ * Adds a string to the pool, unless it already exists. Returns
+ * a reference to the string in the pool.
+ */
+ Ref MakeRef(const StringPiece& str);
- /**
- * Adds a style to the string pool and returns a reference to it.
- */
- StyleRef makeRef(const StyleString& str);
+ /**
+ * Adds a string to the pool, unless it already exists, with a context
+ * object that can be used when sorting the string pool. Returns
+ * a reference to the string in the pool.
+ */
+ Ref MakeRef(const StringPiece& str, const Context& context);
- /**
- * Adds a style to the string pool with a context object that
- * can be used when sorting the string pool. Returns a reference
- * to the style in the string pool.
- */
- StyleRef makeRef(const StyleString& str, const Context& context);
+ /**
+ * Adds a style to the string pool and returns a reference to it.
+ */
+ StyleRef MakeRef(const StyleString& str);
- /**
- * Adds a style from another string pool. Returns a reference to the
- * style in the string pool.
- */
- StyleRef makeRef(const StyleRef& ref);
+ /**
+ * Adds a style to the string pool with a context object that
+ * can be used when sorting the string pool. Returns a reference
+ * to the style in the string pool.
+ */
+ StyleRef MakeRef(const StyleString& str, const Context& context);
- /**
- * Moves pool into this one without coalescing strings. When this
- * function returns, pool will be empty.
- */
- void merge(StringPool&& pool);
+ /**
+ * Adds a style from another string pool. Returns a reference to the
+ * style in the string pool.
+ */
+ StyleRef MakeRef(const StyleRef& ref);
- /**
- * Retuns the number of strings in the table.
- */
- inline size_t size() const;
+ /**
+ * Moves pool into this one without coalescing strings. When this
+ * function returns, pool will be empty.
+ */
+ void Merge(StringPool&& pool);
- /**
- * Reserves space for strings and styles as an optimization.
- */
- void hintWillAdd(size_t stringCount, size_t styleCount);
+ /**
+ * Returns the number of strings in the table.
+ */
+ inline size_t size() const;
- /**
- * Sorts the strings according to some comparison function.
- */
- void sort(const std::function<bool(const Entry&, const Entry&)>& cmp);
+ /**
+ * Reserves space for strings and styles as an optimization.
+ */
+ void HintWillAdd(size_t string_count, size_t style_count);
- /**
- * Removes any strings that have no references.
- */
- void prune();
+ /**
+ * Sorts the strings according to some comparison function.
+ */
+ void Sort(const std::function<bool(const Entry&, const Entry&)>& cmp);
-private:
- friend const_iterator begin(const StringPool& pool);
- friend const_iterator end(const StringPool& pool);
+ /**
+ * Removes any strings that have no references.
+ */
+ void Prune();
- static bool flatten(BigBuffer* out, const StringPool& pool, bool utf8);
+ private:
+ friend const_iterator begin(const StringPool& pool);
+ friend const_iterator end(const StringPool& pool);
- Ref makeRefImpl(const StringPiece& str, const Context& context, bool unique);
+ static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8);
- std::vector<std::unique_ptr<Entry>> mStrings;
- std::vector<std::unique_ptr<StyleEntry>> mStyles;
- std::unordered_multimap<StringPiece, Entry*> mIndexedStrings;
+ Ref MakeRefImpl(const StringPiece& str, const Context& context, bool unique);
+
+ std::vector<std::unique_ptr<Entry>> strings_;
+ std::vector<std::unique_ptr<StyleEntry>> styles_;
+ std::unordered_multimap<StringPiece, Entry*> indexed_strings_;
};
//
// Inline implementation
//
-inline size_t StringPool::size() const {
- return mStrings.size();
-}
+inline size_t StringPool::size() const { return strings_.size(); }
inline StringPool::const_iterator begin(const StringPool& pool) {
- return pool.mStrings.begin();
+ return pool.strings_.begin();
}
inline StringPool::const_iterator end(const StringPool& pool) {
- return pool.mStrings.end();
+ return pool.strings_.end();
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_STRING_POOL_H
+#endif // AAPT_STRING_POOL_H
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 1367af7..e1394fc 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -15,254 +15,254 @@
*/
#include "StringPool.h"
-#include "test/Test.h"
-#include "util/Util.h"
#include <string>
+#include "test/Test.h"
+#include "util/Util.h"
+
namespace aapt {
TEST(StringPoolTest, InsertOneString) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("wut");
- EXPECT_EQ(*ref, "wut");
+ StringPool::Ref ref = pool.MakeRef("wut");
+ EXPECT_EQ(*ref, "wut");
}
TEST(StringPoolTest, InsertTwoUniqueStrings) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("wut");
- StringPool::Ref ref2 = pool.makeRef("hey");
+ StringPool::Ref ref = pool.MakeRef("wut");
+ StringPool::Ref ref2 = pool.MakeRef("hey");
- EXPECT_EQ(*ref, "wut");
- EXPECT_EQ(*ref2, "hey");
+ EXPECT_EQ(*ref, "wut");
+ EXPECT_EQ(*ref2, "hey");
}
TEST(StringPoolTest, DoNotInsertNewDuplicateString) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("wut");
- StringPool::Ref ref2 = pool.makeRef("wut");
+ StringPool::Ref ref = pool.MakeRef("wut");
+ StringPool::Ref ref2 = pool.MakeRef("wut");
- EXPECT_EQ(*ref, "wut");
- EXPECT_EQ(*ref2, "wut");
- EXPECT_EQ(1u, pool.size());
+ EXPECT_EQ(*ref, "wut");
+ EXPECT_EQ(*ref2, "wut");
+ EXPECT_EQ(1u, pool.size());
}
TEST(StringPoolTest, MaintainInsertionOrderIndex) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("z");
- StringPool::Ref ref2 = pool.makeRef("a");
- StringPool::Ref ref3 = pool.makeRef("m");
+ StringPool::Ref ref = pool.MakeRef("z");
+ StringPool::Ref ref2 = pool.MakeRef("a");
+ StringPool::Ref ref3 = pool.MakeRef("m");
- EXPECT_EQ(0u, ref.getIndex());
- EXPECT_EQ(1u, ref2.getIndex());
- EXPECT_EQ(2u, ref3.getIndex());
+ EXPECT_EQ(0u, ref.index());
+ EXPECT_EQ(1u, ref2.index());
+ EXPECT_EQ(2u, ref3.index());
}
TEST(StringPoolTest, PruneStringsWithNoReferences) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref refA = pool.makeRef("foo");
- {
- StringPool::Ref ref = pool.makeRef("wut");
- EXPECT_EQ(*ref, "wut");
- EXPECT_EQ(2u, pool.size());
- }
- StringPool::Ref refB = pool.makeRef("bar");
-
- EXPECT_EQ(3u, pool.size());
- pool.prune();
+ StringPool::Ref refA = pool.MakeRef("foo");
+ {
+ StringPool::Ref ref = pool.MakeRef("wut");
+ EXPECT_EQ(*ref, "wut");
EXPECT_EQ(2u, pool.size());
- StringPool::const_iterator iter = begin(pool);
- EXPECT_EQ((*iter)->value, "foo");
- EXPECT_LT((*iter)->index, 2u);
- ++iter;
- EXPECT_EQ((*iter)->value, "bar");
- EXPECT_LT((*iter)->index, 2u);
+ }
+ StringPool::Ref refB = pool.MakeRef("bar");
+
+ EXPECT_EQ(3u, pool.size());
+ pool.Prune();
+ EXPECT_EQ(2u, pool.size());
+ StringPool::const_iterator iter = begin(pool);
+ EXPECT_EQ((*iter)->value, "foo");
+ EXPECT_LT((*iter)->index, 2u);
+ ++iter;
+ EXPECT_EQ((*iter)->value, "bar");
+ EXPECT_LT((*iter)->index, 2u);
}
TEST(StringPoolTest, SortAndMaintainIndexesInReferences) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("z");
- StringPool::StyleRef ref2 = pool.makeRef(StyleString{ {"a"} });
- StringPool::Ref ref3 = pool.makeRef("m");
+ StringPool::Ref ref = pool.MakeRef("z");
+ StringPool::StyleRef ref2 = pool.MakeRef(StyleString{{"a"}});
+ StringPool::Ref ref3 = pool.MakeRef("m");
- EXPECT_EQ(*ref, "z");
- EXPECT_EQ(0u, ref.getIndex());
+ EXPECT_EQ(*ref, "z");
+ EXPECT_EQ(0u, ref.index());
- EXPECT_EQ(*(ref2->str), "a");
- EXPECT_EQ(1u, ref2.getIndex());
+ EXPECT_EQ(*(ref2->str), "a");
+ EXPECT_EQ(1u, ref2.index());
- EXPECT_EQ(*ref3, "m");
- EXPECT_EQ(2u, ref3.getIndex());
+ EXPECT_EQ(*ref3, "m");
+ EXPECT_EQ(2u, ref3.index());
- pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- return a.value < b.value;
- });
+ pool.Sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+ return a.value < b.value;
+ });
+ EXPECT_EQ(*ref, "z");
+ EXPECT_EQ(2u, ref.index());
- EXPECT_EQ(*ref, "z");
- EXPECT_EQ(2u, ref.getIndex());
+ EXPECT_EQ(*(ref2->str), "a");
+ EXPECT_EQ(0u, ref2.index());
- EXPECT_EQ(*(ref2->str), "a");
- EXPECT_EQ(0u, ref2.getIndex());
-
- EXPECT_EQ(*ref3, "m");
- EXPECT_EQ(1u, ref3.getIndex());
+ EXPECT_EQ(*ref3, "m");
+ EXPECT_EQ(1u, ref3.index());
}
TEST(StringPoolTest, SortAndStillDedupe) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("z");
- StringPool::Ref ref2 = pool.makeRef("a");
- StringPool::Ref ref3 = pool.makeRef("m");
+ StringPool::Ref ref = pool.MakeRef("z");
+ StringPool::Ref ref2 = pool.MakeRef("a");
+ StringPool::Ref ref3 = pool.MakeRef("m");
- pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- return a.value < b.value;
- });
+ pool.Sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+ return a.value < b.value;
+ });
- StringPool::Ref ref4 = pool.makeRef("z");
- StringPool::Ref ref5 = pool.makeRef("a");
- StringPool::Ref ref6 = pool.makeRef("m");
+ StringPool::Ref ref4 = pool.MakeRef("z");
+ StringPool::Ref ref5 = pool.MakeRef("a");
+ StringPool::Ref ref6 = pool.MakeRef("m");
- EXPECT_EQ(ref4.getIndex(), ref.getIndex());
- EXPECT_EQ(ref5.getIndex(), ref2.getIndex());
- EXPECT_EQ(ref6.getIndex(), ref3.getIndex());
+ EXPECT_EQ(ref4.index(), ref.index());
+ EXPECT_EQ(ref5.index(), ref2.index());
+ EXPECT_EQ(ref6.index(), ref3.index());
}
TEST(StringPoolTest, AddStyles) {
- StringPool pool;
+ StringPool pool;
- StyleString str {
- { "android" },
- {
- Span{ { "b" }, 2, 6 }
- }
- };
+ StyleString str{{"android"}, {Span{{"b"}, 2, 6}}};
- StringPool::StyleRef ref = pool.makeRef(str);
+ StringPool::StyleRef ref = pool.MakeRef(str);
- EXPECT_EQ(0u, ref.getIndex());
- EXPECT_EQ(std::string("android"), *(ref->str));
- ASSERT_EQ(1u, ref->spans.size());
+ EXPECT_EQ(0u, ref.index());
+ EXPECT_EQ(std::string("android"), *(ref->str));
+ ASSERT_EQ(1u, ref->spans.size());
- const StringPool::Span& span = ref->spans.front();
- EXPECT_EQ(*(span.name), "b");
- EXPECT_EQ(2u, span.firstChar);
- EXPECT_EQ(6u, span.lastChar);
+ const StringPool::Span& span = ref->spans.front();
+ EXPECT_EQ(*(span.name), "b");
+ EXPECT_EQ(2u, span.first_char);
+ EXPECT_EQ(6u, span.last_char);
}
TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("android");
+ StringPool::Ref ref = pool.MakeRef("android");
- StyleString str { { "android" } };
- StringPool::StyleRef styleRef = pool.makeRef(str);
+ StyleString str{{"android"}};
+ StringPool::StyleRef styleRef = pool.MakeRef(str);
- EXPECT_NE(ref.getIndex(), styleRef.getIndex());
+ EXPECT_NE(ref.index(), styleRef.index());
}
TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {
- using namespace android; // For NO_ERROR on Windows.
+ using namespace android; // For NO_ERROR on Windows.
- StringPool pool;
- BigBuffer buffer(1024);
- StringPool::flattenUtf8(&buffer, pool);
+ StringPool pool;
+ BigBuffer buffer(1024);
+ StringPool::FlattenUtf8(&buffer, pool);
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- ResStringPool test;
- ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+ std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+ ResStringPool test;
+ ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
}
TEST(StringPoolTest, FlattenOddCharactersUtf16) {
- using namespace android; // For NO_ERROR on Windows.
+ using namespace android; // For NO_ERROR on Windows.
- StringPool pool;
- pool.makeRef("\u093f");
- BigBuffer buffer(1024);
- StringPool::flattenUtf16(&buffer, pool);
+ StringPool pool;
+ pool.MakeRef("\u093f");
+ BigBuffer buffer(1024);
+ StringPool::FlattenUtf16(&buffer, pool);
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- ResStringPool test;
- ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
- size_t len = 0;
- const char16_t* str = test.stringAt(0, &len);
- EXPECT_EQ(1u, len);
- EXPECT_EQ(u'\u093f', *str);
- EXPECT_EQ(0u, str[1]);
+ std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+ ResStringPool test;
+ ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+ size_t len = 0;
+ const char16_t* str = test.stringAt(0, &len);
+ EXPECT_EQ(1u, len);
+ EXPECT_EQ(u'\u093f', *str);
+ EXPECT_EQ(0u, str[1]);
}
-constexpr const char* sLongString = "バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使 用するその他のアプリは、起動しても更新されないことがあります。バッテリーセーバーは端末の充電中は自動的にOFFになります。";
+constexpr const char* sLongString =
+ "バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑"
+ "え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限"
+ "します。メール、SMSや、同期を使 "
+ "用するその他のアプリは、起動しても更新されないことがあります。バッテリーセ"
+ "ーバーは端末の充電中は自動的にOFFになります。";
TEST(StringPoolTest, Flatten) {
- using namespace android; // For NO_ERROR on Windows.
+ using namespace android; // For NO_ERROR on Windows.
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref1 = pool.makeRef("hello");
- StringPool::Ref ref2 = pool.makeRef("goodbye");
- StringPool::Ref ref3 = pool.makeRef(sLongString);
- StringPool::Ref ref4 = pool.makeRef("");
- StringPool::StyleRef ref5 = pool.makeRef(StyleString{
- { "style" },
- { Span{ { "b" }, 0, 1 }, Span{ { "i" }, 2, 3 } }
- });
+ StringPool::Ref ref1 = pool.MakeRef("hello");
+ StringPool::Ref ref2 = pool.MakeRef("goodbye");
+ StringPool::Ref ref3 = pool.MakeRef(sLongString);
+ StringPool::Ref ref4 = pool.MakeRef("");
+ StringPool::StyleRef ref5 = pool.MakeRef(
+ StyleString{{"style"}, {Span{{"b"}, 0, 1}, Span{{"i"}, 2, 3}}});
- EXPECT_EQ(0u, ref1.getIndex());
- EXPECT_EQ(1u, ref2.getIndex());
- EXPECT_EQ(2u, ref3.getIndex());
- EXPECT_EQ(3u, ref4.getIndex());
- EXPECT_EQ(4u, ref5.getIndex());
+ EXPECT_EQ(0u, ref1.index());
+ EXPECT_EQ(1u, ref2.index());
+ EXPECT_EQ(2u, ref3.index());
+ EXPECT_EQ(3u, ref4.index());
+ EXPECT_EQ(4u, ref5.index());
- BigBuffer buffers[2] = { BigBuffer(1024), BigBuffer(1024) };
- StringPool::flattenUtf8(&buffers[0], pool);
- StringPool::flattenUtf16(&buffers[1], pool);
+ BigBuffer buffers[2] = {BigBuffer(1024), BigBuffer(1024)};
+ StringPool::FlattenUtf8(&buffers[0], pool);
+ StringPool::FlattenUtf16(&buffers[1], pool);
- // Test both UTF-8 and UTF-16 buffers.
- for (const BigBuffer& buffer : buffers) {
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+ // Test both UTF-8 and UTF-16 buffers.
+ for (const BigBuffer& buffer : buffers) {
+ std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
- ResStringPool test;
- ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+ ResStringPool test;
+ ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
- EXPECT_EQ(std::string("hello"), util::getString(test, 0));
- EXPECT_EQ(StringPiece16(u"hello"), util::getString16(test, 0));
+ EXPECT_EQ(std::string("hello"), util::GetString(test, 0));
+ EXPECT_EQ(StringPiece16(u"hello"), util::GetString16(test, 0));
- EXPECT_EQ(std::string("goodbye"), util::getString(test, 1));
- EXPECT_EQ(StringPiece16(u"goodbye"), util::getString16(test, 1));
+ EXPECT_EQ(std::string("goodbye"), util::GetString(test, 1));
+ EXPECT_EQ(StringPiece16(u"goodbye"), util::GetString16(test, 1));
- EXPECT_EQ(StringPiece(sLongString), util::getString(test, 2));
- EXPECT_EQ(util::utf8ToUtf16(sLongString), util::getString16(test, 2).toString());
+ EXPECT_EQ(StringPiece(sLongString), util::GetString(test, 2));
+ EXPECT_EQ(util::Utf8ToUtf16(sLongString),
+ util::GetString16(test, 2).ToString());
- size_t len;
- EXPECT_TRUE(test.stringAt(3, &len) != nullptr || test.string8At(3, &len) != nullptr);
+ size_t len;
+ EXPECT_TRUE(test.stringAt(3, &len) != nullptr ||
+ test.string8At(3, &len) != nullptr);
- EXPECT_EQ(std::string("style"), util::getString(test, 4));
- EXPECT_EQ(StringPiece16(u"style"), util::getString16(test, 4));
+ EXPECT_EQ(std::string("style"), util::GetString(test, 4));
+ EXPECT_EQ(StringPiece16(u"style"), util::GetString16(test, 4));
- const ResStringPool_span* span = test.styleAt(4);
- ASSERT_NE(nullptr, span);
- EXPECT_EQ(std::string("b"), util::getString(test, span->name.index));
- EXPECT_EQ(StringPiece16(u"b"), util::getString16(test, span->name.index));
- EXPECT_EQ(0u, span->firstChar);
- EXPECT_EQ(1u, span->lastChar);
- span++;
+ const ResStringPool_span* span = test.styleAt(4);
+ ASSERT_NE(nullptr, span);
+ EXPECT_EQ(std::string("b"), util::GetString(test, span->name.index));
+ EXPECT_EQ(StringPiece16(u"b"), util::GetString16(test, span->name.index));
+ EXPECT_EQ(0u, span->firstChar);
+ EXPECT_EQ(1u, span->lastChar);
+ span++;
- ASSERT_NE(ResStringPool_span::END, span->name.index);
- EXPECT_EQ(std::string("i"), util::getString(test, span->name.index));
- EXPECT_EQ(StringPiece16(u"i"), util::getString16(test, span->name.index));
- EXPECT_EQ(2u, span->firstChar);
- EXPECT_EQ(3u, span->lastChar);
- span++;
+ ASSERT_NE(ResStringPool_span::END, span->name.index);
+ EXPECT_EQ(std::string("i"), util::GetString(test, span->name.index));
+ EXPECT_EQ(StringPiece16(u"i"), util::GetString16(test, span->name.index));
+ EXPECT_EQ(2u, span->firstChar);
+ EXPECT_EQ(3u, span->lastChar);
+ span++;
- EXPECT_EQ(ResStringPool_span::END, span->name.index);
- }
+ EXPECT_EQ(ResStringPool_span::END, span->name.index);
+ }
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 9dc6a9c..1cb6aa1 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -17,91 +17,92 @@
#ifndef AAPT_VALUE_VISITOR_H
#define AAPT_VALUE_VISITOR_H
-#include "ResourceValues.h"
#include "ResourceTable.h"
+#include "ResourceValues.h"
namespace aapt {
/**
- * Visits a value and invokes the appropriate method based on its type. Does not traverse
- * into compound types. Use ValueVisitor for that.
+ * Visits a value and invokes the appropriate method based on its type. Does not
+ * traverse into compound types. Use ValueVisitor for that.
*/
struct RawValueVisitor {
- virtual ~RawValueVisitor() = default;
+ virtual ~RawValueVisitor() = default;
- virtual void visitItem(Item* value) {}
- virtual void visit(Reference* value) { visitItem(value); }
- virtual void visit(RawString* value) { visitItem(value); }
- virtual void visit(String* value) { visitItem(value); }
- virtual void visit(StyledString* value) { visitItem(value); }
- virtual void visit(FileReference* value) { visitItem(value); }
- virtual void visit(Id* value) { visitItem(value); }
- virtual void visit(BinaryPrimitive* value) { visitItem(value); }
+ virtual void VisitItem(Item* value) {}
+ virtual void Visit(Reference* value) { VisitItem(value); }
+ virtual void Visit(RawString* value) { VisitItem(value); }
+ virtual void Visit(String* value) { VisitItem(value); }
+ virtual void Visit(StyledString* value) { VisitItem(value); }
+ virtual void Visit(FileReference* value) { VisitItem(value); }
+ virtual void Visit(Id* value) { VisitItem(value); }
+ virtual void Visit(BinaryPrimitive* value) { VisitItem(value); }
- virtual void visit(Attribute* value) {}
- virtual void visit(Style* value) {}
- virtual void visit(Array* value) {}
- virtual void visit(Plural* value) {}
- virtual void visit(Styleable* value) {}
+ virtual void Visit(Attribute* value) {}
+ virtual void Visit(Style* value) {}
+ virtual void Visit(Array* value) {}
+ virtual void Visit(Plural* value) {}
+ virtual void Visit(Styleable* value) {}
};
// NOLINT, do not add parentheses around T.
-#define DECL_VISIT_COMPOUND_VALUE(T) \
- virtual void visit(T* value) { /* NOLINT */ \
- visitSubValues(value); \
- }
+#define DECL_VISIT_COMPOUND_VALUE(T) \
+ virtual void Visit(T* value) override { /* NOLINT */ \
+ VisitSubValues(value); \
+ }
/**
- * Visits values, and if they are compound values, visits the components as well.
+ * Visits values, and if they are compound values, visits the components as
+ * well.
*/
struct ValueVisitor : public RawValueVisitor {
- // The compiler will think we're hiding an overload, when we actually intend
- // to call into RawValueVisitor. This will expose the visit methods in the super
- // class so the compiler knows we are trying to call them.
- using RawValueVisitor::visit;
+ // The compiler will think we're hiding an overload, when we actually intend
+ // to call into RawValueVisitor. This will expose the visit methods in the
+ // super class so the compiler knows we are trying to call them.
+ using RawValueVisitor::Visit;
- void visitSubValues(Attribute* attribute) {
- for (Attribute::Symbol& symbol : attribute->symbols) {
- visit(&symbol.symbol);
- }
+ void VisitSubValues(Attribute* attribute) {
+ for (Attribute::Symbol& symbol : attribute->symbols) {
+ Visit(&symbol.symbol);
+ }
+ }
+
+ void VisitSubValues(Style* style) {
+ if (style->parent) {
+ Visit(&style->parent.value());
}
- void visitSubValues(Style* style) {
- if (style->parent) {
- visit(&style->parent.value());
- }
-
- for (Style::Entry& entry : style->entries) {
- visit(&entry.key);
- entry.value->accept(this);
- }
+ for (Style::Entry& entry : style->entries) {
+ Visit(&entry.key);
+ entry.value->Accept(this);
}
+ }
- void visitSubValues(Array* array) {
- for (std::unique_ptr<Item>& item : array->items) {
- item->accept(this);
- }
+ void VisitSubValues(Array* array) {
+ for (std::unique_ptr<Item>& item : array->items) {
+ item->Accept(this);
}
+ }
- void visitSubValues(Plural* plural) {
- for (std::unique_ptr<Item>& item : plural->values) {
- if (item) {
- item->accept(this);
- }
- }
+ void VisitSubValues(Plural* plural) {
+ for (std::unique_ptr<Item>& item : plural->values) {
+ if (item) {
+ item->Accept(this);
+ }
}
+ }
- void visitSubValues(Styleable* styleable) {
- for (Reference& reference : styleable->entries) {
- visit(&reference);
- }
+ void VisitSubValues(Styleable* styleable) {
+ for (Reference& reference : styleable->entries) {
+ Visit(&reference);
}
+ }
- DECL_VISIT_COMPOUND_VALUE(Attribute);
- DECL_VISIT_COMPOUND_VALUE(Style);
- DECL_VISIT_COMPOUND_VALUE(Array);
- DECL_VISIT_COMPOUND_VALUE(Plural);
- DECL_VISIT_COMPOUND_VALUE(Styleable);
+ DECL_VISIT_COMPOUND_VALUE(Attribute);
+ DECL_VISIT_COMPOUND_VALUE(Style);
+ DECL_VISIT_COMPOUND_VALUE(Array);
+ DECL_VISIT_COMPOUND_VALUE(Plural);
+ DECL_VISIT_COMPOUND_VALUE(Styleable);
};
/**
@@ -109,11 +110,9 @@
*/
template <typename T>
struct DynCastVisitor : public RawValueVisitor {
- T* value = nullptr;
+ T* value = nullptr;
- void visit(T* v) override {
- value = v;
- }
+ void Visit(T* v) override { value = v; }
};
/**
@@ -121,16 +120,14 @@
*/
template <>
struct DynCastVisitor<Item> : public RawValueVisitor {
- Item* value = nullptr;
+ Item* value = nullptr;
- void visitItem(Item* item) override {
- value = item;
- }
+ void VisitItem(Item* item) override { value = item; }
};
template <typename T>
-const T* valueCast(const Value* value) {
- return valueCast<T>(const_cast<Value*>(value));
+const T* ValueCast(const Value* value) {
+ return ValueCast<T>(const_cast<Value*>(value));
}
/**
@@ -138,31 +135,33 @@
* Otherwise, returns nullptr.
*/
template <typename T>
-T* valueCast(Value* value) {
- if (!value) {
- return nullptr;
- }
- DynCastVisitor<T> visitor;
- value->accept(&visitor);
- return visitor.value;
+T* ValueCast(Value* value) {
+ if (!value) {
+ return nullptr;
+ }
+ DynCastVisitor<T> visitor;
+ value->Accept(&visitor);
+ return visitor.value;
}
-inline void visitAllValuesInPackage(ResourceTablePackage* pkg, RawValueVisitor* visitor) {
- for (auto& type : pkg->types) {
- for (auto& entry : type->entries) {
- for (auto& configValue : entry->values) {
- configValue->value->accept(visitor);
- }
- }
+inline void VisitAllValuesInPackage(ResourceTablePackage* pkg,
+ RawValueVisitor* visitor) {
+ for (auto& type : pkg->types) {
+ for (auto& entry : type->entries) {
+ for (auto& config_value : entry->values) {
+ config_value->value->Accept(visitor);
+ }
}
+ }
}
-inline void visitAllValuesInTable(ResourceTable* table, RawValueVisitor* visitor) {
- for (auto& pkg : table->packages) {
- visitAllValuesInPackage(pkg.get(), visitor);
- }
+inline void VisitAllValuesInTable(ResourceTable* table,
+ RawValueVisitor* visitor) {
+ for (auto& pkg : table->packages) {
+ VisitAllValuesInPackage(pkg.get(), visitor);
+ }
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_VALUE_VISITOR_H
+#endif // AAPT_VALUE_VISITOR_H
diff --git a/tools/aapt2/ValueVisitor_test.cpp b/tools/aapt2/ValueVisitor_test.cpp
index 54e9fcd..eb75b10 100644
--- a/tools/aapt2/ValueVisitor_test.cpp
+++ b/tools/aapt2/ValueVisitor_test.cpp
@@ -14,73 +14,74 @@
* limitations under the License.
*/
-#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "test/Test.h"
-#include "util/Util.h"
#include <string>
+#include "ResourceValues.h"
+#include "test/Test.h"
+#include "util/Util.h"
+
namespace aapt {
struct SingleReferenceVisitor : public ValueVisitor {
- using ValueVisitor::visit;
+ using ValueVisitor::Visit;
- Reference* visited = nullptr;
+ Reference* visited = nullptr;
- void visit(Reference* ref) override {
- visited = ref;
- }
+ void Visit(Reference* ref) override { visited = ref; }
};
struct StyleVisitor : public ValueVisitor {
- using ValueVisitor::visit;
+ using ValueVisitor::Visit;
- std::list<Reference*> visitedRefs;
- Style* visitedStyle = nullptr;
+ std::list<Reference*> visited_refs;
+ Style* visited_style = nullptr;
- void visit(Reference* ref) override {
- visitedRefs.push_back(ref);
- }
+ void Visit(Reference* ref) override { visited_refs.push_back(ref); }
- void visit(Style* style) override {
- visitedStyle = style;
- ValueVisitor::visit(style);
- }
+ void Visit(Style* style) override {
+ visited_style = style;
+ ValueVisitor::Visit(style);
+ }
};
TEST(ValueVisitorTest, VisitsReference) {
- Reference ref(ResourceName{"android", ResourceType::kAttr, "foo"});
- SingleReferenceVisitor visitor;
- ref.accept(&visitor);
+ Reference ref(ResourceName{"android", ResourceType::kAttr, "foo"});
+ SingleReferenceVisitor visitor;
+ ref.Accept(&visitor);
- EXPECT_EQ(visitor.visited, &ref);
+ EXPECT_EQ(visitor.visited, &ref);
}
TEST(ValueVisitorTest, VisitsReferencesInStyle) {
- std::unique_ptr<Style> style = test::StyleBuilder()
- .setParent("android:style/foo")
- .addItem("android:attr/one", test::buildReference("android:id/foo"))
- .build();
+ std::unique_ptr<Style> style =
+ test::StyleBuilder()
+ .SetParent("android:style/foo")
+ .AddItem("android:attr/one", test::BuildReference("android:id/foo"))
+ .Build();
- StyleVisitor visitor;
- style->accept(&visitor);
+ StyleVisitor visitor;
+ style->Accept(&visitor);
- ASSERT_EQ(style.get(), visitor.visitedStyle);
+ ASSERT_EQ(style.get(), visitor.visited_style);
- // Entry attribute references, plus the parent reference, plus one value reference.
- ASSERT_EQ(style->entries.size() + 2, visitor.visitedRefs.size());
+ // Entry attribute references, plus the parent reference, plus one value
+ // reference.
+ ASSERT_EQ(style->entries.size() + 2, visitor.visited_refs.size());
}
TEST(ValueVisitorTest, ValueCast) {
- std::unique_ptr<Reference> ref = test::buildReference("android:color/white");
- EXPECT_NE(valueCast<Reference>(ref.get()), nullptr);
+ std::unique_ptr<Reference> ref = test::BuildReference("android:color/white");
+ EXPECT_NE(ValueCast<Reference>(ref.get()), nullptr);
- std::unique_ptr<Style> style = test::StyleBuilder()
- .addItem("android:attr/foo", test::buildReference("android:color/black"))
- .build();
- EXPECT_NE(valueCast<Style>(style.get()), nullptr);
- EXPECT_EQ(valueCast<Reference>(style.get()), nullptr);
+ std::unique_ptr<Style> style =
+ test::StyleBuilder()
+ .AddItem("android:attr/foo",
+ test::BuildReference("android:color/black"))
+ .Build();
+ EXPECT_NE(ValueCast<Style>(style.get()), nullptr);
+ EXPECT_EQ(ValueCast<Reference>(style.get()), nullptr);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index dbd8062..a06140c 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+#include <dirent.h>
+
+#include <fstream>
+#include <string>
+
#include "ConfigDescription.h"
#include "Diagnostics.h"
#include "Flags.h"
@@ -33,14 +38,10 @@
#include "xml/XmlDom.h"
#include "xml/XmlPullParser.h"
-#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
-#include <google/protobuf/io/coded_stream.h>
-
-#include <android-base/errors.h>
-#include <android-base/file.h>
-#include <dirent.h>
-#include <fstream>
-#include <string>
+#include "android-base/errors.h"
+#include "android-base/file.h"
+#include "google/protobuf/io/coded_stream.h"
+#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
using google::protobuf::io::CopyingOutputStreamAdaptor;
using google::protobuf::io::ZeroCopyOutputStream;
@@ -48,714 +49,758 @@
namespace aapt {
struct ResourcePathData {
- Source source;
- std::string resourceDir;
- std::string name;
- std::string extension;
+ Source source;
+ std::string resource_dir;
+ std::string name;
+ std::string extension;
- // Original config str. We keep this because when we parse the config, we may add on
- // version qualifiers. We want to preserve the original input so the output is easily
- // computed before hand.
- std::string configStr;
- ConfigDescription config;
+ // Original config str. We keep this because when we parse the config, we may
+ // add on
+ // version qualifiers. We want to preserve the original input so the output is
+ // easily
+ // computed before hand.
+ std::string config_str;
+ ConfigDescription config;
};
/**
* Resource file paths are expected to look like:
* [--/res/]type[-config]/name
*/
-static Maybe<ResourcePathData> extractResourcePathData(const std::string& path,
- std::string* outError) {
- std::vector<std::string> parts = util::split(path, file::sDirSep);
- if (parts.size() < 2) {
- if (outError) *outError = "bad resource path";
- return {};
+static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
+ std::string* out_error) {
+ std::vector<std::string> parts = util::Split(path, file::sDirSep);
+ if (parts.size() < 2) {
+ if (out_error) *out_error = "bad resource path";
+ return {};
+ }
+
+ std::string& dir = parts[parts.size() - 2];
+ StringPiece dir_str = dir;
+
+ StringPiece config_str;
+ ConfigDescription config;
+ size_t dash_pos = dir.find('-');
+ if (dash_pos != std::string::npos) {
+ config_str = dir_str.substr(dash_pos + 1, dir.size() - (dash_pos + 1));
+ if (!ConfigDescription::Parse(config_str, &config)) {
+ if (out_error) {
+ std::stringstream err_str;
+ err_str << "invalid configuration '" << config_str << "'";
+ *out_error = err_str.str();
+ }
+ return {};
}
+ dir_str = dir_str.substr(0, dash_pos);
+ }
- std::string& dir = parts[parts.size() - 2];
- StringPiece dirStr = dir;
+ std::string& filename = parts[parts.size() - 1];
+ StringPiece name = filename;
+ StringPiece extension;
+ size_t dot_pos = filename.find('.');
+ if (dot_pos != std::string::npos) {
+ extension = name.substr(dot_pos + 1, filename.size() - (dot_pos + 1));
+ name = name.substr(0, dot_pos);
+ }
- StringPiece configStr;
- ConfigDescription config;
- size_t dashPos = dir.find('-');
- if (dashPos != std::string::npos) {
- configStr = dirStr.substr(dashPos + 1, dir.size() - (dashPos + 1));
- if (!ConfigDescription::parse(configStr, &config)) {
- if (outError) {
- std::stringstream errStr;
- errStr << "invalid configuration '" << configStr << "'";
- *outError = errStr.str();
- }
- return {};
- }
- dirStr = dirStr.substr(0, dashPos);
- }
-
- std::string& filename = parts[parts.size() - 1];
- StringPiece name = filename;
- StringPiece extension;
- size_t dotPos = filename.find('.');
- if (dotPos != std::string::npos) {
- extension = name.substr(dotPos + 1, filename.size() - (dotPos + 1));
- name = name.substr(0, dotPos);
- }
-
- return ResourcePathData{
- Source(path),
- dirStr.toString(),
- name.toString(),
- extension.toString(),
- configStr.toString(),
- config
- };
+ return ResourcePathData{Source(path), dir_str.ToString(),
+ name.ToString(), extension.ToString(),
+ config_str.ToString(), config};
}
struct CompileOptions {
- std::string outputPath;
- Maybe<std::string> resDir;
- bool pseudolocalize = false;
- bool legacyMode = false;
- bool verbose = false;
+ std::string output_path;
+ Maybe<std::string> res_dir;
+ bool pseudolocalize = false;
+ bool legacy_mode = false;
+ bool verbose = false;
};
-static std::string buildIntermediateFilename(const ResourcePathData& data) {
- std::stringstream name;
- name << data.resourceDir;
- if (!data.configStr.empty()) {
- name << "-" << data.configStr;
- }
- name << "_" << data.name;
- if (!data.extension.empty()) {
- name << "." << data.extension;
- }
- name << ".flat";
- return name.str();
+static std::string BuildIntermediateFilename(const ResourcePathData& data) {
+ std::stringstream name;
+ name << data.resource_dir;
+ if (!data.config_str.empty()) {
+ name << "-" << data.config_str;
+ }
+ name << "_" << data.name;
+ if (!data.extension.empty()) {
+ name << "." << data.extension;
+ }
+ name << ".flat";
+ return name.str();
}
-static bool isHidden(const StringPiece& filename) {
- return util::stringStartsWith(filename, ".");
+static bool IsHidden(const StringPiece& filename) {
+ return util::StartsWith(filename, ".");
}
/**
* Walks the res directory structure, looking for resource files.
*/
-static bool loadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
- std::vector<ResourcePathData>* outPathData) {
- const std::string& rootDir = options.resDir.value();
- std::unique_ptr<DIR, decltype(closedir)*> d(opendir(rootDir.data()), closedir);
- if (!d) {
- context->getDiagnostics()->error(DiagMessage() << strerror(errno));
+static bool LoadInputFilesFromDir(
+ IAaptContext* context, const CompileOptions& options,
+ std::vector<ResourcePathData>* out_path_data) {
+ const std::string& root_dir = options.res_dir.value();
+ std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()),
+ closedir);
+ if (!d) {
+ context->GetDiagnostics()->Error(DiagMessage() << strerror(errno));
+ return false;
+ }
+
+ while (struct dirent* entry = readdir(d.get())) {
+ if (IsHidden(entry->d_name)) {
+ continue;
+ }
+
+ std::string prefix_path = root_dir;
+ file::AppendPath(&prefix_path, entry->d_name);
+
+ if (file::GetFileType(prefix_path) != file::FileType::kDirectory) {
+ continue;
+ }
+
+ std::unique_ptr<DIR, decltype(closedir)*> subdir(
+ opendir(prefix_path.data()), closedir);
+ if (!subdir) {
+ context->GetDiagnostics()->Error(DiagMessage() << strerror(errno));
+ return false;
+ }
+
+ while (struct dirent* leaf_entry = readdir(subdir.get())) {
+ if (IsHidden(leaf_entry->d_name)) {
+ continue;
+ }
+
+ std::string full_path = prefix_path;
+ file::AppendPath(&full_path, leaf_entry->d_name);
+
+ std::string err_str;
+ Maybe<ResourcePathData> path_data =
+ ExtractResourcePathData(full_path, &err_str);
+ if (!path_data) {
+ context->GetDiagnostics()->Error(DiagMessage() << err_str);
return false;
+ }
+
+ out_path_data->push_back(std::move(path_data.value()));
}
-
- while (struct dirent* entry = readdir(d.get())) {
- if (isHidden(entry->d_name)) {
- continue;
- }
-
- std::string prefixPath = rootDir;
- file::appendPath(&prefixPath, entry->d_name);
-
- if (file::getFileType(prefixPath) != file::FileType::kDirectory) {
- continue;
- }
-
- std::unique_ptr<DIR, decltype(closedir)*> subDir(opendir(prefixPath.data()), closedir);
- if (!subDir) {
- context->getDiagnostics()->error(DiagMessage() << strerror(errno));
- return false;
- }
-
- while (struct dirent* leafEntry = readdir(subDir.get())) {
- if (isHidden(leafEntry->d_name)) {
- continue;
- }
-
- std::string fullPath = prefixPath;
- file::appendPath(&fullPath, leafEntry->d_name);
-
- std::string errStr;
- Maybe<ResourcePathData> pathData = extractResourcePathData(fullPath, &errStr);
- if (!pathData) {
- context->getDiagnostics()->error(DiagMessage() << errStr);
- return false;
- }
-
- outPathData->push_back(std::move(pathData.value()));
- }
- }
- return true;
+ }
+ return true;
}
-static bool compileTable(IAaptContext* context, const CompileOptions& options,
- const ResourcePathData& pathData, IArchiveWriter* writer,
- const std::string& outputPath) {
- ResourceTable table;
- {
- std::ifstream fin(pathData.source.path, std::ifstream::binary);
- if (!fin) {
- context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
- return false;
- }
-
-
- // Parse the values file from XML.
- xml::XmlPullParser xmlParser(fin);
-
- ResourceParserOptions parserOptions;
- parserOptions.errorOnPositionalArguments = !options.legacyMode;
-
- // If the filename includes donottranslate, then the default translatable is false.
- parserOptions.translatable = pathData.name.find("donottranslate") == std::string::npos;
-
- ResourceParser resParser(context->getDiagnostics(), &table, pathData.source,
- pathData.config, parserOptions);
- if (!resParser.parse(&xmlParser)) {
- return false;
- }
-
- fin.close();
+static bool CompileTable(IAaptContext* context, const CompileOptions& options,
+ const ResourcePathData& path_data,
+ IArchiveWriter* writer,
+ const std::string& output_path) {
+ ResourceTable table;
+ {
+ std::ifstream fin(path_data.source.path, std::ifstream::binary);
+ if (!fin) {
+ context->GetDiagnostics()->Error(DiagMessage(path_data.source)
+ << strerror(errno));
+ return false;
}
- if (options.pseudolocalize) {
- // Generate pseudo-localized strings (en-XA and ar-XB).
- // These are created as weak symbols, and are only generated from default configuration
- // strings and plurals.
- PseudolocaleGenerator pseudolocaleGenerator;
- if (!pseudolocaleGenerator.consume(context, &table)) {
- return false;
- }
+ // Parse the values file from XML.
+ xml::XmlPullParser xml_parser(fin);
+
+ ResourceParserOptions parser_options;
+ parser_options.error_on_positional_arguments = !options.legacy_mode;
+
+ // If the filename includes donottranslate, then the default translatable is
+ // false.
+ parser_options.translatable =
+ path_data.name.find("donottranslate") == std::string::npos;
+
+ ResourceParser res_parser(context->GetDiagnostics(), &table,
+ path_data.source, path_data.config,
+ parser_options);
+ if (!res_parser.Parse(&xml_parser)) {
+ return false;
}
- // Ensure we have the compilation package at least.
- table.createPackage(context->getCompilationPackage());
+ fin.close();
+ }
- // Assign an ID to any package that has resources.
- for (auto& pkg : table.packages) {
- if (!pkg->id) {
- // If no package ID was set while parsing (public identifiers), auto assign an ID.
- pkg->id = context->getPackageId();
- }
+ if (options.pseudolocalize) {
+ // Generate pseudo-localized strings (en-XA and ar-XB).
+ // These are created as weak symbols, and are only generated from default
+ // configuration
+ // strings and plurals.
+ PseudolocaleGenerator pseudolocale_generator;
+ if (!pseudolocale_generator.Consume(context, &table)) {
+ return false;
}
+ }
- // Create the file/zip entry.
- if (!writer->startEntry(outputPath, 0)) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
- return false;
+ // Ensure we have the compilation package at least.
+ table.CreatePackage(context->GetCompilationPackage());
+
+ // Assign an ID to any package that has resources.
+ for (auto& pkg : table.packages) {
+ if (!pkg->id) {
+ // If no package ID was set while parsing (public identifiers), auto
+ // assign an ID.
+ pkg->id = context->GetPackageId();
}
+ }
- // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->finishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
- // interface.
- CopyingOutputStreamAdaptor copyingAdaptor(writer);
+ // Create the file/zip entry.
+ if (!writer->StartEntry(output_path, 0)) {
+ context->GetDiagnostics()->Error(DiagMessage(output_path)
+ << "failed to open");
+ return false;
+ }
- std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(&table);
- if (!pbTable->SerializeToZeroCopyStream(©ingAdaptor)) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
- return false;
- }
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call
+ // writer->FinishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the
+ // ZeroCopyOutputStream
+ // interface.
+ CopyingOutputStreamAdaptor copying_adaptor(writer);
+
+ std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(&table);
+ if (!pb_table->SerializeToZeroCopyStream(©ing_adaptor)) {
+ context->GetDiagnostics()->Error(DiagMessage(output_path)
+ << "failed to write");
+ return false;
}
+ }
- if (!writer->finishEntry()) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to finish entry");
- return false;
- }
- return true;
+ if (!writer->FinishEntry()) {
+ context->GetDiagnostics()->Error(DiagMessage(output_path)
+ << "failed to finish entry");
+ return false;
+ }
+ return true;
}
-static bool writeHeaderAndBufferToWriter(const StringPiece& outputPath, const ResourceFile& file,
- const BigBuffer& buffer, IArchiveWriter* writer,
+static bool WriteHeaderAndBufferToWriter(const StringPiece& output_path,
+ const ResourceFile& file,
+ const BigBuffer& buffer,
+ IArchiveWriter* writer,
IDiagnostics* diag) {
- // Start the entry so we can write the header.
- if (!writer->startEntry(outputPath, 0)) {
- diag->error(DiagMessage(outputPath) << "failed to open file");
- return false;
+ // Start the entry so we can write the header.
+ if (!writer->StartEntry(output_path, 0)) {
+ diag->Error(DiagMessage(output_path) << "failed to open file");
+ return false;
+ }
+
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call
+ // writer->FinishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the
+ // ZeroCopyOutputStream
+ // interface.
+ CopyingOutputStreamAdaptor copying_adaptor(writer);
+ CompiledFileOutputStream output_stream(©ing_adaptor);
+
+ // Number of CompiledFiles.
+ output_stream.WriteLittleEndian32(1);
+
+ std::unique_ptr<pb::CompiledFile> compiled_file =
+ SerializeCompiledFileToPb(file);
+ output_stream.WriteCompiledFile(compiled_file.get());
+ output_stream.WriteData(&buffer);
+
+ if (output_stream.HadError()) {
+ diag->Error(DiagMessage(output_path) << "failed to write data");
+ return false;
}
+ }
- // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->finishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
- // interface.
- CopyingOutputStreamAdaptor copyingAdaptor(writer);
- CompiledFileOutputStream outputStream(©ingAdaptor);
-
- // Number of CompiledFiles.
- outputStream.WriteLittleEndian32(1);
-
- std::unique_ptr<pb::CompiledFile> compiledFile = serializeCompiledFileToPb(file);
- outputStream.WriteCompiledFile(compiledFile.get());
- outputStream.WriteData(&buffer);
-
- if (outputStream.HadError()) {
- diag->error(DiagMessage(outputPath) << "failed to write data");
- return false;
- }
- }
-
- if (!writer->finishEntry()) {
- diag->error(DiagMessage(outputPath) << "failed to finish writing data");
- return false;
- }
- return true;
+ if (!writer->FinishEntry()) {
+ diag->Error(DiagMessage(output_path) << "failed to finish writing data");
+ return false;
+ }
+ return true;
}
-static bool writeHeaderAndMmapToWriter(const StringPiece& outputPath, const ResourceFile& file,
- const android::FileMap& map, IArchiveWriter* writer,
+static bool WriteHeaderAndMmapToWriter(const StringPiece& output_path,
+ const ResourceFile& file,
+ const android::FileMap& map,
+ IArchiveWriter* writer,
IDiagnostics* diag) {
- // Start the entry so we can write the header.
- if (!writer->startEntry(outputPath, 0)) {
- diag->error(DiagMessage(outputPath) << "failed to open file");
- return false;
+ // Start the entry so we can write the header.
+ if (!writer->StartEntry(output_path, 0)) {
+ diag->Error(DiagMessage(output_path) << "failed to open file");
+ return false;
+ }
+
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call
+ // writer->FinishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the
+ // ZeroCopyOutputStream interface.
+ CopyingOutputStreamAdaptor copying_adaptor(writer);
+ CompiledFileOutputStream output_stream(©ing_adaptor);
+
+ // Number of CompiledFiles.
+ output_stream.WriteLittleEndian32(1);
+
+ std::unique_ptr<pb::CompiledFile> compiled_file =
+ SerializeCompiledFileToPb(file);
+ output_stream.WriteCompiledFile(compiled_file.get());
+ output_stream.WriteData(map.getDataPtr(), map.getDataLength());
+
+ if (output_stream.HadError()) {
+ diag->Error(DiagMessage(output_path) << "failed to write data");
+ return false;
}
+ }
- // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->finishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
- // interface.
- CopyingOutputStreamAdaptor copyingAdaptor(writer);
- CompiledFileOutputStream outputStream(©ingAdaptor);
-
- // Number of CompiledFiles.
- outputStream.WriteLittleEndian32(1);
-
- std::unique_ptr<pb::CompiledFile> compiledFile = serializeCompiledFileToPb(file);
- outputStream.WriteCompiledFile(compiledFile.get());
- outputStream.WriteData(map.getDataPtr(), map.getDataLength());
-
- if (outputStream.HadError()) {
- diag->error(DiagMessage(outputPath) << "failed to write data");
- return false;
- }
- }
-
- if (!writer->finishEntry()) {
- diag->error(DiagMessage(outputPath) << "failed to finish writing data");
- return false;
- }
- return true;
+ if (!writer->FinishEntry()) {
+ diag->Error(DiagMessage(output_path) << "failed to finish writing data");
+ return false;
+ }
+ return true;
}
-static bool flattenXmlToOutStream(IAaptContext* context, const StringPiece& outputPath,
- xml::XmlResource* xmlRes,
+static bool FlattenXmlToOutStream(IAaptContext* context,
+ const StringPiece& output_path,
+ xml::XmlResource* xmlres,
CompiledFileOutputStream* out) {
- BigBuffer buffer(1024);
- XmlFlattenerOptions xmlFlattenerOptions;
- xmlFlattenerOptions.keepRawValues = true;
- XmlFlattener flattener(&buffer, xmlFlattenerOptions);
- if (!flattener.consume(context, xmlRes)) {
- return false;
- }
+ BigBuffer buffer(1024);
+ XmlFlattenerOptions xml_flattener_options;
+ xml_flattener_options.keep_raw_values = true;
+ XmlFlattener flattener(&buffer, xml_flattener_options);
+ if (!flattener.Consume(context, xmlres)) {
+ return false;
+ }
- std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(xmlRes->file);
- out->WriteCompiledFile(pbCompiledFile.get());
- out->WriteData(&buffer);
+ std::unique_ptr<pb::CompiledFile> pb_compiled_file =
+ SerializeCompiledFileToPb(xmlres->file);
+ out->WriteCompiledFile(pb_compiled_file.get());
+ out->WriteData(&buffer);
- if (out->HadError()) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write data");
- return false;
- }
- return true;
+ if (out->HadError()) {
+ context->GetDiagnostics()->Error(DiagMessage(output_path)
+ << "failed to write data");
+ return false;
+ }
+ return true;
}
-static bool compileXml(IAaptContext* context, const CompileOptions& options,
- const ResourcePathData& pathData, IArchiveWriter* writer,
- const std::string& outputPath) {
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling XML");
+static bool CompileXml(IAaptContext* context, const CompileOptions& options,
+ const ResourcePathData& path_data,
+ IArchiveWriter* writer, const std::string& output_path) {
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(DiagMessage(path_data.source)
+ << "compiling XML");
+ }
+
+ std::unique_ptr<xml::XmlResource> xmlres;
+ {
+ std::ifstream fin(path_data.source.path, std::ifstream::binary);
+ if (!fin) {
+ context->GetDiagnostics()->Error(DiagMessage(path_data.source)
+ << strerror(errno));
+ return false;
}
- std::unique_ptr<xml::XmlResource> xmlRes;
- {
- std::ifstream fin(pathData.source.path, std::ifstream::binary);
- if (!fin) {
- context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
- return false;
- }
+ xmlres = xml::Inflate(&fin, context->GetDiagnostics(), path_data.source);
- xmlRes = xml::inflate(&fin, context->getDiagnostics(), pathData.source);
+ fin.close();
+ }
- fin.close();
+ if (!xmlres) {
+ return false;
+ }
+
+ xmlres->file.name = ResourceName(
+ {}, *ParseResourceType(path_data.resource_dir), path_data.name);
+ xmlres->file.config = path_data.config;
+ xmlres->file.source = path_data.source;
+
+ // Collect IDs that are defined here.
+ XmlIdCollector collector;
+ if (!collector.Consume(context, xmlres.get())) {
+ return false;
+ }
+
+ // Look for and process any <aapt:attr> tags and create sub-documents.
+ InlineXmlFormatParser inline_xml_format_parser;
+ if (!inline_xml_format_parser.Consume(context, xmlres.get())) {
+ return false;
+ }
+
+ // Start the entry so we can write the header.
+ if (!writer->StartEntry(output_path, 0)) {
+ context->GetDiagnostics()->Error(DiagMessage(output_path)
+ << "failed to open file");
+ return false;
+ }
+
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call
+ // writer->FinishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the
+ // ZeroCopyOutputStream
+ // interface.
+ CopyingOutputStreamAdaptor copying_adaptor(writer);
+ CompiledFileOutputStream output_stream(©ing_adaptor);
+
+ std::vector<std::unique_ptr<xml::XmlResource>>& inline_documents =
+ inline_xml_format_parser.GetExtractedInlineXmlDocuments();
+
+ // Number of CompiledFiles.
+ output_stream.WriteLittleEndian32(1 + inline_documents.size());
+
+ if (!FlattenXmlToOutStream(context, output_path, xmlres.get(),
+ &output_stream)) {
+ return false;
}
- if (!xmlRes) {
+ for (auto& inline_xml_doc : inline_documents) {
+ if (!FlattenXmlToOutStream(context, output_path, inline_xml_doc.get(),
+ &output_stream)) {
return false;
+ }
}
+ }
- xmlRes->file.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
- xmlRes->file.config = pathData.config;
- xmlRes->file.source = pathData.source;
-
- // Collect IDs that are defined here.
- XmlIdCollector collector;
- if (!collector.consume(context, xmlRes.get())) {
- return false;
- }
-
- // Look for and process any <aapt:attr> tags and create sub-documents.
- InlineXmlFormatParser inlineXmlFormatParser;
- if (!inlineXmlFormatParser.consume(context, xmlRes.get())) {
- return false;
- }
-
- // Start the entry so we can write the header.
- if (!writer->startEntry(outputPath, 0)) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open file");
- return false;
- }
-
- // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->finishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
- // interface.
- CopyingOutputStreamAdaptor copyingAdaptor(writer);
- CompiledFileOutputStream outputStream(©ingAdaptor);
-
- std::vector<std::unique_ptr<xml::XmlResource>>& inlineDocuments =
- inlineXmlFormatParser.getExtractedInlineXmlDocuments();
-
- // Number of CompiledFiles.
- outputStream.WriteLittleEndian32(1 + inlineDocuments.size());
-
- if (!flattenXmlToOutStream(context, outputPath, xmlRes.get(), &outputStream)) {
- return false;
- }
-
- for (auto& inlineXmlDoc : inlineDocuments) {
- if (!flattenXmlToOutStream(context, outputPath, inlineXmlDoc.get(), &outputStream)) {
- return false;
- }
- }
- }
-
- if (!writer->finishEntry()) {
- context->getDiagnostics()->error(DiagMessage(outputPath)
- << "failed to finish writing data");
- return false;
- }
- return true;
+ if (!writer->FinishEntry()) {
+ context->GetDiagnostics()->Error(DiagMessage(output_path)
+ << "failed to finish writing data");
+ return false;
+ }
+ return true;
}
class BigBufferOutputStream : public io::OutputStream {
-public:
- explicit BigBufferOutputStream(BigBuffer* buffer) : mBuffer(buffer) {
- }
+ public:
+ explicit BigBufferOutputStream(BigBuffer* buffer) : buffer_(buffer) {}
- bool Next(void** data, int* len) override {
- size_t count;
- *data = mBuffer->nextBlock(&count);
- *len = static_cast<int>(count);
- return true;
- }
+ bool Next(void** data, int* len) override {
+ size_t count;
+ *data = buffer_->NextBlock(&count);
+ *len = static_cast<int>(count);
+ return true;
+ }
- void BackUp(int count) override {
- mBuffer->backUp(count);
- }
+ void BackUp(int count) override { buffer_->BackUp(count); }
- int64_t ByteCount() const override {
- return mBuffer->size();
- }
+ int64_t ByteCount() const override { return buffer_->size(); }
- bool HadError() const override {
- return false;
- }
+ bool HadError() const override { return false; }
-private:
- BigBuffer* mBuffer;
+ private:
+ BigBuffer* buffer_;
- DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
+ DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
};
-static bool compilePng(IAaptContext* context, const CompileOptions& options,
- const ResourcePathData& pathData, IArchiveWriter* writer,
- const std::string& outputPath) {
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling PNG");
+static bool CompilePng(IAaptContext* context, const CompileOptions& options,
+ const ResourcePathData& path_data,
+ IArchiveWriter* writer, const std::string& output_path) {
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(DiagMessage(path_data.source)
+ << "compiling PNG");
+ }
+
+ BigBuffer buffer(4096);
+ ResourceFile res_file;
+ res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir),
+ path_data.name);
+ res_file.config = path_data.config;
+ res_file.source = path_data.source;
+
+ {
+ std::string content;
+ if (!android::base::ReadFileToString(path_data.source.path, &content)) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(path_data.source)
+ << android::base::SystemErrorCodeToString(errno));
+ return false;
}
- BigBuffer buffer(4096);
- ResourceFile resFile;
- resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
- resFile.config = pathData.config;
- resFile.source = pathData.source;
+ BigBuffer crunched_png_buffer(4096);
+ BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
- {
- std::string content;
- if (!android::base::ReadFileToString(pathData.source.path, &content)) {
- context->getDiagnostics()->error(DiagMessage(pathData.source)
- << android::base::SystemErrorCodeToString(errno));
- return false;
- }
-
- BigBuffer crunchedPngBuffer(4096);
- BigBufferOutputStream crunchedPngBufferOut(&crunchedPngBuffer);
-
- // Ensure that we only keep the chunks we care about if we end up
- // using the original PNG instead of the crunched one.
- PngChunkFilter pngChunkFilter(content);
- std::unique_ptr<Image> image = readPng(context, &pngChunkFilter);
- if (!image) {
- return false;
- }
-
- std::unique_ptr<NinePatch> ninePatch;
- if (pathData.extension == "9.png") {
- std::string err;
- ninePatch = NinePatch::create(image->rows.get(), image->width, image->height, &err);
- if (!ninePatch) {
- context->getDiagnostics()->error(DiagMessage() << err);
- return false;
- }
-
- // Remove the 1px border around the NinePatch.
- // Basically the row array is shifted up by 1, and the length is treated
- // as height - 2.
- // For each row, shift the array to the left by 1, and treat the length as width - 2.
- image->width -= 2;
- image->height -= 2;
- memmove(image->rows.get(), image->rows.get() + 1, image->height * sizeof(uint8_t**));
- for (int32_t h = 0; h < image->height; h++) {
- memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
- }
-
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage(pathData.source)
- << "9-patch: " << *ninePatch);
- }
- }
-
- // Write the crunched PNG.
- if (!writePng(context, image.get(), ninePatch.get(), &crunchedPngBufferOut, {})) {
- return false;
- }
-
- if (ninePatch != nullptr
- || crunchedPngBufferOut.ByteCount() <= pngChunkFilter.ByteCount()) {
- // No matter what, we must use the re-encoded PNG, even if it is larger.
- // 9-patch images must be re-encoded since their borders are stripped.
- buffer.appendBuffer(std::move(crunchedPngBuffer));
- } else {
- // The re-encoded PNG is larger than the original, and there is
- // no mandatory transformation. Use the original.
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage(pathData.source)
- << "original PNG is smaller than crunched PNG"
- << ", using original");
- }
-
- PngChunkFilter pngChunkFilterAgain(content);
- BigBuffer filteredPngBuffer(4096);
- BigBufferOutputStream filteredPngBufferOut(&filteredPngBuffer);
- io::copy(&filteredPngBufferOut, &pngChunkFilterAgain);
- buffer.appendBuffer(std::move(filteredPngBuffer));
- }
-
- if (context->verbose()) {
- // For debugging only, use the legacy PNG cruncher and compare the resulting file sizes.
- // This will help catch exotic cases where the new code may generate larger PNGs.
- std::stringstream legacyStream(content);
- BigBuffer legacyBuffer(4096);
- Png png(context->getDiagnostics());
- if (!png.process(pathData.source, &legacyStream, &legacyBuffer, {})) {
- return false;
- }
-
- context->getDiagnostics()->note(DiagMessage(pathData.source)
- << "legacy=" << legacyBuffer.size()
- << " new=" << buffer.size());
- }
+ // Ensure that we only keep the chunks we care about if we end up
+ // using the original PNG instead of the crunched one.
+ PngChunkFilter png_chunk_filter(content);
+ std::unique_ptr<Image> image = ReadPng(context, &png_chunk_filter);
+ if (!image) {
+ return false;
}
- if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
- context->getDiagnostics())) {
+ std::unique_ptr<NinePatch> nine_patch;
+ if (path_data.extension == "9.png") {
+ std::string err;
+ nine_patch = NinePatch::Create(image->rows.get(), image->width,
+ image->height, &err);
+ if (!nine_patch) {
+ context->GetDiagnostics()->Error(DiagMessage() << err);
return false;
+ }
+
+ // Remove the 1px border around the NinePatch.
+ // Basically the row array is shifted up by 1, and the length is treated
+ // as height - 2.
+ // For each row, shift the array to the left by 1, and treat the length as
+ // width - 2.
+ image->width -= 2;
+ image->height -= 2;
+ memmove(image->rows.get(), image->rows.get() + 1,
+ image->height * sizeof(uint8_t**));
+ for (int32_t h = 0; h < image->height; h++) {
+ memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
+ }
+
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(DiagMessage(path_data.source)
+ << "9-patch: " << *nine_patch);
+ }
}
- return true;
+
+ // Write the crunched PNG.
+ if (!WritePng(context, image.get(), nine_patch.get(),
+ &crunched_png_buffer_out, {})) {
+ return false;
+ }
+
+ if (nine_patch != nullptr ||
+ crunched_png_buffer_out.ByteCount() <= png_chunk_filter.ByteCount()) {
+ // No matter what, we must use the re-encoded PNG, even if it is larger.
+ // 9-patch images must be re-encoded since their borders are stripped.
+ buffer.AppendBuffer(std::move(crunched_png_buffer));
+ } else {
+ // The re-encoded PNG is larger than the original, and there is
+ // no mandatory transformation. Use the original.
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(
+ DiagMessage(path_data.source)
+ << "original PNG is smaller than crunched PNG"
+ << ", using original");
+ }
+
+ PngChunkFilter png_chunk_filter_again(content);
+ BigBuffer filtered_png_buffer(4096);
+ BigBufferOutputStream filtered_png_buffer_out(&filtered_png_buffer);
+ io::Copy(&filtered_png_buffer_out, &png_chunk_filter_again);
+ buffer.AppendBuffer(std::move(filtered_png_buffer));
+ }
+
+ if (context->IsVerbose()) {
+ // For debugging only, use the legacy PNG cruncher and compare the
+ // resulting file sizes.
+ // This will help catch exotic cases where the new code may generate
+ // larger PNGs.
+ std::stringstream legacy_stream(content);
+ BigBuffer legacy_buffer(4096);
+ Png png(context->GetDiagnostics());
+ if (!png.process(path_data.source, &legacy_stream, &legacy_buffer, {})) {
+ return false;
+ }
+
+ context->GetDiagnostics()->Note(DiagMessage(path_data.source)
+ << "legacy=" << legacy_buffer.size()
+ << " new=" << buffer.size());
+ }
+ }
+
+ if (!WriteHeaderAndBufferToWriter(output_path, res_file, buffer, writer,
+ context->GetDiagnostics())) {
+ return false;
+ }
+ return true;
}
-static bool compileFile(IAaptContext* context, const CompileOptions& options,
- const ResourcePathData& pathData, IArchiveWriter* writer,
- const std::string& outputPath) {
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling file");
- }
+static bool CompileFile(IAaptContext* context, const CompileOptions& options,
+ const ResourcePathData& path_data,
+ IArchiveWriter* writer,
+ const std::string& output_path) {
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(DiagMessage(path_data.source)
+ << "compiling file");
+ }
- BigBuffer buffer(256);
- ResourceFile resFile;
- resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
- resFile.config = pathData.config;
- resFile.source = pathData.source;
+ BigBuffer buffer(256);
+ ResourceFile res_file;
+ res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir),
+ path_data.name);
+ res_file.config = path_data.config;
+ res_file.source = path_data.source;
- std::string errorStr;
- Maybe<android::FileMap> f = file::mmapPath(pathData.source.path, &errorStr);
- if (!f) {
- context->getDiagnostics()->error(DiagMessage(pathData.source) << errorStr);
- return false;
- }
+ std::string error_str;
+ Maybe<android::FileMap> f = file::MmapPath(path_data.source.path, &error_str);
+ if (!f) {
+ context->GetDiagnostics()->Error(DiagMessage(path_data.source)
+ << error_str);
+ return false;
+ }
- if (!writeHeaderAndMmapToWriter(outputPath, resFile, f.value(), writer,
- context->getDiagnostics())) {
- return false;
- }
- return true;
+ if (!WriteHeaderAndMmapToWriter(output_path, res_file, f.value(), writer,
+ context->GetDiagnostics())) {
+ return false;
+ }
+ return true;
}
class CompileContext : public IAaptContext {
-public:
- void setVerbose(bool val) {
- mVerbose = val;
- }
+ public:
+ void SetVerbose(bool val) { verbose_ = val; }
- bool verbose() override {
- return mVerbose;
- }
+ bool IsVerbose() override { return verbose_; }
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ IDiagnostics* GetDiagnostics() override { return &diagnostics_; }
- NameMangler* getNameMangler() override {
- abort();
- return nullptr;
- }
+ NameMangler* GetNameMangler() override {
+ abort();
+ return nullptr;
+ }
- const std::string& getCompilationPackage() override {
- static std::string empty;
- return empty;
- }
+ const std::string& GetCompilationPackage() override {
+ static std::string empty;
+ return empty;
+ }
- uint8_t getPackageId() override {
- return 0x0;
- }
+ uint8_t GetPackageId() override { return 0x0; }
- SymbolTable* getExternalSymbols() override {
- abort();
- return nullptr;
- }
+ SymbolTable* GetExternalSymbols() override {
+ abort();
+ return nullptr;
+ }
- int getMinSdkVersion() override {
- return 0;
- }
+ int GetMinSdkVersion() override { return 0; }
-private:
- StdErrDiagnostics mDiagnostics;
- bool mVerbose = false;
-
+ private:
+ StdErrDiagnostics diagnostics_;
+ bool verbose_ = false;
};
/**
- * Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
+ * Entry point for compilation phase. Parses arguments and dispatches to the
+ * correct steps.
*/
-int compile(const std::vector<StringPiece>& args) {
- CompileContext context;
- CompileOptions options;
+int Compile(const std::vector<StringPiece>& args) {
+ CompileContext context;
+ CompileOptions options;
- bool verbose = false;
- Flags flags = Flags()
- .requiredFlag("-o", "Output path", &options.outputPath)
- .optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
- .optionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
- "(en-XA and ar-XB)", &options.pseudolocalize)
- .optionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
- &options.legacyMode)
- .optionalSwitch("-v", "Enables verbose logging", &verbose);
- if (!flags.parse("aapt2 compile", args, &std::cerr)) {
- return 1;
+ bool verbose = false;
+ Flags flags =
+ Flags()
+ .RequiredFlag("-o", "Output path", &options.output_path)
+ .OptionalFlag("--dir", "Directory to scan for resources",
+ &options.res_dir)
+ .OptionalSwitch("--pseudo-localize",
+ "Generate resources for pseudo-locales "
+ "(en-XA and ar-XB)",
+ &options.pseudolocalize)
+ .OptionalSwitch(
+ "--legacy",
+ "Treat errors that used to be valid in AAPT as warnings",
+ &options.legacy_mode)
+ .OptionalSwitch("-v", "Enables verbose logging", &verbose);
+ if (!flags.Parse("aapt2 compile", args, &std::cerr)) {
+ return 1;
+ }
+
+ context.SetVerbose(verbose);
+
+ std::unique_ptr<IArchiveWriter> archive_writer;
+
+ std::vector<ResourcePathData> input_data;
+ if (options.res_dir) {
+ if (!flags.GetArgs().empty()) {
+ // Can't have both files and a resource directory.
+ context.GetDiagnostics()->Error(DiagMessage()
+ << "files given but --dir specified");
+ flags.Usage("aapt2 compile", &std::cerr);
+ return 1;
}
- context.setVerbose(verbose);
+ if (!LoadInputFilesFromDir(&context, options, &input_data)) {
+ return 1;
+ }
- std::unique_ptr<IArchiveWriter> archiveWriter;
+ archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(),
+ options.output_path);
- std::vector<ResourcePathData> inputData;
- if (options.resDir) {
- if (!flags.getArgs().empty()) {
- // Can't have both files and a resource directory.
- context.getDiagnostics()->error(DiagMessage() << "files given but --dir specified");
- flags.usage("aapt2 compile", &std::cerr);
- return 1;
- }
+ } else {
+ input_data.reserve(flags.GetArgs().size());
- if (!loadInputFilesFromDir(&context, options, &inputData)) {
- return 1;
- }
+ // Collect data from the path for each input file.
+ for (const std::string& arg : flags.GetArgs()) {
+ std::string error_str;
+ if (Maybe<ResourcePathData> path_data =
+ ExtractResourcePathData(arg, &error_str)) {
+ input_data.push_back(std::move(path_data.value()));
+ } else {
+ context.GetDiagnostics()->Error(DiagMessage() << error_str << " ("
+ << arg << ")");
+ return 1;
+ }
+ }
- archiveWriter = createZipFileArchiveWriter(context.getDiagnostics(), options.outputPath);
+ archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(),
+ options.output_path);
+ }
+
+ if (!archive_writer) {
+ return 1;
+ }
+
+ bool error = false;
+ for (ResourcePathData& path_data : input_data) {
+ if (options.verbose) {
+ context.GetDiagnostics()->Note(DiagMessage(path_data.source)
+ << "processing");
+ }
+
+ if (path_data.resource_dir == "values") {
+ // Overwrite the extension.
+ path_data.extension = "arsc";
+
+ const std::string output_filename = BuildIntermediateFilename(path_data);
+ if (!CompileTable(&context, options, path_data, archive_writer.get(),
+ output_filename)) {
+ error = true;
+ }
} else {
- inputData.reserve(flags.getArgs().size());
-
- // Collect data from the path for each input file.
- for (const std::string& arg : flags.getArgs()) {
- std::string errorStr;
- if (Maybe<ResourcePathData> pathData = extractResourcePathData(arg, &errorStr)) {
- inputData.push_back(std::move(pathData.value()));
- } else {
- context.getDiagnostics()->error(DiagMessage() << errorStr << " (" << arg << ")");
- return 1;
+ const std::string output_filename = BuildIntermediateFilename(path_data);
+ if (const ResourceType* type =
+ ParseResourceType(path_data.resource_dir)) {
+ if (*type != ResourceType::kRaw) {
+ if (path_data.extension == "xml") {
+ if (!CompileXml(&context, options, path_data, archive_writer.get(),
+ output_filename)) {
+ error = true;
}
- }
-
- archiveWriter = createDirectoryArchiveWriter(context.getDiagnostics(), options.outputPath);
- }
-
- if (!archiveWriter) {
- return false;
- }
-
- bool error = false;
- for (ResourcePathData& pathData : inputData) {
- if (options.verbose) {
- context.getDiagnostics()->note(DiagMessage(pathData.source) << "processing");
- }
-
- if (pathData.resourceDir == "values") {
- // Overwrite the extension.
- pathData.extension = "arsc";
-
- const std::string outputFilename = buildIntermediateFilename(pathData);
- if (!compileTable(&context, options, pathData, archiveWriter.get(), outputFilename)) {
- error = true;
+ } else if (path_data.extension == "png" ||
+ path_data.extension == "9.png") {
+ if (!CompilePng(&context, options, path_data, archive_writer.get(),
+ output_filename)) {
+ error = true;
}
-
+ } else {
+ if (!CompileFile(&context, options, path_data, archive_writer.get(),
+ output_filename)) {
+ error = true;
+ }
+ }
} else {
- const std::string outputFilename = buildIntermediateFilename(pathData);
- if (const ResourceType* type = parseResourceType(pathData.resourceDir)) {
- if (*type != ResourceType::kRaw) {
- if (pathData.extension == "xml") {
- if (!compileXml(&context, options, pathData, archiveWriter.get(),
- outputFilename)) {
- error = true;
- }
- } else if (pathData.extension == "png" || pathData.extension == "9.png") {
- if (!compilePng(&context, options, pathData, archiveWriter.get(),
- outputFilename)) {
- error = true;
- }
- } else {
- if (!compileFile(&context, options, pathData, archiveWriter.get(),
- outputFilename)) {
- error = true;
- }
- }
- } else {
- if (!compileFile(&context, options, pathData, archiveWriter.get(),
- outputFilename)) {
- error = true;
- }
- }
- } else {
- context.getDiagnostics()->error(
- DiagMessage() << "invalid file path '" << pathData.source << "'");
- error = true;
- }
+ if (!CompileFile(&context, options, path_data, archive_writer.get(),
+ output_filename)) {
+ error = true;
+ }
}
+ } else {
+ context.GetDiagnostics()->Error(
+ DiagMessage() << "invalid file path '" << path_data.source << "'");
+ error = true;
+ }
}
+ }
- if (error) {
- return 1;
- }
- return 0;
+ if (error) {
+ return 1;
+ }
+ return 0;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 4a3f1e1..17c22c5 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -14,189 +14,205 @@
* limitations under the License.
*/
-#include "ResourceTable.h"
#include "compile/IdAssigner.h"
+
+#include <map>
+
+#include "android-base/logging.h"
+
+#include "ResourceTable.h"
#include "process/IResourceTableConsumer.h"
#include "util/Util.h"
-#include <cassert>
-#include <map>
-
namespace aapt {
/**
- * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and ResourceEntry,
+ * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and
+ * ResourceEntry,
* as long as there is no existing ID or the ID is the same.
*/
-static bool assignId(IDiagnostics* diag, const ResourceId& id, const ResourceName& name,
- ResourceTablePackage* pkg, ResourceTableType* type, ResourceEntry* entry) {
- if (pkg->id.value() == id.packageId()) {
- if (!type->id || type->id.value() == id.typeId()) {
- type->id = id.typeId();
+static bool AssignId(IDiagnostics* diag, const ResourceId& id,
+ const ResourceName& name, ResourceTablePackage* pkg,
+ ResourceTableType* type, ResourceEntry* entry) {
+ if (pkg->id.value() == id.package_id()) {
+ if (!type->id || type->id.value() == id.type_id()) {
+ type->id = id.type_id();
- if (!entry->id || entry->id.value() == id.entryId()) {
- entry->id = id.entryId();
- return true;
- }
- }
+ if (!entry->id || entry->id.value() == id.entry_id()) {
+ entry->id = id.entry_id();
+ return true;
+ }
}
+ }
- const ResourceId existingId(pkg->id.value(),
- type->id ? type->id.value() : 0,
- entry->id ? entry->id.value() : 0);
- diag->error(DiagMessage() << "can't assign ID " << id
- << " to resource " << name
- << " with conflicting ID " << existingId);
- return false;
+ const ResourceId existing_id(pkg->id.value(), type->id ? type->id.value() : 0,
+ entry->id ? entry->id.value() : 0);
+ diag->Error(DiagMessage() << "can't assign ID " << id << " to resource "
+ << name << " with conflicting ID " << existing_id);
+ return false;
}
-bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
- std::map<ResourceId, ResourceName> assignedIds;
+bool IdAssigner::Consume(IAaptContext* context, ResourceTable* table) {
+ std::map<ResourceId, ResourceName> assigned_ids;
- for (auto& package : table->packages) {
- assert(package->id && "packages must have manually assigned IDs");
+ for (auto& package : table->packages) {
+ CHECK(bool(package->id)) << "packages must have manually assigned IDs";
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- const ResourceName name(package->name, type->type, entry->name);
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ const ResourceName name(package->name, type->type, entry->name);
- if (mAssignedIdMap) {
- // Assign the pre-assigned stable ID meant for this resource.
- const auto iter = mAssignedIdMap->find(name);
- if (iter != mAssignedIdMap->end()) {
- const ResourceId assignedId = iter->second;
- const bool result = assignId(context->getDiagnostics(), assignedId, name,
- package.get(), type.get(), entry.get());
- if (!result) {
- return false;
- }
- }
- }
-
- if (package->id && type->id && entry->id) {
- // If the ID is set for this resource, then reserve it.
- ResourceId resourceId(package->id.value(), type->id.value(), entry->id.value());
- auto result = assignedIds.insert({ resourceId, name });
- const ResourceName& existingName = result.first->second;
- if (!result.second) {
- context->getDiagnostics()->error(DiagMessage() << "resource " << name
- << " has same ID "
- << resourceId
- << " as " << existingName);
- return false;
- }
- }
+ if (assigned_id_map_) {
+ // Assign the pre-assigned stable ID meant for this resource.
+ const auto iter = assigned_id_map_->find(name);
+ if (iter != assigned_id_map_->end()) {
+ const ResourceId assigned_id = iter->second;
+ const bool result =
+ AssignId(context->GetDiagnostics(), assigned_id, name,
+ package.get(), type.get(), entry.get());
+ if (!result) {
+ return false;
}
+ }
}
- }
- if (mAssignedIdMap) {
- // Reserve all the IDs mentioned in the stable ID map. That way we won't assign
- // IDs that were listed in the map if they don't exist in the table.
- for (const auto& stableIdEntry : *mAssignedIdMap) {
- const ResourceName& preAssignedName = stableIdEntry.first;
- const ResourceId& preAssignedId = stableIdEntry.second;
- auto result = assignedIds.insert({ preAssignedId, preAssignedName });
- const ResourceName& existingName = result.first->second;
- if (!result.second && existingName != preAssignedName) {
- context->getDiagnostics()->error(DiagMessage() << "stable ID " << preAssignedId
- << " for resource " << preAssignedName
- << " is already taken by resource "
- << existingName);
- return false;
- }
+ if (package->id && type->id && entry->id) {
+ // If the ID is set for this resource, then reserve it.
+ ResourceId resource_id(package->id.value(), type->id.value(),
+ entry->id.value());
+ auto result = assigned_ids.insert({resource_id, name});
+ const ResourceName& existing_name = result.first->second;
+ if (!result.second) {
+ context->GetDiagnostics()->Error(
+ DiagMessage() << "resource " << name << " has same ID "
+ << resource_id << " as " << existing_name);
+ return false;
+ }
}
+ }
}
+ }
- // Assign any resources without IDs the next available ID. Gaps will be filled if possible,
- // unless those IDs have been reserved.
+ if (assigned_id_map_) {
+ // Reserve all the IDs mentioned in the stable ID map. That way we won't
+ // assign
+ // IDs that were listed in the map if they don't exist in the table.
+ for (const auto& stable_id_entry : *assigned_id_map_) {
+ const ResourceName& pre_assigned_name = stable_id_entry.first;
+ const ResourceId& pre_assigned_id = stable_id_entry.second;
+ auto result = assigned_ids.insert({pre_assigned_id, pre_assigned_name});
+ const ResourceName& existing_name = result.first->second;
+ if (!result.second && existing_name != pre_assigned_name) {
+ context->GetDiagnostics()->Error(
+ DiagMessage() << "stable ID " << pre_assigned_id << " for resource "
+ << pre_assigned_name
+ << " is already taken by resource " << existing_name);
+ return false;
+ }
+ }
+ }
- const auto assignedIdsIterEnd = assignedIds.end();
- for (auto& package : table->packages) {
- assert(package->id && "packages must have manually assigned IDs");
+ // Assign any resources without IDs the next available ID. Gaps will be filled
+ // if possible,
+ // unless those IDs have been reserved.
- // Build a half filled ResourceId object, which will be used to find the closest matching
- // reserved ID in the assignedId map. From that point the next available type ID can be
- // found.
- ResourceId resourceId(package->id.value(), 0, 0);
- uint8_t nextExpectedTypeId = 1;
+ const auto assigned_ids_iter_end = assigned_ids.end();
+ for (auto& package : table->packages) {
+ CHECK(bool(package->id)) << "packages must have manually assigned IDs";
- // Find the closest matching ResourceId that is <= the one with only the package set.
- auto nextTypeIter = assignedIds.lower_bound(resourceId);
- for (auto& type : package->types) {
- if (!type->id) {
- // We need to assign a type ID. Iterate over the reserved IDs until we find
- // some type ID that is a distance of 2 greater than the last one we've seen.
- // That means there is an available type ID between these reserved IDs.
- while (nextTypeIter != assignedIdsIterEnd) {
- if (nextTypeIter->first.packageId() != package->id.value()) {
- break;
- }
+ // Build a half filled ResourceId object, which will be used to find the
+ // closest matching
+ // reserved ID in the assignedId map. From that point the next available
+ // type ID can be
+ // found.
+ ResourceId resource_id(package->id.value(), 0, 0);
+ uint8_t next_expected_type_id = 1;
- const uint8_t typeId = nextTypeIter->first.typeId();
- if (typeId > nextExpectedTypeId) {
- // There is a gap in the type IDs, so use the missing one.
- type->id = nextExpectedTypeId++;
- break;
- }
+ // Find the closest matching ResourceId that is <= the one with only the
+ // package set.
+ auto next_type_iter = assigned_ids.lower_bound(resource_id);
+ for (auto& type : package->types) {
+ if (!type->id) {
+ // We need to assign a type ID. Iterate over the reserved IDs until we
+ // find
+ // some type ID that is a distance of 2 greater than the last one we've
+ // seen.
+ // That means there is an available type ID between these reserved IDs.
+ while (next_type_iter != assigned_ids_iter_end) {
+ if (next_type_iter->first.package_id() != package->id.value()) {
+ break;
+ }
- // Set our expectation to be the next type ID after the reserved one we
- // just saw.
- nextExpectedTypeId = typeId + 1;
+ const uint8_t type_id = next_type_iter->first.type_id();
+ if (type_id > next_expected_type_id) {
+ // There is a gap in the type IDs, so use the missing one.
+ type->id = next_expected_type_id++;
+ break;
+ }
- // Move to the next reserved ID.
- ++nextTypeIter;
- }
+ // Set our expectation to be the next type ID after the reserved one
+ // we
+ // just saw.
+ next_expected_type_id = type_id + 1;
- if (!type->id) {
- // We must have hit the end of the reserved IDs and not found a gap.
- // That means the next ID is available.
- type->id = nextExpectedTypeId++;
- }
- }
-
- resourceId = ResourceId(package->id.value(), type->id.value(), 0);
- uint16_t nextExpectedEntryId = 0;
-
- // Find the closest matching ResourceId that is <= the one with only the package
- // and type set.
- auto nextEntryIter = assignedIds.lower_bound(resourceId);
- for (auto& entry : type->entries) {
- if (!entry->id) {
- // We need to assign an entry ID. Iterate over the reserved IDs until we find
- // some entry ID that is a distance of 2 greater than the last one we've seen.
- // That means there is an available entry ID between these reserved IDs.
- while (nextEntryIter != assignedIdsIterEnd) {
- if (nextEntryIter->first.packageId() != package->id.value() ||
- nextEntryIter->first.typeId() != type->id.value()) {
- break;
- }
-
- const uint16_t entryId = nextEntryIter->first.entryId();
- if (entryId > nextExpectedEntryId) {
- // There is a gap in the entry IDs, so use the missing one.
- entry->id = nextExpectedEntryId++;
- break;
- }
-
- // Set our expectation to be the next type ID after the reserved one we
- // just saw.
- nextExpectedEntryId = entryId + 1;
-
- // Move to the next reserved entry ID.
- ++nextEntryIter;
- }
-
- if (!entry->id) {
- // We must have hit the end of the reserved IDs and not found a gap.
- // That means the next ID is available.
- entry->id = nextExpectedEntryId++;
- }
- }
- }
+ // Move to the next reserved ID.
+ ++next_type_iter;
}
+
+ if (!type->id) {
+ // We must have hit the end of the reserved IDs and not found a gap.
+ // That means the next ID is available.
+ type->id = next_expected_type_id++;
+ }
+ }
+
+ resource_id = ResourceId(package->id.value(), type->id.value(), 0);
+ uint16_t next_expected_entry_id = 0;
+
+ // Find the closest matching ResourceId that is <= the one with only the
+ // package
+ // and type set.
+ auto next_entry_iter = assigned_ids.lower_bound(resource_id);
+ for (auto& entry : type->entries) {
+ if (!entry->id) {
+ // We need to assign an entry ID. Iterate over the reserved IDs until
+ // we find
+ // some entry ID that is a distance of 2 greater than the last one
+ // we've seen.
+ // That means there is an available entry ID between these reserved
+ // IDs.
+ while (next_entry_iter != assigned_ids_iter_end) {
+ if (next_entry_iter->first.package_id() != package->id.value() ||
+ next_entry_iter->first.type_id() != type->id.value()) {
+ break;
+ }
+
+ const uint16_t entry_id = next_entry_iter->first.entry_id();
+ if (entry_id > next_expected_entry_id) {
+ // There is a gap in the entry IDs, so use the missing one.
+ entry->id = next_expected_entry_id++;
+ break;
+ }
+
+ // Set our expectation to be the next type ID after the reserved one
+ // we
+ // just saw.
+ next_expected_entry_id = entry_id + 1;
+
+ // Move to the next reserved entry ID.
+ ++next_entry_iter;
+ }
+
+ if (!entry->id) {
+ // We must have hit the end of the reserved IDs and not found a gap.
+ // That means the next ID is available.
+ entry->id = next_expected_entry_id++;
+ }
+ }
+ }
}
- return true;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.h b/tools/aapt2/compile/IdAssigner.h
index 06cd5e3..371ec01 100644
--- a/tools/aapt2/compile/IdAssigner.h
+++ b/tools/aapt2/compile/IdAssigner.h
@@ -17,31 +17,33 @@
#ifndef AAPT_COMPILE_IDASSIGNER_H
#define AAPT_COMPILE_IDASSIGNER_H
+#include <unordered_map>
+
#include "Resource.h"
#include "process/IResourceTableConsumer.h"
-#include <android-base/macros.h>
-#include <unordered_map>
+#include "android-base/macros.h"
namespace aapt {
/**
- * Assigns IDs to each resource in the table, respecting existing IDs and filling in gaps
+ * Assigns IDs to each resource in the table, respecting existing IDs and
+ * filling in gaps
* in between fixed ID assignments.
*/
class IdAssigner : public IResourceTableConsumer {
-public:
- IdAssigner() = default;
- explicit IdAssigner(const std::unordered_map<ResourceName, ResourceId>* map) :
- mAssignedIdMap(map) {
- }
+ public:
+ IdAssigner() = default;
+ explicit IdAssigner(const std::unordered_map<ResourceName, ResourceId>* map)
+ : assigned_id_map_(map) {}
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
-private:
- const std::unordered_map<ResourceName, ResourceId>* mAssignedIdMap = nullptr;
+ private:
+ const std::unordered_map<ResourceName, ResourceId>* assigned_id_map_ =
+ nullptr;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_COMPILE_IDASSIGNER_H */
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index d21fcba..d465091 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -15,161 +15,171 @@
*/
#include "compile/IdAssigner.h"
+
#include "test/Test.h"
namespace aapt {
-::testing::AssertionResult verifyIds(ResourceTable* table);
+::testing::AssertionResult VerifyIds(ResourceTable* table);
TEST(IdAssignerTest, AssignIds) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:attr/foo")
- .addSimple("android:attr/bar")
- .addSimple("android:id/foo")
- .setPackageId("android", 0x01)
- .build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo")
+ .AddSimple("android:attr/bar")
+ .AddSimple("android:id/foo")
+ .SetPackageId("android", 0x01)
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- IdAssigner assigner;
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ IdAssigner assigner;
- ASSERT_TRUE(assigner.consume(context.get(), table.get()));
- ASSERT_TRUE(verifyIds(table.get()));
+ ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
+ ASSERT_TRUE(VerifyIds(table.get()));
}
TEST(IdAssignerTest, AssignIdsWithReservedIds) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:id/foo", ResourceId(0x01010000))
- .addSimple("android:dimen/two")
- .addSimple("android:integer/three")
- .addSimple("android:string/five")
- .addSimple("android:attr/fun", ResourceId(0x01040000))
- .addSimple("android:attr/foo", ResourceId(0x01040006))
- .addSimple("android:attr/bar")
- .addSimple("android:attr/baz")
- .addSimple("app:id/biz")
- .setPackageId("android", 0x01)
- .setPackageId("app", 0x7f)
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddSimple("android:id/foo", ResourceId(0x01010000))
+ .AddSimple("android:dimen/two")
+ .AddSimple("android:integer/three")
+ .AddSimple("android:string/five")
+ .AddSimple("android:attr/fun", ResourceId(0x01040000))
+ .AddSimple("android:attr/foo", ResourceId(0x01040006))
+ .AddSimple("android:attr/bar")
+ .AddSimple("android:attr/baz")
+ .AddSimple("app:id/biz")
+ .SetPackageId("android", 0x01)
+ .SetPackageId("app", 0x7f)
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- IdAssigner assigner;
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ IdAssigner assigner;
- ASSERT_TRUE(assigner.consume(context.get(), table.get()));
- ASSERT_TRUE(verifyIds(table.get()));
+ ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
+ ASSERT_TRUE(VerifyIds(table.get()));
- Maybe<ResourceTable::SearchResult> maybeResult;
+ Maybe<ResourceTable::SearchResult> maybe_result;
- // Expect to fill in the gaps between 0x0101XXXX and 0x0104XXXX.
+ // Expect to fill in the gaps between 0x0101XXXX and 0x0104XXXX.
- maybeResult = table->findResource(test::parseNameOrDie("android:dimen/two"));
- AAPT_ASSERT_TRUE(maybeResult);
- EXPECT_EQ(make_value<uint8_t>(2), maybeResult.value().type->id);
+ maybe_result = table->FindResource(test::ParseNameOrDie("android:dimen/two"));
+ AAPT_ASSERT_TRUE(maybe_result);
+ EXPECT_EQ(make_value<uint8_t>(2), maybe_result.value().type->id);
- maybeResult = table->findResource(test::parseNameOrDie("android:integer/three"));
- AAPT_ASSERT_TRUE(maybeResult);
- EXPECT_EQ(make_value<uint8_t>(3), maybeResult.value().type->id);
+ maybe_result =
+ table->FindResource(test::ParseNameOrDie("android:integer/three"));
+ AAPT_ASSERT_TRUE(maybe_result);
+ EXPECT_EQ(make_value<uint8_t>(3), maybe_result.value().type->id);
- // Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX IDs.
+ // Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX
+ // IDs.
- maybeResult = table->findResource(test::parseNameOrDie("android:string/five"));
- AAPT_ASSERT_TRUE(maybeResult);
- EXPECT_EQ(make_value<uint8_t>(5), maybeResult.value().type->id);
+ maybe_result =
+ table->FindResource(test::ParseNameOrDie("android:string/five"));
+ AAPT_ASSERT_TRUE(maybe_result);
+ EXPECT_EQ(make_value<uint8_t>(5), maybe_result.value().type->id);
- // Expect to fill in the gaps between 0x01040000 and 0x01040006.
+ // Expect to fill in the gaps between 0x01040000 and 0x01040006.
- maybeResult = table->findResource(test::parseNameOrDie("android:attr/bar"));
- AAPT_ASSERT_TRUE(maybeResult);
- EXPECT_EQ(make_value<uint16_t>(1), maybeResult.value().entry->id);
+ maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/bar"));
+ AAPT_ASSERT_TRUE(maybe_result);
+ EXPECT_EQ(make_value<uint16_t>(1), maybe_result.value().entry->id);
- maybeResult = table->findResource(test::parseNameOrDie("android:attr/baz"));
- AAPT_ASSERT_TRUE(maybeResult);
- EXPECT_EQ(make_value<uint16_t>(2), maybeResult.value().entry->id);
+ maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/baz"));
+ AAPT_ASSERT_TRUE(maybe_result);
+ EXPECT_EQ(make_value<uint16_t>(2), maybe_result.value().entry->id);
}
TEST(IdAssignerTest, FailWhenNonUniqueIdsAssigned) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:attr/foo", ResourceId(0x01040006))
- .addSimple("android:attr/bar", ResourceId(0x01040006))
- .setPackageId("android", 0x01)
- .setPackageId("app", 0x7f)
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo", ResourceId(0x01040006))
+ .AddSimple("android:attr/bar", ResourceId(0x01040006))
+ .SetPackageId("android", 0x01)
+ .SetPackageId("app", 0x7f)
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- IdAssigner assigner;
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ IdAssigner assigner;
- ASSERT_FALSE(assigner.consume(context.get(), table.get()));
+ ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
}
TEST(IdAssignerTest, AssignIdsWithIdMap) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:attr/foo")
- .addSimple("android:attr/bar")
- .setPackageId("android", 0x01)
- .build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo")
+ .AddSimple("android:attr/bar")
+ .SetPackageId("android", 0x01)
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unordered_map<ResourceName, ResourceId> idMap = {
- { test::parseNameOrDie("android:attr/foo"), ResourceId(0x01010002) } };
- IdAssigner assigner(&idMap);
- ASSERT_TRUE(assigner.consume(context.get(), table.get()));
- ASSERT_TRUE(verifyIds(table.get()));
- Maybe<ResourceTable::SearchResult> result = table->findResource(
- test::parseNameOrDie("android:attr/foo"));
- AAPT_ASSERT_TRUE(result);
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unordered_map<ResourceName, ResourceId> id_map = {
+ {test::ParseNameOrDie("android:attr/foo"), ResourceId(0x01010002)}};
+ IdAssigner assigner(&id_map);
+ ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
+ ASSERT_TRUE(VerifyIds(table.get()));
+ Maybe<ResourceTable::SearchResult> result =
+ table->FindResource(test::ParseNameOrDie("android:attr/foo"));
+ AAPT_ASSERT_TRUE(result);
- const ResourceTable::SearchResult& searchResult = result.value();
- EXPECT_EQ(make_value<uint8_t>(0x01), searchResult.package->id);
- EXPECT_EQ(make_value<uint8_t>(0x01), searchResult.type->id);
- EXPECT_EQ(make_value<uint16_t>(0x0002), searchResult.entry->id);
+ const ResourceTable::SearchResult& search_result = result.value();
+ EXPECT_EQ(make_value<uint8_t>(0x01), search_result.package->id);
+ EXPECT_EQ(make_value<uint8_t>(0x01), search_result.type->id);
+ EXPECT_EQ(make_value<uint16_t>(0x0002), search_result.entry->id);
}
-::testing::AssertionResult verifyIds(ResourceTable* table) {
- std::set<uint8_t> packageIds;
- for (auto& package : table->packages) {
- if (!package->id) {
- return ::testing::AssertionFailure() << "package " << package->name << " has no ID";
- }
-
- if (!packageIds.insert(package->id.value()).second) {
- return ::testing::AssertionFailure() << "package " << package->name
- << " has non-unique ID " << std::hex << (int) package->id.value() << std::dec;
- }
+::testing::AssertionResult VerifyIds(ResourceTable* table) {
+ std::set<uint8_t> package_ids;
+ for (auto& package : table->packages) {
+ if (!package->id) {
+ return ::testing::AssertionFailure() << "package " << package->name
+ << " has no ID";
}
- for (auto& package : table->packages) {
- std::set<uint8_t> typeIds;
- for (auto& type : package->types) {
- if (!type->id) {
- return ::testing::AssertionFailure() << "type " << type->type << " of package "
- << package->name << " has no ID";
- }
-
- if (!typeIds.insert(type->id.value()).second) {
- return ::testing::AssertionFailure() << "type " << type->type
- << " of package " << package->name << " has non-unique ID "
- << std::hex << (int) type->id.value() << std::dec;
- }
- }
-
-
- for (auto& type : package->types) {
- std::set<uint16_t> entryIds;
- for (auto& entry : type->entries) {
- if (!entry->id) {
- return ::testing::AssertionFailure() << "entry " << entry->name << " of type "
- << type->type << " of package " << package->name << " has no ID";
- }
-
- if (!entryIds.insert(entry->id.value()).second) {
- return ::testing::AssertionFailure() << "entry " << entry->name
- << " of type " << type->type << " of package " << package->name
- << " has non-unique ID "
- << std::hex << (int) entry->id.value() << std::dec;
- }
- }
- }
+ if (!package_ids.insert(package->id.value()).second) {
+ return ::testing::AssertionFailure()
+ << "package " << package->name << " has non-unique ID " << std::hex
+ << (int)package->id.value() << std::dec;
}
- return ::testing::AssertionSuccess() << "all IDs are unique and assigned";
+ }
+
+ for (auto& package : table->packages) {
+ std::set<uint8_t> type_ids;
+ for (auto& type : package->types) {
+ if (!type->id) {
+ return ::testing::AssertionFailure() << "type " << type->type
+ << " of package " << package->name
+ << " has no ID";
+ }
+
+ if (!type_ids.insert(type->id.value()).second) {
+ return ::testing::AssertionFailure()
+ << "type " << type->type << " of package " << package->name
+ << " has non-unique ID " << std::hex << (int)type->id.value()
+ << std::dec;
+ }
+ }
+
+ for (auto& type : package->types) {
+ std::set<uint16_t> entry_ids;
+ for (auto& entry : type->entries) {
+ if (!entry->id) {
+ return ::testing::AssertionFailure()
+ << "entry " << entry->name << " of type " << type->type
+ << " of package " << package->name << " has no ID";
+ }
+
+ if (!entry_ids.insert(entry->id.value()).second) {
+ return ::testing::AssertionFailure()
+ << "entry " << entry->name << " of type " << type->type
+ << " of package " << package->name << " has non-unique ID "
+ << std::hex << (int)entry->id.value() << std::dec;
+ }
+ }
+ }
+ }
+ return ::testing::AssertionSuccess() << "all IDs are unique and assigned";
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/Image.h b/tools/aapt2/compile/Image.h
index fda6a3a..db0b945 100644
--- a/tools/aapt2/compile/Image.h
+++ b/tools/aapt2/compile/Image.h
@@ -17,185 +17,193 @@
#ifndef AAPT_COMPILE_IMAGE_H
#define AAPT_COMPILE_IMAGE_H
-#include <android-base/macros.h>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
+#include "android-base/macros.h"
+
namespace aapt {
/**
* An in-memory image, loaded from disk, with pixels in RGBA_8888 format.
*/
class Image {
-public:
- explicit Image() = default;
+ public:
+ explicit Image() = default;
- /**
- * A `height` sized array of pointers, where each element points to a
- * `width` sized row of RGBA_8888 pixels.
- */
- std::unique_ptr<uint8_t*[]> rows;
+ /**
+ * A `height` sized array of pointers, where each element points to a
+ * `width` sized row of RGBA_8888 pixels.
+ */
+ std::unique_ptr<uint8_t* []> rows;
- /**
- * The width of the image in RGBA_8888 pixels. This is int32_t because of 9-patch data
- * format limitations.
- */
- int32_t width = 0;
+ /**
+ * The width of the image in RGBA_8888 pixels. This is int32_t because of
+ * 9-patch data
+ * format limitations.
+ */
+ int32_t width = 0;
- /**
- * The height of the image in RGBA_8888 pixels. This is int32_t because of 9-patch data
- * format limitations.
- */
- int32_t height = 0;
+ /**
+ * The height of the image in RGBA_8888 pixels. This is int32_t because of
+ * 9-patch data
+ * format limitations.
+ */
+ int32_t height = 0;
- /**
- * Buffer to the raw image data stored sequentially.
- * Use `rows` to access the data on a row-by-row basis.
- */
- std::unique_ptr<uint8_t[]> data;
+ /**
+ * Buffer to the raw image data stored sequentially.
+ * Use `rows` to access the data on a row-by-row basis.
+ */
+ std::unique_ptr<uint8_t[]> data;
-private:
- DISALLOW_COPY_AND_ASSIGN(Image);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Image);
};
/**
- * A range of pixel values, starting at 'start' and ending before 'end' exclusive. Or rather [a, b).
+ * A range of pixel values, starting at 'start' and ending before 'end'
+ * exclusive. Or rather [a, b).
*/
struct Range {
- int32_t start = 0;
- int32_t end = 0;
+ int32_t start = 0;
+ int32_t end = 0;
- explicit Range() = default;
- inline explicit Range(int32_t s, int32_t e) : start(s), end(e) {
- }
+ explicit Range() = default;
+ inline explicit Range(int32_t s, int32_t e) : start(s), end(e) {}
};
inline bool operator==(const Range& left, const Range& right) {
- return left.start == right.start && left.end == right.end;
+ return left.start == right.start && left.end == right.end;
}
/**
- * Inset lengths from all edges of a rectangle. `left` and `top` are measured from the left and top
- * edges, while `right` and `bottom` are measured from the right and bottom edges, respectively.
+ * Inset lengths from all edges of a rectangle. `left` and `top` are measured
+ * from the left and top
+ * edges, while `right` and `bottom` are measured from the right and bottom
+ * edges, respectively.
*/
struct Bounds {
- int32_t left = 0;
- int32_t top = 0;
- int32_t right = 0;
- int32_t bottom = 0;
+ int32_t left = 0;
+ int32_t top = 0;
+ int32_t right = 0;
+ int32_t bottom = 0;
- explicit Bounds() = default;
- inline explicit Bounds(int32_t l, int32_t t, int32_t r, int32_t b) :
- left(l), top(t), right(r), bottom(b) {
- }
+ explicit Bounds() = default;
+ inline explicit Bounds(int32_t l, int32_t t, int32_t r, int32_t b)
+ : left(l), top(t), right(r), bottom(b) {}
- bool nonZero() const;
+ bool nonZero() const;
};
inline bool Bounds::nonZero() const {
- return left != 0 || top != 0 || right != 0 || bottom != 0;
+ return left != 0 || top != 0 || right != 0 || bottom != 0;
}
inline bool operator==(const Bounds& left, const Bounds& right) {
- return left.left == right.left && left.top == right.top &&
- left.right == right.right && left.bottom == right.bottom;
+ return left.left == right.left && left.top == right.top &&
+ left.right == right.right && left.bottom == right.bottom;
}
/**
- * Contains 9-patch data from a source image. All measurements exclude the 1px border of the
+ * Contains 9-patch data from a source image. All measurements exclude the 1px
+ * border of the
* source 9-patch image.
*/
class NinePatch {
-public:
- static std::unique_ptr<NinePatch> create(uint8_t** rows,
- const int32_t width, const int32_t height,
- std::string* errOut);
+ public:
+ static std::unique_ptr<NinePatch> Create(uint8_t** rows, const int32_t width,
+ const int32_t height,
+ std::string* err_out);
- /**
- * Packs the RGBA_8888 data pointed to by pixel into a uint32_t
- * with format 0xAARRGGBB (the way 9-patch expects it).
- */
- static uint32_t packRGBA(const uint8_t* pixel);
+ /**
+ * Packs the RGBA_8888 data pointed to by pixel into a uint32_t
+ * with format 0xAARRGGBB (the way 9-patch expects it).
+ */
+ static uint32_t PackRGBA(const uint8_t* pixel);
- /**
- * 9-patch content padding/insets. All positions are relative to the 9-patch
- * NOT including the 1px thick source border.
- */
- Bounds padding;
+ /**
+ * 9-patch content padding/insets. All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ Bounds padding;
- /**
- * Optical layout bounds/insets. This overrides the padding for
- * layout purposes. All positions are relative to the 9-patch
- * NOT including the 1px thick source border.
- * See https://developer.android.com/about/versions/android-4.3.html#OpticalBounds
- */
- Bounds layoutBounds;
+ /**
+ * Optical layout bounds/insets. This overrides the padding for
+ * layout purposes. All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ * See
+ * https://developer.android.com/about/versions/android-4.3.html#OpticalBounds
+ */
+ Bounds layout_bounds;
- /**
- * Outline of the image, calculated based on opacity.
- */
- Bounds outline;
+ /**
+ * Outline of the image, calculated based on opacity.
+ */
+ Bounds outline;
- /**
- * The computed radius of the outline. If non-zero, the outline is a rounded-rect.
- */
- float outlineRadius = 0.0f;
+ /**
+ * The computed radius of the outline. If non-zero, the outline is a
+ * rounded-rect.
+ */
+ float outline_radius = 0.0f;
- /**
- * The largest alpha value within the outline.
- */
- uint32_t outlineAlpha = 0x000000ffu;
+ /**
+ * The largest alpha value within the outline.
+ */
+ uint32_t outline_alpha = 0x000000ffu;
- /**
- * Horizontal regions of the image that are stretchable.
- * All positions are relative to the 9-patch
- * NOT including the 1px thick source border.
- */
- std::vector<Range> horizontalStretchRegions;
+ /**
+ * Horizontal regions of the image that are stretchable.
+ * All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ std::vector<Range> horizontal_stretch_regions;
- /**
- * Vertical regions of the image that are stretchable.
- * All positions are relative to the 9-patch
- * NOT including the 1px thick source border.
- */
- std::vector<Range> verticalStretchRegions;
+ /**
+ * Vertical regions of the image that are stretchable.
+ * All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ std::vector<Range> vertical_stretch_regions;
- /**
- * The colors within each region, fixed or stretchable.
- * For w*h regions, the color of region (x,y) is addressable
- * via index y*w + x.
- */
- std::vector<uint32_t> regionColors;
+ /**
+ * The colors within each region, fixed or stretchable.
+ * For w*h regions, the color of region (x,y) is addressable
+ * via index y*w + x.
+ */
+ std::vector<uint32_t> region_colors;
- /**
- * Returns serialized data containing the original basic 9-patch meta data.
- * Optical layout bounds and round rect outline data must be serialized
- * separately using serializeOpticalLayoutBounds() and serializeRoundedRectOutline().
- */
- std::unique_ptr<uint8_t[]> serializeBase(size_t* outLen) const;
+ /**
+ * Returns serialized data containing the original basic 9-patch meta data.
+ * Optical layout bounds and round rect outline data must be serialized
+ * separately using SerializeOpticalLayoutBounds() and
+ * SerializeRoundedRectOutline().
+ */
+ std::unique_ptr<uint8_t[]> SerializeBase(size_t* out_len) const;
- /**
- * Serializes the layout bounds.
- */
- std::unique_ptr<uint8_t[]> serializeLayoutBounds(size_t* outLen) const;
+ /**
+ * Serializes the layout bounds.
+ */
+ std::unique_ptr<uint8_t[]> SerializeLayoutBounds(size_t* out_len) const;
- /**
- * Serializes the rounded-rect outline.
- */
- std::unique_ptr<uint8_t[]> serializeRoundedRectOutline(size_t* outLen) const;
+ /**
+ * Serializes the rounded-rect outline.
+ */
+ std::unique_ptr<uint8_t[]> SerializeRoundedRectOutline(size_t* out_len) const;
-private:
- explicit NinePatch() = default;
+ private:
+ explicit NinePatch() = default;
- DISALLOW_COPY_AND_ASSIGN(NinePatch);
+ DISALLOW_COPY_AND_ASSIGN(NinePatch);
};
::std::ostream& operator<<(::std::ostream& out, const Range& range);
::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds);
-::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch);
+::std::ostream& operator<<(::std::ostream& out, const NinePatch& nine_patch);
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_COMPILE_IMAGE_H */
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp
index f965bff..786494b 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -14,17 +14,19 @@
* limitations under the License.
*/
+#include "compile/InlineXmlFormatParser.h"
+
+#include <sstream>
+#include <string>
+
+#include "android-base/macros.h"
+
#include "Debug.h"
#include "ResourceUtils.h"
-#include "compile/InlineXmlFormatParser.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
#include "xml/XmlUtil.h"
-#include <android-base/macros.h>
-#include <sstream>
-#include <string>
-
namespace aapt {
namespace {
@@ -33,158 +35,173 @@
* XML Visitor that will find all <aapt:attr> elements for extraction.
*/
class Visitor : public xml::PackageAwareVisitor {
-public:
- using xml::PackageAwareVisitor::visit;
+ public:
+ using xml::PackageAwareVisitor::Visit;
- struct InlineDeclaration {
- xml::Element* el;
- std::string attrNamespaceUri;
- std::string attrName;
- };
+ struct InlineDeclaration {
+ xml::Element* el;
+ std::string attr_namespace_uri;
+ std::string attr_name;
+ };
- explicit Visitor(IAaptContext* context, xml::XmlResource* xmlResource) :
- mContext(context), mXmlResource(xmlResource) {
+ explicit Visitor(IAaptContext* context, xml::XmlResource* xml_resource)
+ : context_(context), xml_resource_(xml_resource) {}
+
+ void Visit(xml::Element* el) override {
+ if (el->namespace_uri != xml::kSchemaAapt || el->name != "attr") {
+ xml::PackageAwareVisitor::Visit(el);
+ return;
}
- void visit(xml::Element* el) override {
- if (el->namespaceUri != xml::kSchemaAapt || el->name != "attr") {
- xml::PackageAwareVisitor::visit(el);
- return;
- }
+ const Source& src = xml_resource_->file.source.WithLine(el->line_number);
- const Source& src = mXmlResource->file.source.withLine(el->lineNumber);
-
- xml::Attribute* attr = el->findAttribute({}, "name");
- if (!attr) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "missing 'name' attribute");
- mError = true;
- return;
- }
-
- Maybe<Reference> ref = ResourceUtils::parseXmlAttributeName(attr->value);
- if (!ref) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "invalid XML attribute '"
- << attr->value << "'");
- mError = true;
- return;
- }
-
- const ResourceName& name = ref.value().name.value();
-
- // Use an empty string for the compilation package because we don't want to default to
- // the local package if the user specified name="style" or something. This should just
- // be the default namespace.
- Maybe<xml::ExtractedPackage> maybePkg = transformPackageAlias(name.package, {});
- if (!maybePkg) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "invalid namespace prefix '"
- << name.package << "'");
- mError = true;
- return;
- }
-
- const xml::ExtractedPackage& pkg = maybePkg.value();
- const bool privateNamespace = pkg.privateNamespace || ref.value().privateReference;
-
- InlineDeclaration decl;
- decl.el = el;
- decl.attrName = name.entry;
- if (!pkg.package.empty()) {
- decl.attrNamespaceUri = xml::buildPackageNamespace(pkg.package, privateNamespace);
- }
-
- mInlineDeclarations.push_back(std::move(decl));
+ xml::Attribute* attr = el->FindAttribute({}, "name");
+ if (!attr) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "missing 'name' attribute");
+ error_ = true;
+ return;
}
- const std::vector<InlineDeclaration>& getInlineDeclarations() const {
- return mInlineDeclarations;
+ Maybe<Reference> ref = ResourceUtils::ParseXmlAttributeName(attr->value);
+ if (!ref) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(src) << "invalid XML attribute '" << attr->value << "'");
+ error_ = true;
+ return;
}
- bool hasError() const {
- return mError;
+ const ResourceName& name = ref.value().name.value();
+
+ // Use an empty string for the compilation package because we don't want to
+ // default to
+ // the local package if the user specified name="style" or something. This
+ // should just
+ // be the default namespace.
+ Maybe<xml::ExtractedPackage> maybe_pkg =
+ TransformPackageAlias(name.package, {});
+ if (!maybe_pkg) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "invalid namespace prefix '"
+ << name.package << "'");
+ error_ = true;
+ return;
}
-private:
- DISALLOW_COPY_AND_ASSIGN(Visitor);
+ const xml::ExtractedPackage& pkg = maybe_pkg.value();
+ const bool private_namespace =
+ pkg.private_namespace || ref.value().private_reference;
- IAaptContext* mContext;
- xml::XmlResource* mXmlResource;
- std::vector<InlineDeclaration> mInlineDeclarations;
- bool mError = false;
+ InlineDeclaration decl;
+ decl.el = el;
+ decl.attr_name = name.entry;
+ if (!pkg.package.empty()) {
+ decl.attr_namespace_uri =
+ xml::BuildPackageNamespace(pkg.package, private_namespace);
+ }
+
+ inline_declarations_.push_back(std::move(decl));
+ }
+
+ const std::vector<InlineDeclaration>& GetInlineDeclarations() const {
+ return inline_declarations_;
+ }
+
+ bool HasError() const { return error_; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Visitor);
+
+ IAaptContext* context_;
+ xml::XmlResource* xml_resource_;
+ std::vector<InlineDeclaration> inline_declarations_;
+ bool error_ = false;
};
-} // namespace
+} // namespace
-bool InlineXmlFormatParser::consume(IAaptContext* context, xml::XmlResource* doc) {
- Visitor visitor(context, doc);
- doc->root->accept(&visitor);
- if (visitor.hasError()) {
+bool InlineXmlFormatParser::Consume(IAaptContext* context,
+ xml::XmlResource* doc) {
+ Visitor visitor(context, doc);
+ doc->root->Accept(&visitor);
+ if (visitor.HasError()) {
+ return false;
+ }
+
+ size_t name_suffix_counter = 0;
+ for (const Visitor::InlineDeclaration& decl :
+ visitor.GetInlineDeclarations()) {
+ auto new_doc = util::make_unique<xml::XmlResource>();
+ new_doc->file.config = doc->file.config;
+ new_doc->file.source = doc->file.source.WithLine(decl.el->line_number);
+ new_doc->file.name = doc->file.name;
+
+ // Modify the new entry name. We need to suffix the entry with a number to
+ // avoid
+ // local collisions, then mangle it with the empty package, such that it
+ // won't show up
+ // in R.java.
+
+ new_doc->file.name.entry =
+ NameMangler::MangleEntry({}, new_doc->file.name.entry + "__" +
+ std::to_string(name_suffix_counter));
+
+ // Extracted elements must be the only child of <aapt:attr>.
+ // Make sure there is one root node in the children (ignore empty text).
+ for (auto& child : decl.el->children) {
+ const Source child_source = doc->file.source.WithLine(child->line_number);
+ if (xml::Text* t = xml::NodeCast<xml::Text>(child.get())) {
+ if (!util::TrimWhitespace(t->text).empty()) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(child_source)
+ << "can't extract text into its own resource");
+ return false;
+ }
+ } else if (new_doc->root) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(child_source)
+ << "inline XML resources must have a single root");
return false;
+ } else {
+ new_doc->root = std::move(child);
+ new_doc->root->parent = nullptr;
+ }
}
- size_t nameSuffixCounter = 0;
- for (const Visitor::InlineDeclaration& decl : visitor.getInlineDeclarations()) {
- auto newDoc = util::make_unique<xml::XmlResource>();
- newDoc->file.config = doc->file.config;
- newDoc->file.source = doc->file.source.withLine(decl.el->lineNumber);
- newDoc->file.name = doc->file.name;
-
- // Modify the new entry name. We need to suffix the entry with a number to avoid
- // local collisions, then mangle it with the empty package, such that it won't show up
- // in R.java.
-
- newDoc->file.name.entry = NameMangler::mangleEntry(
- {}, newDoc->file.name.entry + "__" + std::to_string(nameSuffixCounter));
-
- // Extracted elements must be the only child of <aapt:attr>.
- // Make sure there is one root node in the children (ignore empty text).
- for (auto& child : decl.el->children) {
- const Source childSource = doc->file.source.withLine(child->lineNumber);
- if (xml::Text* t = xml::nodeCast<xml::Text>(child.get())) {
- if (!util::trimWhitespace(t->text).empty()) {
- context->getDiagnostics()->error(DiagMessage(childSource)
- << "can't extract text into its own resource");
- return false;
- }
- } else if (newDoc->root) {
- context->getDiagnostics()->error(DiagMessage(childSource)
- << "inline XML resources must have a single root");
- return false;
- } else {
- newDoc->root = std::move(child);
- newDoc->root->parent = nullptr;
- }
- }
-
- // Walk up and find the parent element.
- xml::Node* node = decl.el;
- xml::Element* parentEl = nullptr;
- while (node->parent && (parentEl = xml::nodeCast<xml::Element>(node->parent)) == nullptr) {
- node = node->parent;
- }
-
- if (!parentEl) {
- context->getDiagnostics()->error(DiagMessage(newDoc->file.source)
- << "no suitable parent for inheriting attribute");
- return false;
- }
-
- // Add the inline attribute to the parent.
- parentEl->attributes.push_back(xml::Attribute{
- decl.attrNamespaceUri, decl.attrName, "@" + newDoc->file.name.toString() });
-
- // Delete the subtree.
- for (auto iter = parentEl->children.begin(); iter != parentEl->children.end(); ++iter) {
- if (iter->get() == node) {
- parentEl->children.erase(iter);
- break;
- }
- }
-
- mQueue.push_back(std::move(newDoc));
-
- nameSuffixCounter++;
+ // Walk up and find the parent element.
+ xml::Node* node = decl.el;
+ xml::Element* parent_el = nullptr;
+ while (node->parent &&
+ (parent_el = xml::NodeCast<xml::Element>(node->parent)) == nullptr) {
+ node = node->parent;
}
- return true;
+
+ if (!parent_el) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(new_doc->file.source)
+ << "no suitable parent for inheriting attribute");
+ return false;
+ }
+
+ // Add the inline attribute to the parent.
+ parent_el->attributes.push_back(
+ xml::Attribute{decl.attr_namespace_uri, decl.attr_name,
+ "@" + new_doc->file.name.ToString()});
+
+ // Delete the subtree.
+ for (auto iter = parent_el->children.begin();
+ iter != parent_el->children.end(); ++iter) {
+ if (iter->get() == node) {
+ parent_el->children.erase(iter);
+ break;
+ }
+ }
+
+ queue_.push_back(std::move(new_doc));
+
+ name_suffix_counter++;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.h b/tools/aapt2/compile/InlineXmlFormatParser.h
index 69065fd..1a658fd 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.h
+++ b/tools/aapt2/compile/InlineXmlFormatParser.h
@@ -17,12 +17,13 @@
#ifndef AAPT_COMPILE_INLINEXMLFORMATPARSER_H
#define AAPT_COMPILE_INLINEXMLFORMATPARSER_H
-#include "process/IResourceTableConsumer.h"
-
-#include <android-base/macros.h>
#include <memory>
#include <vector>
+#include "android-base/macros.h"
+
+#include "process/IResourceTableConsumer.h"
+
namespace aapt {
/**
@@ -41,25 +42,28 @@
* </aapt:attr>
* </animated-vector>
*
- * The <vector> will be extracted into its own XML file and <animated-vector> will
- * gain an attribute 'android:drawable' set to a reference to the extracted <vector> resource.
+ * The <vector> will be extracted into its own XML file and <animated-vector>
+ * will
+ * gain an attribute 'android:drawable' set to a reference to the extracted
+ * <vector> resource.
*/
class InlineXmlFormatParser : public IXmlResourceConsumer {
-public:
- explicit InlineXmlFormatParser() = default;
+ public:
+ explicit InlineXmlFormatParser() = default;
- bool consume(IAaptContext* context, xml::XmlResource* doc) override;
+ bool Consume(IAaptContext* context, xml::XmlResource* doc) override;
- std::vector<std::unique_ptr<xml::XmlResource>>& getExtractedInlineXmlDocuments() {
- return mQueue;
- }
+ std::vector<std::unique_ptr<xml::XmlResource>>&
+ GetExtractedInlineXmlDocuments() {
+ return queue_;
+ }
-private:
- DISALLOW_COPY_AND_ASSIGN(InlineXmlFormatParser);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InlineXmlFormatParser);
- std::vector<std::unique_ptr<xml::XmlResource>> mQueue;
+ std::vector<std::unique_ptr<xml::XmlResource>> queue_;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_COMPILE_INLINEXMLFORMATPARSER_H */
diff --git a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
index 8d62210..348796c 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
@@ -15,27 +15,28 @@
*/
#include "compile/InlineXmlFormatParser.h"
+
#include "test/Test.h"
namespace aapt {
TEST(InlineXmlFormatParserTest, PassThrough) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android">
<View android:text="hey">
<View android:id="hi" />
</View>
</View>)EOF");
- InlineXmlFormatParser parser;
- ASSERT_TRUE(parser.consume(context.get(), doc.get()));
- EXPECT_EQ(0u, parser.getExtractedInlineXmlDocuments().size());
+ InlineXmlFormatParser parser;
+ ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
+ EXPECT_EQ(0u, parser.GetExtractedInlineXmlDocuments().size());
}
TEST(InlineXmlFormatParserTest, ExtractOneXmlResource) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View1 xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:text">
@@ -45,47 +46,48 @@
</aapt:attr>
</View1>)EOF");
- doc->file.name = test::parseNameOrDie("layout/main");
+ doc->file.name = test::ParseNameOrDie("layout/main");
- InlineXmlFormatParser parser;
- ASSERT_TRUE(parser.consume(context.get(), doc.get()));
+ InlineXmlFormatParser parser;
+ ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
- // One XML resource should have been extracted.
- EXPECT_EQ(1u, parser.getExtractedInlineXmlDocuments().size());
+ // One XML resource should have been extracted.
+ EXPECT_EQ(1u, parser.GetExtractedInlineXmlDocuments().size());
- xml::Element* el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
+ xml::Element* el = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
- EXPECT_EQ("View1", el->name);
+ EXPECT_EQ("View1", el->name);
- // The <aapt:attr> tag should be extracted.
- EXPECT_EQ(nullptr, el->findChild(xml::kSchemaAapt, "attr"));
+ // The <aapt:attr> tag should be extracted.
+ EXPECT_EQ(nullptr, el->FindChild(xml::kSchemaAapt, "attr"));
- // The 'android:text' attribute should be set with a reference.
- xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "text");
- ASSERT_NE(nullptr, attr);
+ // The 'android:text' attribute should be set with a reference.
+ xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "text");
+ ASSERT_NE(nullptr, attr);
- ResourceNameRef nameRef;
- ASSERT_TRUE(ResourceUtils::parseReference(attr->value, &nameRef));
+ ResourceNameRef name_ref;
+ ASSERT_TRUE(ResourceUtils::ParseReference(attr->value, &name_ref));
- xml::XmlResource* extractedDoc = parser.getExtractedInlineXmlDocuments()[0].get();
- ASSERT_NE(nullptr, extractedDoc);
+ xml::XmlResource* extracted_doc =
+ parser.GetExtractedInlineXmlDocuments()[0].get();
+ ASSERT_NE(nullptr, extracted_doc);
- // Make sure the generated reference is correct.
- EXPECT_EQ(nameRef.package, extractedDoc->file.name.package);
- EXPECT_EQ(nameRef.type, extractedDoc->file.name.type);
- EXPECT_EQ(nameRef.entry, extractedDoc->file.name.entry);
+ // Make sure the generated reference is correct.
+ EXPECT_EQ(name_ref.package, extracted_doc->file.name.package);
+ EXPECT_EQ(name_ref.type, extracted_doc->file.name.type);
+ EXPECT_EQ(name_ref.entry, extracted_doc->file.name.entry);
- // Verify the structure of the extracted XML.
- el = xml::findRootElement(extractedDoc);
- ASSERT_NE(nullptr, el);
- EXPECT_EQ("View2", el->name);
- EXPECT_NE(nullptr, el->findChild({}, "View3"));
+ // Verify the structure of the extracted XML.
+ el = xml::FindRootElement(extracted_doc);
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ("View2", el->name);
+ EXPECT_NE(nullptr, el->FindChild({}, "View3"));
}
TEST(InlineXmlFormatParserTest, ExtractTwoXmlResources) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View1 xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:text">
@@ -99,40 +101,43 @@
</aapt:attr>
</View1>)EOF");
- doc->file.name = test::parseNameOrDie("layout/main");
+ doc->file.name = test::ParseNameOrDie("layout/main");
- InlineXmlFormatParser parser;
- ASSERT_TRUE(parser.consume(context.get(), doc.get()));
- ASSERT_EQ(2u, parser.getExtractedInlineXmlDocuments().size());
+ InlineXmlFormatParser parser;
+ ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
+ ASSERT_EQ(2u, parser.GetExtractedInlineXmlDocuments().size());
- xml::Element* el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
+ xml::Element* el = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
- EXPECT_EQ("View1", el->name);
+ EXPECT_EQ("View1", el->name);
- xml::Attribute* attrText = el->findAttribute(xml::kSchemaAndroid, "text");
- ASSERT_NE(nullptr, attrText);
+ xml::Attribute* attr_text = el->FindAttribute(xml::kSchemaAndroid, "text");
+ ASSERT_NE(nullptr, attr_text);
- xml::Attribute* attrDrawable = el->findAttribute(xml::kSchemaAndroid, "drawable");
- ASSERT_NE(nullptr, attrDrawable);
+ xml::Attribute* attr_drawable =
+ el->FindAttribute(xml::kSchemaAndroid, "drawable");
+ ASSERT_NE(nullptr, attr_drawable);
- // The two extracted resources should have different names.
- EXPECT_NE(attrText->value, attrDrawable->value);
+ // The two extracted resources should have different names.
+ EXPECT_NE(attr_text->value, attr_drawable->value);
- // The child <aapt:attr> elements should be gone.
- EXPECT_EQ(nullptr, el->findChild(xml::kSchemaAapt, "attr"));
+ // The child <aapt:attr> elements should be gone.
+ EXPECT_EQ(nullptr, el->FindChild(xml::kSchemaAapt, "attr"));
- xml::XmlResource* extractedDocText = parser.getExtractedInlineXmlDocuments()[0].get();
- ASSERT_NE(nullptr, extractedDocText);
- el = xml::findRootElement(extractedDocText);
- ASSERT_NE(nullptr, el);
- EXPECT_EQ("View2", el->name);
+ xml::XmlResource* extracted_doc_text =
+ parser.GetExtractedInlineXmlDocuments()[0].get();
+ ASSERT_NE(nullptr, extracted_doc_text);
+ el = xml::FindRootElement(extracted_doc_text);
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ("View2", el->name);
- xml::XmlResource* extractedDocDrawable = parser.getExtractedInlineXmlDocuments()[1].get();
- ASSERT_NE(nullptr, extractedDocDrawable);
- el = xml::findRootElement(extractedDocDrawable);
- ASSERT_NE(nullptr, el);
- EXPECT_EQ("vector", el->name);
+ xml::XmlResource* extracted_doc_drawable =
+ parser.GetExtractedInlineXmlDocuments()[1].get();
+ ASSERT_NE(nullptr, extracted_doc_drawable);
+ el = xml::FindRootElement(extracted_doc_drawable);
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ("vector", el->name);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/NinePatch.cpp b/tools/aapt2/compile/NinePatch.cpp
index 0fc1c5d..eab5c97 100644
--- a/tools/aapt2/compile/NinePatch.cpp
+++ b/tools/aapt2/compile/NinePatch.cpp
@@ -15,20 +15,22 @@
*/
#include "compile/Image.h"
-#include "util/StringPiece.h"
-#include "util/Util.h"
-#include <androidfw/ResourceTypes.h>
#include <sstream>
#include <string>
#include <vector>
+#include "androidfw/ResourceTypes.h"
+
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
namespace aapt {
// Colors in the format 0xAARRGGBB (the way 9-patch expects it).
constexpr static const uint32_t kColorOpaqueWhite = 0xffffffffu;
constexpr static const uint32_t kColorOpaqueBlack = 0xff000000u;
-constexpr static const uint32_t kColorOpaqueRed = 0xffff0000u;
+constexpr static const uint32_t kColorOpaqueRed = 0xffff0000u;
constexpr static const uint32_t kPrimaryColor = kColorOpaqueBlack;
constexpr static const uint32_t kSecondaryColor = kColorOpaqueRed;
@@ -36,7 +38,7 @@
/**
* Returns the alpha value encoded in the 0xAARRGBB encoded pixel.
*/
-static uint32_t getAlpha(uint32_t color);
+static uint32_t get_alpha(uint32_t color);
/**
* Determines whether a color on an ImageLine is valid.
@@ -46,631 +48,651 @@
* but we need to ensure consistency throughout the image.
*/
class ColorValidator {
-public:
- virtual ~ColorValidator() = default;
+ public:
+ virtual ~ColorValidator() = default;
- /**
- * Returns true if the color specified is a neutral color
- * (no padding, stretching, or optical bounds).
- */
- virtual bool isNeutralColor(uint32_t color) const = 0;
+ /**
+ * Returns true if the color specified is a neutral color
+ * (no padding, stretching, or optical bounds).
+ */
+ virtual bool IsNeutralColor(uint32_t color) const = 0;
- /**
- * Returns true if the color is either a neutral color
- * or one denoting padding, stretching, or optical bounds.
- */
- bool isValidColor(uint32_t color) const {
- switch (color) {
- case kPrimaryColor:
- case kSecondaryColor:
- return true;
- }
- return isNeutralColor(color);
+ /**
+ * Returns true if the color is either a neutral color
+ * or one denoting padding, stretching, or optical bounds.
+ */
+ bool IsValidColor(uint32_t color) const {
+ switch (color) {
+ case kPrimaryColor:
+ case kSecondaryColor:
+ return true;
}
+ return IsNeutralColor(color);
+ }
};
// Walks an ImageLine and records Ranges of primary and secondary colors.
-// The primary color is black and is used to denote a padding or stretching range,
+// The primary color is black and is used to denote a padding or stretching
+// range,
// depending on which border we're iterating over.
// The secondary color is red and is used to denote optical bounds.
//
-// An ImageLine is a templated-interface that would look something like this if it
+// An ImageLine is a templated-interface that would look something like this if
+// it
// were polymorphic:
//
// class ImageLine {
// public:
-// virtual int32_t getLength() const = 0;
-// virtual uint32_t getColor(int32_t idx) const = 0;
+// virtual int32_t GetLength() const = 0;
+// virtual uint32_t GetColor(int32_t idx) const = 0;
// };
//
template <typename ImageLine>
-static bool fillRanges(const ImageLine* imageLine,
- const ColorValidator* colorValidator,
- std::vector<Range>* primaryRanges,
- std::vector<Range>* secondaryRanges,
- std::string* err) {
- const int32_t length = imageLine->getLength();
+static bool FillRanges(const ImageLine* image_line,
+ const ColorValidator* color_validator,
+ std::vector<Range>* primary_ranges,
+ std::vector<Range>* secondary_ranges,
+ std::string* out_err) {
+ const int32_t length = image_line->GetLength();
- uint32_t lastColor = 0xffffffffu;
- for (int32_t idx = 1; idx < length - 1; idx++) {
- const uint32_t color = imageLine->getColor(idx);
- if (!colorValidator->isValidColor(color)) {
- *err = "found an invalid color";
- return false;
- }
-
- if (color != lastColor) {
- // We are ending a range. Which range?
- // note: encode the x offset without the final 1 pixel border.
- if (lastColor == kPrimaryColor) {
- primaryRanges->back().end = idx - 1;
- } else if (lastColor == kSecondaryColor) {
- secondaryRanges->back().end = idx - 1;
- }
-
- // We are starting a range. Which range?
- // note: encode the x offset without the final 1 pixel border.
- if (color == kPrimaryColor) {
- primaryRanges->push_back(Range(idx - 1, length - 2));
- } else if (color == kSecondaryColor) {
- secondaryRanges->push_back(Range(idx - 1, length - 2));
- }
- lastColor = color;
- }
+ uint32_t last_color = 0xffffffffu;
+ for (int32_t idx = 1; idx < length - 1; idx++) {
+ const uint32_t color = image_line->GetColor(idx);
+ if (!color_validator->IsValidColor(color)) {
+ *out_err = "found an invalid color";
+ return false;
}
- return true;
+
+ if (color != last_color) {
+ // We are ending a range. Which range?
+ // note: encode the x offset without the final 1 pixel border.
+ if (last_color == kPrimaryColor) {
+ primary_ranges->back().end = idx - 1;
+ } else if (last_color == kSecondaryColor) {
+ secondary_ranges->back().end = idx - 1;
+ }
+
+ // We are starting a range. Which range?
+ // note: encode the x offset without the final 1 pixel border.
+ if (color == kPrimaryColor) {
+ primary_ranges->push_back(Range(idx - 1, length - 2));
+ } else if (color == kSecondaryColor) {
+ secondary_ranges->push_back(Range(idx - 1, length - 2));
+ }
+ last_color = color;
+ }
+ }
+ return true;
}
/**
- * Iterates over a row in an image. Implements the templated ImageLine interface.
+ * Iterates over a row in an image. Implements the templated ImageLine
+ * interface.
*/
class HorizontalImageLine {
-public:
- explicit HorizontalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
- int32_t length) :
- mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {
- }
+ public:
+ explicit HorizontalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset,
+ int32_t length)
+ : rows_(rows), xoffset_(xoffset), yoffset_(yoffset), length_(length) {}
- inline int32_t getLength() const {
- return mLength;
- }
+ inline int32_t GetLength() const { return length_; }
- inline uint32_t getColor(int32_t idx) const {
- return NinePatch::packRGBA(mRows[mYOffset] + (idx + mXOffset) * 4);
- }
+ inline uint32_t GetColor(int32_t idx) const {
+ return NinePatch::PackRGBA(rows_[yoffset_] + (idx + xoffset_) * 4);
+ }
-private:
- uint8_t** mRows;
- int32_t mXOffset, mYOffset, mLength;
+ private:
+ uint8_t** rows_;
+ int32_t xoffset_, yoffset_, length_;
- DISALLOW_COPY_AND_ASSIGN(HorizontalImageLine);
+ DISALLOW_COPY_AND_ASSIGN(HorizontalImageLine);
};
/**
- * Iterates over a column in an image. Implements the templated ImageLine interface.
+ * Iterates over a column in an image. Implements the templated ImageLine
+ * interface.
*/
class VerticalImageLine {
-public:
- explicit VerticalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
- int32_t length) :
- mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {
- }
+ public:
+ explicit VerticalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset,
+ int32_t length)
+ : rows_(rows), xoffset_(xoffset), yoffset_(yoffset), length_(length) {}
- inline int32_t getLength() const {
- return mLength;
- }
+ inline int32_t GetLength() const { return length_; }
- inline uint32_t getColor(int32_t idx) const {
- return NinePatch::packRGBA(mRows[mYOffset + idx] + (mXOffset * 4));
- }
+ inline uint32_t GetColor(int32_t idx) const {
+ return NinePatch::PackRGBA(rows_[yoffset_ + idx] + (xoffset_ * 4));
+ }
-private:
- uint8_t** mRows;
- int32_t mXOffset, mYOffset, mLength;
+ private:
+ uint8_t** rows_;
+ int32_t xoffset_, yoffset_, length_;
- DISALLOW_COPY_AND_ASSIGN(VerticalImageLine);
+ DISALLOW_COPY_AND_ASSIGN(VerticalImageLine);
};
class DiagonalImageLine {
-public:
- explicit DiagonalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
- int32_t xStep, int32_t yStep, int32_t length) :
- mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mXStep(xStep), mYStep(yStep),
- mLength(length) {
- }
+ public:
+ explicit DiagonalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset,
+ int32_t xstep, int32_t ystep, int32_t length)
+ : rows_(rows),
+ xoffset_(xoffset),
+ yoffset_(yoffset),
+ xstep_(xstep),
+ ystep_(ystep),
+ length_(length) {}
- inline int32_t getLength() const {
- return mLength;
- }
+ inline int32_t GetLength() const { return length_; }
- inline uint32_t getColor(int32_t idx) const {
- return NinePatch::packRGBA(
- mRows[mYOffset + (idx * mYStep)] + ((idx + mXOffset) * mXStep) * 4);
- }
+ inline uint32_t GetColor(int32_t idx) const {
+ return NinePatch::PackRGBA(rows_[yoffset_ + (idx * ystep_)] +
+ ((idx + xoffset_) * xstep_) * 4);
+ }
-private:
- uint8_t** mRows;
- int32_t mXOffset, mYOffset, mXStep, mYStep, mLength;
+ private:
+ uint8_t** rows_;
+ int32_t xoffset_, yoffset_, xstep_, ystep_, length_;
- DISALLOW_COPY_AND_ASSIGN(DiagonalImageLine);
+ DISALLOW_COPY_AND_ASSIGN(DiagonalImageLine);
};
class TransparentNeutralColorValidator : public ColorValidator {
-public:
- bool isNeutralColor(uint32_t color) const override {
- return getAlpha(color) == 0;
- }
+ public:
+ bool IsNeutralColor(uint32_t color) const override {
+ return get_alpha(color) == 0;
+ }
};
class WhiteNeutralColorValidator : public ColorValidator {
-public:
- bool isNeutralColor(uint32_t color) const override {
- return color == kColorOpaqueWhite;
- }
+ public:
+ bool IsNeutralColor(uint32_t color) const override {
+ return color == kColorOpaqueWhite;
+ }
};
-inline static uint32_t getAlpha(uint32_t color) {
- return (color & 0xff000000u) >> 24;
+inline static uint32_t get_alpha(uint32_t color) {
+ return (color & 0xff000000u) >> 24;
}
-static bool populateBounds(const std::vector<Range>& padding,
- const std::vector<Range>& layoutBounds,
- const std::vector<Range>& stretchRegions,
- const int32_t length,
- int32_t* paddingStart, int32_t* paddingEnd,
- int32_t* layoutStart, int32_t* layoutEnd,
- const StringPiece& edgeName,
- std::string* err) {
- if (padding.size() > 1) {
- std::stringstream errStream;
- errStream << "too many padding sections on " << edgeName << " border";
- *err = errStream.str();
+static bool PopulateBounds(const std::vector<Range>& padding,
+ const std::vector<Range>& layout_bounds,
+ const std::vector<Range>& stretch_regions,
+ const int32_t length, int32_t* padding_start,
+ int32_t* padding_end, int32_t* layout_start,
+ int32_t* layout_end, const StringPiece& edge_name,
+ std::string* out_err) {
+ if (padding.size() > 1) {
+ std::stringstream err_stream;
+ err_stream << "too many padding sections on " << edge_name << " border";
+ *out_err = err_stream.str();
+ return false;
+ }
+
+ *padding_start = 0;
+ *padding_end = 0;
+ if (!padding.empty()) {
+ const Range& range = padding.front();
+ *padding_start = range.start;
+ *padding_end = length - range.end;
+ } else if (!stretch_regions.empty()) {
+ // No padding was defined. Compute the padding from the first and last
+ // stretch regions.
+ *padding_start = stretch_regions.front().start;
+ *padding_end = length - stretch_regions.back().end;
+ }
+
+ if (layout_bounds.size() > 2) {
+ std::stringstream err_stream;
+ err_stream << "too many layout bounds sections on " << edge_name
+ << " border";
+ *out_err = err_stream.str();
+ return false;
+ }
+
+ *layout_start = 0;
+ *layout_end = 0;
+ if (layout_bounds.size() >= 1) {
+ const Range& range = layout_bounds.front();
+ // If there is only one layout bound segment, it might not start at 0, but
+ // then it should
+ // end at length.
+ if (range.start != 0 && range.end != length) {
+ std::stringstream err_stream;
+ err_stream << "layout bounds on " << edge_name
+ << " border must start at edge";
+ *out_err = err_stream.str();
+ return false;
+ }
+ *layout_start = range.end;
+
+ if (layout_bounds.size() >= 2) {
+ const Range& range = layout_bounds.back();
+ if (range.end != length) {
+ std::stringstream err_stream;
+ err_stream << "layout bounds on " << edge_name
+ << " border must start at edge";
+ *out_err = err_stream.str();
return false;
+ }
+ *layout_end = length - range.start;
}
-
- *paddingStart = 0;
- *paddingEnd = 0;
- if (!padding.empty()) {
- const Range& range = padding.front();
- *paddingStart = range.start;
- *paddingEnd = length - range.end;
- } else if (!stretchRegions.empty()) {
- // No padding was defined. Compute the padding from the first and last
- // stretch regions.
- *paddingStart = stretchRegions.front().start;
- *paddingEnd = length - stretchRegions.back().end;
- }
-
- if (layoutBounds.size() > 2) {
- std::stringstream errStream;
- errStream << "too many layout bounds sections on " << edgeName << " border";
- *err = errStream.str();
- return false;
- }
-
- *layoutStart = 0;
- *layoutEnd = 0;
- if (layoutBounds.size() >= 1) {
- const Range& range = layoutBounds.front();
- // If there is only one layout bound segment, it might not start at 0, but then it should
- // end at length.
- if (range.start != 0 && range.end != length) {
- std::stringstream errStream;
- errStream << "layout bounds on " << edgeName << " border must start at edge";
- *err = errStream.str();
- return false;
- }
- *layoutStart = range.end;
-
- if (layoutBounds.size() >= 2) {
- const Range& range = layoutBounds.back();
- if (range.end != length) {
- std::stringstream errStream;
- errStream << "layout bounds on " << edgeName << " border must start at edge";
- *err = errStream.str();
- return false;
- }
- *layoutEnd = length - range.start;
- }
- }
- return true;
+ }
+ return true;
}
-static int32_t calculateSegmentCount(const std::vector<Range>& stretchRegions, int32_t length) {
- if (stretchRegions.size() == 0) {
- return 0;
- }
+static int32_t CalculateSegmentCount(const std::vector<Range>& stretch_regions,
+ int32_t length) {
+ if (stretch_regions.size() == 0) {
+ return 0;
+ }
- const bool startIsFixed = stretchRegions.front().start != 0;
- const bool endIsFixed = stretchRegions.back().end != length;
- int32_t modifier = 0;
- if (startIsFixed && endIsFixed) {
- modifier = 1;
- } else if (!startIsFixed && !endIsFixed) {
- modifier = -1;
- }
- return static_cast<int32_t>(stretchRegions.size()) * 2 + modifier;
+ const bool start_is_fixed = stretch_regions.front().start != 0;
+ const bool end_is_fixed = stretch_regions.back().end != length;
+ int32_t modifier = 0;
+ if (start_is_fixed && end_is_fixed) {
+ modifier = 1;
+ } else if (!start_is_fixed && !end_is_fixed) {
+ modifier = -1;
+ }
+ return static_cast<int32_t>(stretch_regions.size()) * 2 + modifier;
}
-static uint32_t getRegionColor(uint8_t** rows, const Bounds& region) {
- // Sample the first pixel to compare against.
- const uint32_t expectedColor = NinePatch::packRGBA(rows[region.top] + region.left * 4);
- for (int32_t y = region.top; y < region.bottom; y++) {
- const uint8_t* row = rows[y];
- for (int32_t x = region.left; x < region.right; x++) {
- const uint32_t color = NinePatch::packRGBA(row + x * 4);
- if (getAlpha(color) == 0) {
- // The color is transparent.
- // If the expectedColor is not transparent, NO_COLOR.
- if (getAlpha(expectedColor) != 0) {
- return android::Res_png_9patch::NO_COLOR;
- }
- } else if (color != expectedColor) {
- return android::Res_png_9patch::NO_COLOR;
- }
+static uint32_t GetRegionColor(uint8_t** rows, const Bounds& region) {
+ // Sample the first pixel to compare against.
+ const uint32_t expected_color =
+ NinePatch::PackRGBA(rows[region.top] + region.left * 4);
+ for (int32_t y = region.top; y < region.bottom; y++) {
+ const uint8_t* row = rows[y];
+ for (int32_t x = region.left; x < region.right; x++) {
+ const uint32_t color = NinePatch::PackRGBA(row + x * 4);
+ if (get_alpha(color) == 0) {
+ // The color is transparent.
+ // If the expectedColor is not transparent, NO_COLOR.
+ if (get_alpha(expected_color) != 0) {
+ return android::Res_png_9patch::NO_COLOR;
}
+ } else if (color != expected_color) {
+ return android::Res_png_9patch::NO_COLOR;
+ }
}
+ }
- if (getAlpha(expectedColor) == 0) {
- return android::Res_png_9patch::TRANSPARENT_COLOR;
- }
- return expectedColor;
+ if (get_alpha(expected_color) == 0) {
+ return android::Res_png_9patch::TRANSPARENT_COLOR;
+ }
+ return expected_color;
}
-// Fills outColors with each 9-patch section's colour. If the whole section is transparent,
-// it gets the special TRANSPARENT colour. If the whole section is the same colour, it is assigned
-// that colour. Otherwise it gets the special NO_COLOR colour.
+// Fills out_colors with each 9-patch section's color. If the whole section is
+// transparent,
+// it gets the special TRANSPARENT color. If the whole section is the same
+// color, it is assigned
+// that color. Otherwise it gets the special NO_COLOR color.
//
-// Note that the rows contain the 9-patch 1px border, and the indices in the stretch regions are
-// already offset to exclude the border. This means that each time the rows are accessed,
+// Note that the rows contain the 9-patch 1px border, and the indices in the
+// stretch regions are
+// already offset to exclude the border. This means that each time the rows are
+// accessed,
// the indices must be offset by 1.
//
// width and height also include the 9-patch 1px border.
-static void calculateRegionColors(uint8_t** rows,
- const std::vector<Range>& horizontalStretchRegions,
- const std::vector<Range>& verticalStretchRegions,
- const int32_t width, const int32_t height,
- std::vector<uint32_t>* outColors) {
- int32_t nextTop = 0;
- Bounds bounds;
- auto rowIter = verticalStretchRegions.begin();
- while (nextTop != height) {
- if (rowIter != verticalStretchRegions.end()) {
- if (nextTop != rowIter->start) {
- // This is a fixed segment.
- // Offset the bounds by 1 to accommodate the border.
- bounds.top = nextTop + 1;
- bounds.bottom = rowIter->start + 1;
- nextTop = rowIter->start;
- } else {
- // This is a stretchy segment.
- // Offset the bounds by 1 to accommodate the border.
- bounds.top = rowIter->start + 1;
- bounds.bottom = rowIter->end + 1;
- nextTop = rowIter->end;
- ++rowIter;
- }
- } else {
- // This is the end, fixed section.
- // Offset the bounds by 1 to accommodate the border.
- bounds.top = nextTop + 1;
- bounds.bottom = height + 1;
- nextTop = height;
- }
-
- int32_t nextLeft = 0;
- auto colIter = horizontalStretchRegions.begin();
- while (nextLeft != width) {
- if (colIter != horizontalStretchRegions.end()) {
- if (nextLeft != colIter->start) {
- // This is a fixed segment.
- // Offset the bounds by 1 to accommodate the border.
- bounds.left = nextLeft + 1;
- bounds.right = colIter->start + 1;
- nextLeft = colIter->start;
- } else {
- // This is a stretchy segment.
- // Offset the bounds by 1 to accommodate the border.
- bounds.left = colIter->start + 1;
- bounds.right = colIter->end + 1;
- nextLeft = colIter->end;
- ++colIter;
- }
- } else {
- // This is the end, fixed section.
- // Offset the bounds by 1 to accommodate the border.
- bounds.left = nextLeft + 1;
- bounds.right = width + 1;
- nextLeft = width;
- }
- outColors->push_back(getRegionColor(rows, bounds));
- }
+static void CalculateRegionColors(
+ uint8_t** rows, const std::vector<Range>& horizontal_stretch_regions,
+ const std::vector<Range>& vertical_stretch_regions, const int32_t width,
+ const int32_t height, std::vector<uint32_t>* out_colors) {
+ int32_t next_top = 0;
+ Bounds bounds;
+ auto row_iter = vertical_stretch_regions.begin();
+ while (next_top != height) {
+ if (row_iter != vertical_stretch_regions.end()) {
+ if (next_top != row_iter->start) {
+ // This is a fixed segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = next_top + 1;
+ bounds.bottom = row_iter->start + 1;
+ next_top = row_iter->start;
+ } else {
+ // This is a stretchy segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = row_iter->start + 1;
+ bounds.bottom = row_iter->end + 1;
+ next_top = row_iter->end;
+ ++row_iter;
+ }
+ } else {
+ // This is the end, fixed section.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = next_top + 1;
+ bounds.bottom = height + 1;
+ next_top = height;
}
+
+ int32_t next_left = 0;
+ auto col_iter = horizontal_stretch_regions.begin();
+ while (next_left != width) {
+ if (col_iter != horizontal_stretch_regions.end()) {
+ if (next_left != col_iter->start) {
+ // This is a fixed segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = next_left + 1;
+ bounds.right = col_iter->start + 1;
+ next_left = col_iter->start;
+ } else {
+ // This is a stretchy segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = col_iter->start + 1;
+ bounds.right = col_iter->end + 1;
+ next_left = col_iter->end;
+ ++col_iter;
+ }
+ } else {
+ // This is the end, fixed section.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = next_left + 1;
+ bounds.right = width + 1;
+ next_left = width;
+ }
+ out_colors->push_back(GetRegionColor(rows, bounds));
+ }
+ }
}
-// Calculates the insets of a row/column of pixels based on where the largest alpha value begins
+// Calculates the insets of a row/column of pixels based on where the largest
+// alpha value begins
// (on both sides).
template <typename ImageLine>
-static void findOutlineInsets(const ImageLine* imageLine, int32_t* outStart, int32_t* outEnd) {
- *outStart = 0;
- *outEnd = 0;
+static void FindOutlineInsets(const ImageLine* image_line, int32_t* out_start,
+ int32_t* out_end) {
+ *out_start = 0;
+ *out_end = 0;
- const int32_t length = imageLine->getLength();
- if (length < 3) {
- return;
- }
-
- // If the length is odd, we want both sides to process the center pixel,
- // so we use two different midpoints (to account for < and <= in the different loops).
- const int32_t mid2 = length / 2;
- const int32_t mid1 = mid2 + (length % 2);
-
- uint32_t maxAlpha = 0;
- for (int32_t i = 0; i < mid1 && maxAlpha != 0xff; i++) {
- uint32_t alpha = getAlpha(imageLine->getColor(i));
- if (alpha > maxAlpha) {
- maxAlpha = alpha;
- *outStart = i;
- }
- }
-
- maxAlpha = 0;
- for (int32_t i = length - 1; i >= mid2 && maxAlpha != 0xff; i--) {
- uint32_t alpha = getAlpha(imageLine->getColor(i));
- if (alpha > maxAlpha) {
- maxAlpha = alpha;
- *outEnd = length - (i + 1);
- }
- }
+ const int32_t length = image_line->GetLength();
+ if (length < 3) {
return;
+ }
+
+ // If the length is odd, we want both sides to process the center pixel,
+ // so we use two different midpoints (to account for < and <= in the different
+ // loops).
+ const int32_t mid2 = length / 2;
+ const int32_t mid1 = mid2 + (length % 2);
+
+ uint32_t max_alpha = 0;
+ for (int32_t i = 0; i < mid1 && max_alpha != 0xff; i++) {
+ uint32_t alpha = get_alpha(image_line->GetColor(i));
+ if (alpha > max_alpha) {
+ max_alpha = alpha;
+ *out_start = i;
+ }
+ }
+
+ max_alpha = 0;
+ for (int32_t i = length - 1; i >= mid2 && max_alpha != 0xff; i--) {
+ uint32_t alpha = get_alpha(image_line->GetColor(i));
+ if (alpha > max_alpha) {
+ max_alpha = alpha;
+ *out_end = length - (i + 1);
+ }
+ }
+ return;
}
template <typename ImageLine>
-static uint32_t findMaxAlpha(const ImageLine* imageLine) {
- const int32_t length = imageLine->getLength();
- uint32_t maxAlpha = 0;
- for (int32_t idx = 0; idx < length && maxAlpha != 0xff; idx++) {
- uint32_t alpha = getAlpha(imageLine->getColor(idx));
- if (alpha > maxAlpha) {
- maxAlpha = alpha;
- }
+static uint32_t FindMaxAlpha(const ImageLine* image_line) {
+ const int32_t length = image_line->GetLength();
+ uint32_t max_alpha = 0;
+ for (int32_t idx = 0; idx < length && max_alpha != 0xff; idx++) {
+ uint32_t alpha = get_alpha(image_line->GetColor(idx));
+ if (alpha > max_alpha) {
+ max_alpha = alpha;
}
- return maxAlpha;
+ }
+ return max_alpha;
}
// Pack the pixels in as 0xAARRGGBB (as 9-patch expects it).
-uint32_t NinePatch::packRGBA(const uint8_t* pixel) {
- return (pixel[3] << 24) | (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
+uint32_t NinePatch::PackRGBA(const uint8_t* pixel) {
+ return (pixel[3] << 24) | (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
}
-std::unique_ptr<NinePatch> NinePatch::create(uint8_t** rows,
- const int32_t width, const int32_t height,
- std::string* err) {
- if (width < 3 || height < 3) {
- *err = "image must be at least 3x3 (1x1 image with 1 pixel border)";
- return {};
- }
+std::unique_ptr<NinePatch> NinePatch::Create(uint8_t** rows,
+ const int32_t width,
+ const int32_t height,
+ std::string* out_err) {
+ if (width < 3 || height < 3) {
+ *out_err = "image must be at least 3x3 (1x1 image with 1 pixel border)";
+ return {};
+ }
- std::vector<Range> horizontalPadding;
- std::vector<Range> horizontalOpticalBounds;
- std::vector<Range> verticalPadding;
- std::vector<Range> verticalOpticalBounds;
- std::vector<Range> unexpectedRanges;
- std::unique_ptr<ColorValidator> colorValidator;
+ std::vector<Range> horizontal_padding;
+ std::vector<Range> horizontal_layout_bounds;
+ std::vector<Range> vertical_padding;
+ std::vector<Range> vertical_layout_bounds;
+ std::vector<Range> unexpected_ranges;
+ std::unique_ptr<ColorValidator> color_validator;
- if (rows[0][3] == 0) {
- colorValidator = util::make_unique<TransparentNeutralColorValidator>();
- } else if (packRGBA(rows[0]) == kColorOpaqueWhite) {
- colorValidator = util::make_unique<WhiteNeutralColorValidator>();
- } else {
- *err = "top-left corner pixel must be either opaque white or transparent";
- return {};
- }
+ if (rows[0][3] == 0) {
+ color_validator = util::make_unique<TransparentNeutralColorValidator>();
+ } else if (PackRGBA(rows[0]) == kColorOpaqueWhite) {
+ color_validator = util::make_unique<WhiteNeutralColorValidator>();
+ } else {
+ *out_err =
+ "top-left corner pixel must be either opaque white or transparent";
+ return {};
+ }
- // Private constructor, can't use make_unique.
- auto ninePatch = std::unique_ptr<NinePatch>(new NinePatch());
+ // Private constructor, can't use make_unique.
+ auto nine_patch = std::unique_ptr<NinePatch>(new NinePatch());
- HorizontalImageLine topRow(rows, 0, 0, width);
- if (!fillRanges(&topRow, colorValidator.get(), &ninePatch->horizontalStretchRegions,
- &unexpectedRanges, err)) {
- return {};
- }
+ HorizontalImageLine top_row(rows, 0, 0, width);
+ if (!FillRanges(&top_row, color_validator.get(),
+ &nine_patch->horizontal_stretch_regions, &unexpected_ranges,
+ out_err)) {
+ return {};
+ }
- if (!unexpectedRanges.empty()) {
- const Range& range = unexpectedRanges[0];
- std::stringstream errStream;
- errStream << "found unexpected optical bounds (red pixel) on top border "
- << "at x=" << range.start + 1;
- *err = errStream.str();
- return {};
- }
+ if (!unexpected_ranges.empty()) {
+ const Range& range = unexpected_ranges[0];
+ std::stringstream err_stream;
+ err_stream << "found unexpected optical bounds (red pixel) on top border "
+ << "at x=" << range.start + 1;
+ *out_err = err_stream.str();
+ return {};
+ }
- VerticalImageLine leftCol(rows, 0, 0, height);
- if (!fillRanges(&leftCol, colorValidator.get(), &ninePatch->verticalStretchRegions,
- &unexpectedRanges, err)) {
- return {};
- }
+ VerticalImageLine left_col(rows, 0, 0, height);
+ if (!FillRanges(&left_col, color_validator.get(),
+ &nine_patch->vertical_stretch_regions, &unexpected_ranges,
+ out_err)) {
+ return {};
+ }
- if (!unexpectedRanges.empty()) {
- const Range& range = unexpectedRanges[0];
- std::stringstream errStream;
- errStream << "found unexpected optical bounds (red pixel) on left border "
- << "at y=" << range.start + 1;
- return {};
- }
+ if (!unexpected_ranges.empty()) {
+ const Range& range = unexpected_ranges[0];
+ std::stringstream err_stream;
+ err_stream << "found unexpected optical bounds (red pixel) on left border "
+ << "at y=" << range.start + 1;
+ return {};
+ }
- HorizontalImageLine bottomRow(rows, 0, height - 1, width);
- if (!fillRanges(&bottomRow, colorValidator.get(), &horizontalPadding,
- &horizontalOpticalBounds, err)) {
- return {};
- }
+ HorizontalImageLine bottom_row(rows, 0, height - 1, width);
+ if (!FillRanges(&bottom_row, color_validator.get(), &horizontal_padding,
+ &horizontal_layout_bounds, out_err)) {
+ return {};
+ }
- if (!populateBounds(horizontalPadding, horizontalOpticalBounds,
- ninePatch->horizontalStretchRegions, width - 2,
- &ninePatch->padding.left, &ninePatch->padding.right,
- &ninePatch->layoutBounds.left, &ninePatch->layoutBounds.right,
- "bottom", err)) {
- return {};
- }
+ if (!PopulateBounds(horizontal_padding, horizontal_layout_bounds,
+ nine_patch->horizontal_stretch_regions, width - 2,
+ &nine_patch->padding.left, &nine_patch->padding.right,
+ &nine_patch->layout_bounds.left,
+ &nine_patch->layout_bounds.right, "bottom", out_err)) {
+ return {};
+ }
- VerticalImageLine rightCol(rows, width - 1, 0, height);
- if (!fillRanges(&rightCol, colorValidator.get(), &verticalPadding,
- &verticalOpticalBounds, err)) {
- return {};
- }
+ VerticalImageLine right_col(rows, width - 1, 0, height);
+ if (!FillRanges(&right_col, color_validator.get(), &vertical_padding,
+ &vertical_layout_bounds, out_err)) {
+ return {};
+ }
- if (!populateBounds(verticalPadding, verticalOpticalBounds,
- ninePatch->verticalStretchRegions, height - 2,
- &ninePatch->padding.top, &ninePatch->padding.bottom,
- &ninePatch->layoutBounds.top, &ninePatch->layoutBounds.bottom,
- "right", err)) {
- return {};
- }
+ if (!PopulateBounds(vertical_padding, vertical_layout_bounds,
+ nine_patch->vertical_stretch_regions, height - 2,
+ &nine_patch->padding.top, &nine_patch->padding.bottom,
+ &nine_patch->layout_bounds.top,
+ &nine_patch->layout_bounds.bottom, "right", out_err)) {
+ return {};
+ }
- // Fill the region colors of the 9-patch.
- const int32_t numRows = calculateSegmentCount(ninePatch->horizontalStretchRegions, width - 2);
- const int32_t numCols = calculateSegmentCount(ninePatch->verticalStretchRegions, height - 2);
- if ((int64_t) numRows * (int64_t) numCols > 0x7f) {
- *err = "too many regions in 9-patch";
- return {};
- }
+ // Fill the region colors of the 9-patch.
+ const int32_t num_rows =
+ CalculateSegmentCount(nine_patch->horizontal_stretch_regions, width - 2);
+ const int32_t num_cols =
+ CalculateSegmentCount(nine_patch->vertical_stretch_regions, height - 2);
+ if ((int64_t)num_rows * (int64_t)num_cols > 0x7f) {
+ *out_err = "too many regions in 9-patch";
+ return {};
+ }
- ninePatch->regionColors.reserve(numRows * numCols);
- calculateRegionColors(rows, ninePatch->horizontalStretchRegions,
- ninePatch->verticalStretchRegions,
- width - 2, height - 2,
- &ninePatch->regionColors);
+ nine_patch->region_colors.reserve(num_rows * num_cols);
+ CalculateRegionColors(rows, nine_patch->horizontal_stretch_regions,
+ nine_patch->vertical_stretch_regions, width - 2,
+ height - 2, &nine_patch->region_colors);
- // Compute the outline based on opacity.
+ // Compute the outline based on opacity.
- // Find left and right extent of 9-patch content on center row.
- HorizontalImageLine midRow(rows, 1, height / 2, width - 2);
- findOutlineInsets(&midRow, &ninePatch->outline.left, &ninePatch->outline.right);
+ // Find left and right extent of 9-patch content on center row.
+ HorizontalImageLine mid_row(rows, 1, height / 2, width - 2);
+ FindOutlineInsets(&mid_row, &nine_patch->outline.left,
+ &nine_patch->outline.right);
- // Find top and bottom extent of 9-patch content on center column.
- VerticalImageLine midCol(rows, width / 2, 1, height - 2);
- findOutlineInsets(&midCol, &ninePatch->outline.top, &ninePatch->outline.bottom);
+ // Find top and bottom extent of 9-patch content on center column.
+ VerticalImageLine mid_col(rows, width / 2, 1, height - 2);
+ FindOutlineInsets(&mid_col, &nine_patch->outline.top,
+ &nine_patch->outline.bottom);
- const int32_t outlineWidth = (width - 2) - ninePatch->outline.left - ninePatch->outline.right;
- const int32_t outlineHeight = (height - 2) - ninePatch->outline.top - ninePatch->outline.bottom;
+ const int32_t outline_width =
+ (width - 2) - nine_patch->outline.left - nine_patch->outline.right;
+ const int32_t outline_height =
+ (height - 2) - nine_patch->outline.top - nine_patch->outline.bottom;
- // Find the largest alpha value within the outline area.
- HorizontalImageLine outlineMidRow(rows,
- 1 + ninePatch->outline.left,
- 1 + ninePatch->outline.top + (outlineHeight / 2),
- outlineWidth);
- VerticalImageLine outlineMidCol(rows,
- 1 + ninePatch->outline.left + (outlineWidth / 2),
- 1 + ninePatch->outline.top,
- outlineHeight);
- ninePatch->outlineAlpha = std::max(findMaxAlpha(&outlineMidRow), findMaxAlpha(&outlineMidCol));
+ // Find the largest alpha value within the outline area.
+ HorizontalImageLine outline_mid_row(
+ rows, 1 + nine_patch->outline.left,
+ 1 + nine_patch->outline.top + (outline_height / 2), outline_width);
+ VerticalImageLine outline_mid_col(
+ rows, 1 + nine_patch->outline.left + (outline_width / 2),
+ 1 + nine_patch->outline.top, outline_height);
+ nine_patch->outline_alpha =
+ std::max(FindMaxAlpha(&outline_mid_row), FindMaxAlpha(&outline_mid_col));
- // Assuming the image is a round rect, compute the radius by marching
- // diagonally from the top left corner towards the center.
- DiagonalImageLine diagonal(rows, 1 + ninePatch->outline.left, 1 + ninePatch->outline.top,
- 1, 1, std::min(outlineWidth, outlineHeight));
- int32_t topLeft, bottomRight;
- findOutlineInsets(&diagonal, &topLeft, &bottomRight);
+ // Assuming the image is a round rect, compute the radius by marching
+ // diagonally from the top left corner towards the center.
+ DiagonalImageLine diagonal(rows, 1 + nine_patch->outline.left,
+ 1 + nine_patch->outline.top, 1, 1,
+ std::min(outline_width, outline_height));
+ int32_t top_left, bottom_right;
+ FindOutlineInsets(&diagonal, &top_left, &bottom_right);
- /* Determine source radius based upon inset:
- * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
- * sqrt(2) * r = sqrt(2) * i + r
- * (sqrt(2) - 1) * r = sqrt(2) * i
- * r = sqrt(2) / (sqrt(2) - 1) * i
- */
- ninePatch->outlineRadius = 3.4142f * topLeft;
- return ninePatch;
+ /* Determine source radius based upon inset:
+ * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
+ * sqrt(2) * r = sqrt(2) * i + r
+ * (sqrt(2) - 1) * r = sqrt(2) * i
+ * r = sqrt(2) / (sqrt(2) - 1) * i
+ */
+ nine_patch->outline_radius = 3.4142f * top_left;
+ return nine_patch;
}
-std::unique_ptr<uint8_t[]> NinePatch::serializeBase(size_t* outLen) const {
- android::Res_png_9patch data;
- data.numXDivs = static_cast<uint8_t>(horizontalStretchRegions.size()) * 2;
- data.numYDivs = static_cast<uint8_t>(verticalStretchRegions.size()) * 2;
- data.numColors = static_cast<uint8_t>(regionColors.size());
- data.paddingLeft = padding.left;
- data.paddingRight = padding.right;
- data.paddingTop = padding.top;
- data.paddingBottom = padding.bottom;
+std::unique_ptr<uint8_t[]> NinePatch::SerializeBase(size_t* outLen) const {
+ android::Res_png_9patch data;
+ data.numXDivs = static_cast<uint8_t>(horizontal_stretch_regions.size()) * 2;
+ data.numYDivs = static_cast<uint8_t>(vertical_stretch_regions.size()) * 2;
+ data.numColors = static_cast<uint8_t>(region_colors.size());
+ data.paddingLeft = padding.left;
+ data.paddingRight = padding.right;
+ data.paddingTop = padding.top;
+ data.paddingBottom = padding.bottom;
- auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[data.serializedSize()]);
- android::Res_png_9patch::serialize(data,
- (const int32_t*) horizontalStretchRegions.data(),
- (const int32_t*) verticalStretchRegions.data(),
- regionColors.data(),
- buffer.get());
- // Convert to file endianness.
- reinterpret_cast<android::Res_png_9patch*>(buffer.get())->deviceToFile();
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[data.serializedSize()]);
+ android::Res_png_9patch::serialize(
+ data, (const int32_t*)horizontal_stretch_regions.data(),
+ (const int32_t*)vertical_stretch_regions.data(), region_colors.data(),
+ buffer.get());
+ // Convert to file endianness.
+ reinterpret_cast<android::Res_png_9patch*>(buffer.get())->deviceToFile();
- *outLen = data.serializedSize();
- return buffer;
+ *outLen = data.serializedSize();
+ return buffer;
}
-std::unique_ptr<uint8_t[]> NinePatch::serializeLayoutBounds(size_t* outLen) const {
- size_t chunkLen = sizeof(uint32_t) * 4;
- auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
- uint8_t* cursor = buffer.get();
+std::unique_ptr<uint8_t[]> NinePatch::SerializeLayoutBounds(
+ size_t* out_len) const {
+ size_t chunk_len = sizeof(uint32_t) * 4;
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunk_len]);
+ uint8_t* cursor = buffer.get();
- memcpy(cursor, &layoutBounds.left, sizeof(layoutBounds.left));
- cursor += sizeof(layoutBounds.left);
+ memcpy(cursor, &layout_bounds.left, sizeof(layout_bounds.left));
+ cursor += sizeof(layout_bounds.left);
- memcpy(cursor, &layoutBounds.top, sizeof(layoutBounds.top));
- cursor += sizeof(layoutBounds.top);
+ memcpy(cursor, &layout_bounds.top, sizeof(layout_bounds.top));
+ cursor += sizeof(layout_bounds.top);
- memcpy(cursor, &layoutBounds.right, sizeof(layoutBounds.right));
- cursor += sizeof(layoutBounds.right);
+ memcpy(cursor, &layout_bounds.right, sizeof(layout_bounds.right));
+ cursor += sizeof(layout_bounds.right);
- memcpy(cursor, &layoutBounds.bottom, sizeof(layoutBounds.bottom));
- cursor += sizeof(layoutBounds.bottom);
+ memcpy(cursor, &layout_bounds.bottom, sizeof(layout_bounds.bottom));
+ cursor += sizeof(layout_bounds.bottom);
- *outLen = chunkLen;
- return buffer;
+ *out_len = chunk_len;
+ return buffer;
}
-std::unique_ptr<uint8_t[]> NinePatch::serializeRoundedRectOutline(size_t* outLen) const {
- size_t chunkLen = sizeof(uint32_t) * 6;
- auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
- uint8_t* cursor = buffer.get();
+std::unique_ptr<uint8_t[]> NinePatch::SerializeRoundedRectOutline(
+ size_t* out_len) const {
+ size_t chunk_len = sizeof(uint32_t) * 6;
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunk_len]);
+ uint8_t* cursor = buffer.get();
- memcpy(cursor, &outline.left, sizeof(outline.left));
- cursor += sizeof(outline.left);
+ memcpy(cursor, &outline.left, sizeof(outline.left));
+ cursor += sizeof(outline.left);
- memcpy(cursor, &outline.top, sizeof(outline.top));
- cursor += sizeof(outline.top);
+ memcpy(cursor, &outline.top, sizeof(outline.top));
+ cursor += sizeof(outline.top);
- memcpy(cursor, &outline.right, sizeof(outline.right));
- cursor += sizeof(outline.right);
+ memcpy(cursor, &outline.right, sizeof(outline.right));
+ cursor += sizeof(outline.right);
- memcpy(cursor, &outline.bottom, sizeof(outline.bottom));
- cursor += sizeof(outline.bottom);
+ memcpy(cursor, &outline.bottom, sizeof(outline.bottom));
+ cursor += sizeof(outline.bottom);
- *((float*) cursor) = outlineRadius;
- cursor += sizeof(outlineRadius);
+ *((float*)cursor) = outline_radius;
+ cursor += sizeof(outline_radius);
- *((uint32_t*) cursor) = outlineAlpha;
+ *((uint32_t*)cursor) = outline_alpha;
- *outLen = chunkLen;
- return buffer;
+ *out_len = chunk_len;
+ return buffer;
}
::std::ostream& operator<<(::std::ostream& out, const Range& range) {
- return out << "[" << range.start << ", " << range.end << ")";
+ return out << "[" << range.start << ", " << range.end << ")";
}
::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds) {
- return out << "l=" << bounds.left
- << " t=" << bounds.top
- << " r=" << bounds.right
- << " b=" << bounds.bottom;
+ return out << "l=" << bounds.left << " t=" << bounds.top
+ << " r=" << bounds.right << " b=" << bounds.bottom;
}
-::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch) {
- return out << "horizontalStretch:" << util::joiner(ninePatch.horizontalStretchRegions, " ")
- << " verticalStretch:" << util::joiner(ninePatch.verticalStretchRegions, " ")
- << " padding: " << ninePatch.padding
- << ", bounds: " << ninePatch.layoutBounds
- << ", outline: " << ninePatch.outline
- << " rad=" << ninePatch.outlineRadius
- << " alpha=" << ninePatch.outlineAlpha;
+::std::ostream& operator<<(::std::ostream& out, const NinePatch& nine_patch) {
+ return out << "horizontalStretch:"
+ << util::Joiner(nine_patch.horizontal_stretch_regions, " ")
+ << " verticalStretch:"
+ << util::Joiner(nine_patch.vertical_stretch_regions, " ")
+ << " padding: " << nine_patch.padding
+ << ", bounds: " << nine_patch.layout_bounds
+ << ", outline: " << nine_patch.outline
+ << " rad=" << nine_patch.outline_radius
+ << " alpha=" << nine_patch.outline_alpha;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/NinePatch_test.cpp b/tools/aapt2/compile/NinePatch_test.cpp
index 3106ff8..f54bb2e 100644
--- a/tools/aapt2/compile/NinePatch_test.cpp
+++ b/tools/aapt2/compile/NinePatch_test.cpp
@@ -15,14 +15,15 @@
*/
#include "compile/Image.h"
+
#include "test/Test.h"
namespace aapt {
// Pixels are in RGBA_8888 packing.
-#define RED "\xff\x00\x00\xff"
-#define BLUE "\x00\x00\xff\xff"
+#define RED "\xff\x00\x00\xff"
+#define BLUE "\x00\x00\xff\xff"
#define GREEN "\xff\x00\x00\xff"
#define GR_70 "\xff\x00\x00\xb3"
#define GR_50 "\xff\x00\x00\x80"
@@ -32,327 +33,345 @@
#define TRANS "\x00\x00\x00\x00"
static uint8_t* k2x2[] = {
- (uint8_t*) WHITE WHITE,
- (uint8_t*) WHITE WHITE,
+ (uint8_t*)WHITE WHITE, (uint8_t*)WHITE WHITE,
};
static uint8_t* kMixedNeutralColor3x3[] = {
- (uint8_t*) WHITE BLACK TRANS,
- (uint8_t*) TRANS RED TRANS,
- (uint8_t*) WHITE WHITE WHITE,
+ (uint8_t*)WHITE BLACK TRANS, (uint8_t*)TRANS RED TRANS,
+ (uint8_t*)WHITE WHITE WHITE,
};
static uint8_t* kTransparentNeutralColor3x3[] = {
- (uint8_t*) TRANS BLACK TRANS,
- (uint8_t*) BLACK RED BLACK,
- (uint8_t*) TRANS BLACK TRANS,
+ (uint8_t*)TRANS BLACK TRANS, (uint8_t*)BLACK RED BLACK,
+ (uint8_t*)TRANS BLACK TRANS,
};
static uint8_t* kSingleStretch7x6[] = {
- (uint8_t*) WHITE WHITE BLACK BLACK BLACK WHITE WHITE,
- (uint8_t*) WHITE RED RED RED RED RED WHITE,
- (uint8_t*) BLACK RED RED RED RED RED WHITE,
- (uint8_t*) BLACK RED RED RED RED RED WHITE,
- (uint8_t*) WHITE RED RED RED RED RED WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE BLACK BLACK BLACK WHITE WHITE,
+ (uint8_t*)WHITE RED RED RED RED RED WHITE,
+ (uint8_t*)BLACK RED RED RED RED RED WHITE,
+ (uint8_t*)BLACK RED RED RED RED RED WHITE,
+ (uint8_t*)WHITE RED RED RED RED RED WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kMultipleStretch10x7[] = {
- (uint8_t*) WHITE WHITE BLACK WHITE BLACK BLACK WHITE BLACK WHITE WHITE,
- (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
- (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
- (uint8_t*) WHITE RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
- (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
- (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE BLACK WHITE BLACK BLACK WHITE BLACK WHITE WHITE,
+ (uint8_t*)BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*)BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*)WHITE RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*)BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*)BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kPadding6x5[] = {
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE BLACK,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE BLACK BLACK WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE BLACK,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE BLACK BLACK WHITE WHITE,
};
static uint8_t* kLayoutBoundsWrongEdge3x3[] = {
- (uint8_t*) WHITE RED WHITE,
- (uint8_t*) RED WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE,
+ (uint8_t*)WHITE RED WHITE, (uint8_t*)RED WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE,
};
static uint8_t* kLayoutBoundsNotEdgeAligned5x5[] = {
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE RED WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE RED WHITE WHITE,
};
static uint8_t* kLayoutBounds5x5[] = {
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE RED WHITE RED WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE RED WHITE RED WHITE,
};
static uint8_t* kAsymmetricLayoutBounds5x5[] = {
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE RED WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE RED WHITE WHITE WHITE,
};
static uint8_t* kPaddingAndLayoutBounds5x5[] = {
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE WHITE WHITE WHITE BLACK,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE RED BLACK RED WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE WHITE WHITE WHITE BLACK,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE RED BLACK RED WHITE,
};
static uint8_t* kColorfulImage5x5[] = {
- (uint8_t*) WHITE BLACK WHITE BLACK WHITE,
- (uint8_t*) BLACK RED BLUE GREEN WHITE,
- (uint8_t*) BLACK RED GREEN GREEN WHITE,
- (uint8_t*) WHITE TRANS BLUE GREEN WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE BLACK WHITE BLACK WHITE,
+ (uint8_t*)BLACK RED BLUE GREEN WHITE,
+ (uint8_t*)BLACK RED GREEN GREEN WHITE,
+ (uint8_t*)WHITE TRANS BLUE GREEN WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kOutlineOpaque10x10[] = {
- (uint8_t*) WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kOutlineTranslucent10x10[] = {
- (uint8_t*) WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
- (uint8_t*) WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*)WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kOutlineOffsetTranslucent12x10[] = {
- (uint8_t*) WHITE WHITE WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)
+ WHITE WHITE WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kOutlineRadius5x5[] = {
- (uint8_t*) WHITE BLACK BLACK BLACK WHITE,
- (uint8_t*) BLACK TRANS GREEN TRANS WHITE,
- (uint8_t*) BLACK GREEN GREEN GREEN WHITE,
- (uint8_t*) BLACK TRANS GREEN TRANS WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE BLACK BLACK BLACK WHITE,
+ (uint8_t*)BLACK TRANS GREEN TRANS WHITE,
+ (uint8_t*)BLACK GREEN GREEN GREEN WHITE,
+ (uint8_t*)BLACK TRANS GREEN TRANS WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kStretchAndPadding5x5[] = {
- (uint8_t*) WHITE WHITE BLACK WHITE WHITE,
- (uint8_t*) WHITE RED RED RED WHITE,
- (uint8_t*) BLACK RED RED RED BLACK,
- (uint8_t*) WHITE RED RED RED WHITE,
- (uint8_t*) WHITE WHITE BLACK WHITE WHITE,
+ (uint8_t*)WHITE WHITE BLACK WHITE WHITE, (uint8_t*)WHITE RED RED RED WHITE,
+ (uint8_t*)BLACK RED RED RED BLACK, (uint8_t*)WHITE RED RED RED WHITE,
+ (uint8_t*)WHITE WHITE BLACK WHITE WHITE,
};
TEST(NinePatchTest, Minimum3x3) {
- std::string err;
- EXPECT_EQ(nullptr, NinePatch::create(k2x2, 2, 2, &err));
- EXPECT_FALSE(err.empty());
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::Create(k2x2, 2, 2, &err));
+ EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, MixedNeutralColors) {
- std::string err;
- EXPECT_EQ(nullptr, NinePatch::create(kMixedNeutralColor3x3, 3, 3, &err));
- EXPECT_FALSE(err.empty());
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::Create(kMixedNeutralColor3x3, 3, 3, &err));
+ EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, TransparentNeutralColor) {
- std::string err;
- EXPECT_NE(nullptr, NinePatch::create(kTransparentNeutralColor3x3, 3, 3, &err));
+ std::string err;
+ EXPECT_NE(nullptr,
+ NinePatch::Create(kTransparentNeutralColor3x3, 3, 3, &err));
}
TEST(NinePatchTest, SingleStretchRegion) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kSingleStretch7x6, 7, 6, &err);
- ASSERT_NE(nullptr, ninePatch);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kSingleStretch7x6, 7, 6, &err);
+ ASSERT_NE(nullptr, nine_patch);
- ASSERT_EQ(1u, ninePatch->horizontalStretchRegions.size());
- ASSERT_EQ(1u, ninePatch->verticalStretchRegions.size());
+ ASSERT_EQ(1u, nine_patch->horizontal_stretch_regions.size());
+ ASSERT_EQ(1u, nine_patch->vertical_stretch_regions.size());
- EXPECT_EQ(Range(1, 4), ninePatch->horizontalStretchRegions.front());
- EXPECT_EQ(Range(1, 3), ninePatch->verticalStretchRegions.front());
+ EXPECT_EQ(Range(1, 4), nine_patch->horizontal_stretch_regions.front());
+ EXPECT_EQ(Range(1, 3), nine_patch->vertical_stretch_regions.front());
}
TEST(NinePatchTest, MultipleStretchRegions) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
- ASSERT_NE(nullptr, ninePatch);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kMultipleStretch10x7, 10, 7, &err);
+ ASSERT_NE(nullptr, nine_patch);
- ASSERT_EQ(3u, ninePatch->horizontalStretchRegions.size());
- ASSERT_EQ(2u, ninePatch->verticalStretchRegions.size());
+ ASSERT_EQ(3u, nine_patch->horizontal_stretch_regions.size());
+ ASSERT_EQ(2u, nine_patch->vertical_stretch_regions.size());
- EXPECT_EQ(Range(1, 2), ninePatch->horizontalStretchRegions[0]);
- EXPECT_EQ(Range(3, 5), ninePatch->horizontalStretchRegions[1]);
- EXPECT_EQ(Range(6, 7), ninePatch->horizontalStretchRegions[2]);
+ EXPECT_EQ(Range(1, 2), nine_patch->horizontal_stretch_regions[0]);
+ EXPECT_EQ(Range(3, 5), nine_patch->horizontal_stretch_regions[1]);
+ EXPECT_EQ(Range(6, 7), nine_patch->horizontal_stretch_regions[2]);
- EXPECT_EQ(Range(0, 2), ninePatch->verticalStretchRegions[0]);
- EXPECT_EQ(Range(3, 5), ninePatch->verticalStretchRegions[1]);
+ EXPECT_EQ(Range(0, 2), nine_patch->vertical_stretch_regions[0]);
+ EXPECT_EQ(Range(3, 5), nine_patch->vertical_stretch_regions[1]);
}
TEST(NinePatchTest, InferPaddingFromStretchRegions) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(1, 0, 1, 0), ninePatch->padding);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kMultipleStretch10x7, 10, 7, &err);
+ ASSERT_NE(nullptr, nine_patch);
+ EXPECT_EQ(Bounds(1, 0, 1, 0), nine_patch->padding);
}
TEST(NinePatchTest, Padding) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kPadding6x5, 6, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kPadding6x5, 6, 5, &err);
+ ASSERT_NE(nullptr, nine_patch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->padding);
}
TEST(NinePatchTest, LayoutBoundsAreOnWrongEdge) {
- std::string err;
- EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsWrongEdge3x3, 3, 3, &err));
- EXPECT_FALSE(err.empty());
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::Create(kLayoutBoundsWrongEdge3x3, 3, 3, &err));
+ EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, LayoutBoundsMustTouchEdges) {
- std::string err;
- EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsNotEdgeAligned5x5, 5, 5, &err));
- EXPECT_FALSE(err.empty());
+ std::string err;
+ EXPECT_EQ(nullptr,
+ NinePatch::Create(kLayoutBoundsNotEdgeAligned5x5, 5, 5, &err));
+ EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, LayoutBounds) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kLayoutBounds5x5, 5, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, nine_patch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->layout_bounds);
- ninePatch = NinePatch::create(kAsymmetricLayoutBounds5x5, 5, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(1, 1, 0, 0), ninePatch->layoutBounds);
+ nine_patch = NinePatch::Create(kAsymmetricLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, nine_patch);
+ EXPECT_EQ(Bounds(1, 1, 0, 0), nine_patch->layout_bounds);
}
TEST(NinePatchTest, PaddingAndLayoutBounds) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kPaddingAndLayoutBounds5x5, 5, 5,
- &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
- EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kPaddingAndLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, nine_patch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->padding);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->layout_bounds);
}
TEST(NinePatchTest, RegionColorsAreCorrect) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kColorfulImage5x5, 5, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kColorfulImage5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, nine_patch);
- std::vector<uint32_t> expectedColors = {
- NinePatch::packRGBA((uint8_t*) RED),
- (uint32_t) android::Res_png_9patch::NO_COLOR,
- NinePatch::packRGBA((uint8_t*) GREEN),
- (uint32_t) android::Res_png_9patch::TRANSPARENT_COLOR,
- NinePatch::packRGBA((uint8_t*) BLUE),
- NinePatch::packRGBA((uint8_t*) GREEN),
- };
- EXPECT_EQ(expectedColors, ninePatch->regionColors);
+ std::vector<uint32_t> expected_colors = {
+ NinePatch::PackRGBA((uint8_t*)RED),
+ (uint32_t)android::Res_png_9patch::NO_COLOR,
+ NinePatch::PackRGBA((uint8_t*)GREEN),
+ (uint32_t)android::Res_png_9patch::TRANSPARENT_COLOR,
+ NinePatch::PackRGBA((uint8_t*)BLUE),
+ NinePatch::PackRGBA((uint8_t*)GREEN),
+ };
+ EXPECT_EQ(expected_colors, nine_patch->region_colors);
}
TEST(NinePatchTest, OutlineFromOpaqueImage) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineOpaque10x10, 10, 10, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(2, 2, 2, 2), ninePatch->outline);
- EXPECT_EQ(0x000000ffu, ninePatch->outlineAlpha);
- EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kOutlineOpaque10x10, 10, 10, &err);
+ ASSERT_NE(nullptr, nine_patch);
+ EXPECT_EQ(Bounds(2, 2, 2, 2), nine_patch->outline);
+ EXPECT_EQ(0x000000ffu, nine_patch->outline_alpha);
+ EXPECT_EQ(0.0f, nine_patch->outline_radius);
}
TEST(NinePatchTest, OutlineFromTranslucentImage) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineTranslucent10x10, 10, 10,
- &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(3, 3, 3, 3), ninePatch->outline);
- EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
- EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kOutlineTranslucent10x10, 10, 10, &err);
+ ASSERT_NE(nullptr, nine_patch);
+ EXPECT_EQ(Bounds(3, 3, 3, 3), nine_patch->outline);
+ EXPECT_EQ(0x000000b3u, nine_patch->outline_alpha);
+ EXPECT_EQ(0.0f, nine_patch->outline_radius);
}
TEST(NinePatchTest, OutlineFromOffCenterImage) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineOffsetTranslucent12x10, 12, 10,
- &err);
- ASSERT_NE(nullptr, ninePatch);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kOutlineOffsetTranslucent12x10, 12, 10, &err);
+ ASSERT_NE(nullptr, nine_patch);
- // TODO(adamlesinski): The old AAPT algorithm searches from the outside to the middle
- // for each inset. If the outline is shifted, the search may not find a closer bounds.
- // This check should be:
- // EXPECT_EQ(Bounds(5, 3, 3, 3), ninePatch->outline);
- // but until I know what behaviour I'm breaking, I will leave it at the incorrect:
- EXPECT_EQ(Bounds(4, 3, 3, 3), ninePatch->outline);
+ // TODO(adamlesinski): The old AAPT algorithm searches from the outside to the
+ // middle for each inset. If the outline is shifted, the search may not find a
+ // closer bounds.
+ // This check should be:
+ // EXPECT_EQ(Bounds(5, 3, 3, 3), ninePatch->outline);
+ // but until I know what behavior I'm breaking, I will leave it at the
+ // incorrect:
+ EXPECT_EQ(Bounds(4, 3, 3, 3), nine_patch->outline);
- EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
- EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+ EXPECT_EQ(0x000000b3u, nine_patch->outline_alpha);
+ EXPECT_EQ(0.0f, nine_patch->outline_radius);
}
TEST(NinePatchTest, OutlineRadius) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineRadius5x5, 5, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(0, 0, 0, 0), ninePatch->outline);
- EXPECT_EQ(3.4142f, ninePatch->outlineRadius);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kOutlineRadius5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, nine_patch);
+ EXPECT_EQ(Bounds(0, 0, 0, 0), nine_patch->outline);
+ EXPECT_EQ(3.4142f, nine_patch->outline_radius);
}
-::testing::AssertionResult bigEndianOne(uint8_t* cursor) {
- if (cursor[0] == 0 && cursor[1] == 0 && cursor[2] == 0 && cursor[3] == 1) {
- return ::testing::AssertionSuccess();
- }
- return ::testing::AssertionFailure() << "Not BigEndian 1";
+::testing::AssertionResult BigEndianOne(uint8_t* cursor) {
+ if (cursor[0] == 0 && cursor[1] == 0 && cursor[2] == 0 && cursor[3] == 1) {
+ return ::testing::AssertionSuccess();
+ }
+ return ::testing::AssertionFailure() << "Not BigEndian 1";
}
TEST(NinePatchTest, SerializePngEndianness) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kStretchAndPadding5x5, 5, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
+ std::string err;
+ std::unique_ptr<NinePatch> nine_patch =
+ NinePatch::Create(kStretchAndPadding5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, nine_patch);
- size_t len;
- std::unique_ptr<uint8_t[]> data = ninePatch->serializeBase(&len);
- ASSERT_NE(nullptr, data);
- ASSERT_NE(0u, len);
+ size_t len;
+ std::unique_ptr<uint8_t[]> data = nine_patch->SerializeBase(&len);
+ ASSERT_NE(nullptr, data);
+ ASSERT_NE(0u, len);
- // Skip past wasDeserialized + numXDivs + numYDivs + numColors + xDivsOffset + yDivsOffset
- // (12 bytes)
- uint8_t* cursor = data.get() + 12;
+ // Skip past wasDeserialized + numXDivs + numYDivs + numColors + xDivsOffset +
+ // yDivsOffset
+ // (12 bytes)
+ uint8_t* cursor = data.get() + 12;
- // Check that padding is big-endian. Expecting value 1.
- EXPECT_TRUE(bigEndianOne(cursor));
- EXPECT_TRUE(bigEndianOne(cursor + 4));
- EXPECT_TRUE(bigEndianOne(cursor + 8));
- EXPECT_TRUE(bigEndianOne(cursor + 12));
+ // Check that padding is big-endian. Expecting value 1.
+ EXPECT_TRUE(BigEndianOne(cursor));
+ EXPECT_TRUE(BigEndianOne(cursor + 4));
+ EXPECT_TRUE(BigEndianOne(cursor + 8));
+ EXPECT_TRUE(BigEndianOne(cursor + 12));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/Png.cpp b/tools/aapt2/compile/Png.cpp
index 055d8b5..f1bc53e 100644
--- a/tools/aapt2/compile/Png.cpp
+++ b/tools/aapt2/compile/Png.cpp
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-#include "util/BigBuffer.h"
#include "Png.h"
#include "Source.h"
+#include "util/BigBuffer.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
-#include <iostream>
#include <png.h>
+#include <zlib.h>
+#include <iostream>
#include <sstream>
#include <string>
#include <vector>
-#include <zlib.h>
namespace aapt {
@@ -33,158 +33,166 @@
constexpr size_t kPngSignatureSize = 8u;
struct PngInfo {
- ~PngInfo() {
- for (png_bytep row : rows) {
- if (row != nullptr) {
- delete[] row;
- }
- }
-
- delete[] xDivs;
- delete[] yDivs;
+ ~PngInfo() {
+ for (png_bytep row : rows) {
+ if (row != nullptr) {
+ delete[] row;
+ }
}
- void* serialize9Patch() {
- void* serialized = android::Res_png_9patch::serialize(info9Patch, xDivs, yDivs,
- colors.data());
- reinterpret_cast<android::Res_png_9patch*>(serialized)->deviceToFile();
- return serialized;
- }
+ delete[] xDivs;
+ delete[] yDivs;
+ }
- uint32_t width = 0;
- uint32_t height = 0;
- std::vector<png_bytep> rows;
+ void* serialize9Patch() {
+ void* serialized = android::Res_png_9patch::serialize(info9Patch, xDivs,
+ yDivs, colors.data());
+ reinterpret_cast<android::Res_png_9patch*>(serialized)->deviceToFile();
+ return serialized;
+ }
- bool is9Patch = false;
- android::Res_png_9patch info9Patch;
- int32_t* xDivs = nullptr;
- int32_t* yDivs = nullptr;
- std::vector<uint32_t> colors;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ std::vector<png_bytep> rows;
- // Layout padding.
- bool haveLayoutBounds = false;
- int32_t layoutBoundsLeft;
- int32_t layoutBoundsTop;
- int32_t layoutBoundsRight;
- int32_t layoutBoundsBottom;
+ bool is9Patch = false;
+ android::Res_png_9patch info9Patch;
+ int32_t* xDivs = nullptr;
+ int32_t* yDivs = nullptr;
+ std::vector<uint32_t> colors;
- // Round rect outline description.
- int32_t outlineInsetsLeft;
- int32_t outlineInsetsTop;
- int32_t outlineInsetsRight;
- int32_t outlineInsetsBottom;
- float outlineRadius;
- uint8_t outlineAlpha;
+ // Layout padding.
+ bool haveLayoutBounds = false;
+ int32_t layoutBoundsLeft;
+ int32_t layoutBoundsTop;
+ int32_t layoutBoundsRight;
+ int32_t layoutBoundsBottom;
+
+ // Round rect outline description.
+ int32_t outlineInsetsLeft;
+ int32_t outlineInsetsTop;
+ int32_t outlineInsetsRight;
+ int32_t outlineInsetsBottom;
+ float outlineRadius;
+ uint8_t outlineAlpha;
};
-static void readDataFromStream(png_structp readPtr, png_bytep data, png_size_t length) {
- std::istream* input = reinterpret_cast<std::istream*>(png_get_io_ptr(readPtr));
- if (!input->read(reinterpret_cast<char*>(data), length)) {
- png_error(readPtr, strerror(errno));
- }
+static void readDataFromStream(png_structp readPtr, png_bytep data,
+ png_size_t length) {
+ std::istream* input =
+ reinterpret_cast<std::istream*>(png_get_io_ptr(readPtr));
+ if (!input->read(reinterpret_cast<char*>(data), length)) {
+ png_error(readPtr, strerror(errno));
+ }
}
-static void writeDataToStream(png_structp writePtr, png_bytep data, png_size_t length) {
- BigBuffer* outBuffer = reinterpret_cast<BigBuffer*>(png_get_io_ptr(writePtr));
- png_bytep buf = outBuffer->nextBlock<png_byte>(length);
- memcpy(buf, data, length);
+static void writeDataToStream(png_structp writePtr, png_bytep data,
+ png_size_t length) {
+ BigBuffer* outBuffer = reinterpret_cast<BigBuffer*>(png_get_io_ptr(writePtr));
+ png_bytep buf = outBuffer->NextBlock<png_byte>(length);
+ memcpy(buf, data, length);
}
-static void flushDataToStream(png_structp /*writePtr*/) {
-}
+static void flushDataToStream(png_structp /*writePtr*/) {}
static void logWarning(png_structp readPtr, png_const_charp warningMessage) {
- IDiagnostics* diag = reinterpret_cast<IDiagnostics*>(png_get_error_ptr(readPtr));
- diag->warn(DiagMessage() << warningMessage);
+ IDiagnostics* diag =
+ reinterpret_cast<IDiagnostics*>(png_get_error_ptr(readPtr));
+ diag->Warn(DiagMessage() << warningMessage);
}
+static bool readPng(IDiagnostics* diag, png_structp readPtr, png_infop infoPtr,
+ PngInfo* outInfo) {
+ if (setjmp(png_jmpbuf(readPtr))) {
+ diag->Error(DiagMessage() << "failed reading png");
+ return false;
+ }
-static bool readPng(IDiagnostics* diag, png_structp readPtr, png_infop infoPtr, PngInfo* outInfo) {
- if (setjmp(png_jmpbuf(readPtr))) {
- diag->error(DiagMessage() << "failed reading png");
- return false;
- }
+ png_set_sig_bytes(readPtr, kPngSignatureSize);
+ png_read_info(readPtr, infoPtr);
- png_set_sig_bytes(readPtr, kPngSignatureSize);
- png_read_info(readPtr, infoPtr);
+ int colorType, bitDepth, interlaceType, compressionType;
+ png_get_IHDR(readPtr, infoPtr, &outInfo->width, &outInfo->height, &bitDepth,
+ &colorType, &interlaceType, &compressionType, nullptr);
- int colorType, bitDepth, interlaceType, compressionType;
- png_get_IHDR(readPtr, infoPtr, &outInfo->width, &outInfo->height, &bitDepth, &colorType,
- &interlaceType, &compressionType, nullptr);
+ if (colorType == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(readPtr);
+ }
- if (colorType == PNG_COLOR_TYPE_PALETTE) {
- png_set_palette_to_rgb(readPtr);
- }
+ if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
+ png_set_expand_gray_1_2_4_to_8(readPtr);
+ }
- if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
- png_set_expand_gray_1_2_4_to_8(readPtr);
- }
+ if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(readPtr);
+ }
- if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
- png_set_tRNS_to_alpha(readPtr);
- }
+ if (bitDepth == 16) {
+ png_set_strip_16(readPtr);
+ }
- if (bitDepth == 16) {
- png_set_strip_16(readPtr);
- }
+ if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
+ png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
+ }
- if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
- png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
- }
+ if (colorType == PNG_COLOR_TYPE_GRAY ||
+ colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(readPtr);
+ }
- if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
- png_set_gray_to_rgb(readPtr);
- }
+ png_set_interlace_handling(readPtr);
+ png_read_update_info(readPtr, infoPtr);
- png_set_interlace_handling(readPtr);
- png_read_update_info(readPtr, infoPtr);
+ const uint32_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
+ outInfo->rows.resize(outInfo->height);
+ for (size_t i = 0; i < outInfo->height; i++) {
+ outInfo->rows[i] = new png_byte[rowBytes];
+ }
- const uint32_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
- outInfo->rows.resize(outInfo->height);
- for (size_t i = 0; i < outInfo->height; i++) {
- outInfo->rows[i] = new png_byte[rowBytes];
- }
-
- png_read_image(readPtr, outInfo->rows.data());
- png_read_end(readPtr, infoPtr);
- return true;
+ png_read_image(readPtr, outInfo->rows.data());
+ png_read_end(readPtr, infoPtr);
+ return true;
}
-static void checkNinePatchSerialization(android::Res_png_9patch* inPatch, void* data) {
- size_t patchSize = inPatch->serializedSize();
- void* newData = malloc(patchSize);
- memcpy(newData, data, patchSize);
- android::Res_png_9patch* outPatch = inPatch->deserialize(newData);
- outPatch->fileToDevice();
- // deserialization is done in place, so outPatch == newData
- assert(outPatch == newData);
- assert(outPatch->numXDivs == inPatch->numXDivs);
- assert(outPatch->numYDivs == inPatch->numYDivs);
- assert(outPatch->paddingLeft == inPatch->paddingLeft);
- assert(outPatch->paddingRight == inPatch->paddingRight);
- assert(outPatch->paddingTop == inPatch->paddingTop);
- assert(outPatch->paddingBottom == inPatch->paddingBottom);
-/* for (int i = 0; i < outPatch->numXDivs; i++) {
- assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]);
- }
- for (int i = 0; i < outPatch->numYDivs; i++) {
- assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]);
- }
- for (int i = 0; i < outPatch->numColors; i++) {
- assert(outPatch->getColors()[i] == inPatch->getColors()[i]);
- }*/
- free(newData);
+static void checkNinePatchSerialization(android::Res_png_9patch* inPatch,
+ void* data) {
+ size_t patchSize = inPatch->serializedSize();
+ void* newData = malloc(patchSize);
+ memcpy(newData, data, patchSize);
+ android::Res_png_9patch* outPatch = inPatch->deserialize(newData);
+ outPatch->fileToDevice();
+ // deserialization is done in place, so outPatch == newData
+ assert(outPatch == newData);
+ assert(outPatch->numXDivs == inPatch->numXDivs);
+ assert(outPatch->numYDivs == inPatch->numYDivs);
+ assert(outPatch->paddingLeft == inPatch->paddingLeft);
+ assert(outPatch->paddingRight == inPatch->paddingRight);
+ assert(outPatch->paddingTop == inPatch->paddingTop);
+ assert(outPatch->paddingBottom == inPatch->paddingBottom);
+ /* for (int i = 0; i < outPatch->numXDivs; i++) {
+ assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]);
+ }
+ for (int i = 0; i < outPatch->numYDivs; i++) {
+ assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]);
+ }
+ for (int i = 0; i < outPatch->numColors; i++) {
+ assert(outPatch->getColors()[i] == inPatch->getColors()[i]);
+ }*/
+ free(newData);
}
-/*static void dump_image(int w, int h, const png_byte* const* rows, int color_type) {
+/*static void dump_image(int w, int h, const png_byte* const* rows, int
+color_type) {
int i, j, rr, gg, bb, aa;
int bpp;
- if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
+ if (color_type == PNG_COLOR_TYPE_PALETTE || color_type ==
+PNG_COLOR_TYPE_GRAY) {
bpp = 1;
} else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
bpp = 2;
- } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+ } else if (color_type == PNG_COLOR_TYPE_RGB || color_type ==
+PNG_COLOR_TYPE_RGB_ALPHA) {
// We use a padding byte even when there is no alpha
bpp = 4;
} else {
@@ -224,1055 +232,1083 @@
}
}*/
-#define MAX(a,b) ((a)>(b)?(a):(b))
-#define ABS(a) ((a)<0?-(a):(a))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define ABS(a) ((a) < 0 ? -(a) : (a))
-static void analyze_image(IDiagnostics* diag, const PngInfo& imageInfo, int grayscaleTolerance,
- png_colorp rgbPalette, png_bytep alphaPalette,
- int *paletteEntries, bool *hasTransparency, int *colorType,
+static void analyze_image(IDiagnostics* diag, const PngInfo& imageInfo,
+ int grayscaleTolerance, png_colorp rgbPalette,
+ png_bytep alphaPalette, int* paletteEntries,
+ bool* hasTransparency, int* colorType,
png_bytepp outRows) {
- int w = imageInfo.width;
- int h = imageInfo.height;
- int i, j, rr, gg, bb, aa, idx;
- uint32_t colors[256], col;
- int num_colors = 0;
- int maxGrayDeviation = 0;
+ int w = imageInfo.width;
+ int h = imageInfo.height;
+ int i, j, rr, gg, bb, aa, idx;
+ uint32_t colors[256], col;
+ int num_colors = 0;
+ int maxGrayDeviation = 0;
- bool isOpaque = true;
- bool isPalette = true;
- bool isGrayscale = true;
+ bool isOpaque = true;
+ bool isPalette = true;
+ bool isGrayscale = true;
- // Scan the entire image and determine if:
- // 1. Every pixel has R == G == B (grayscale)
- // 2. Every pixel has A == 255 (opaque)
- // 3. There are no more than 256 distinct RGBA colors
+ // Scan the entire image and determine if:
+ // 1. Every pixel has R == G == B (grayscale)
+ // 2. Every pixel has A == 255 (opaque)
+ // 3. There are no more than 256 distinct RGBA colors
- if (kDebug) {
- printf("Initial image data:\n");
- //dump_image(w, h, imageInfo.rows.data(), PNG_COLOR_TYPE_RGB_ALPHA);
- }
+ if (kDebug) {
+ printf("Initial image data:\n");
+ // dump_image(w, h, imageInfo.rows.data(), PNG_COLOR_TYPE_RGB_ALPHA);
+ }
- for (j = 0; j < h; j++) {
- const png_byte* row = imageInfo.rows[j];
- png_bytep out = outRows[j];
- for (i = 0; i < w; i++) {
- rr = *row++;
- gg = *row++;
- bb = *row++;
- aa = *row++;
+ for (j = 0; j < h; j++) {
+ const png_byte* row = imageInfo.rows[j];
+ png_bytep out = outRows[j];
+ for (i = 0; i < w; i++) {
+ rr = *row++;
+ gg = *row++;
+ bb = *row++;
+ aa = *row++;
- int odev = maxGrayDeviation;
- maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
- maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
- maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
- if (maxGrayDeviation > odev) {
- if (kDebug) {
- printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
- maxGrayDeviation, i, j, rr, gg, bb, aa);
- }
- }
-
- // Check if image is really grayscale
- if (isGrayscale) {
- if (rr != gg || rr != bb) {
- if (kDebug) {
- printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
- i, j, rr, gg, bb, aa);
- }
- isGrayscale = false;
- }
- }
-
- // Check if image is really opaque
- if (isOpaque) {
- if (aa != 0xff) {
- if (kDebug) {
- printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
- i, j, rr, gg, bb, aa);
- }
- isOpaque = false;
- }
- }
-
- // Check if image is really <= 256 colors
- if (isPalette) {
- col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
- bool match = false;
- for (idx = 0; idx < num_colors; idx++) {
- if (colors[idx] == col) {
- match = true;
- break;
- }
- }
-
- // Write the palette index for the pixel to outRows optimistically
- // We might overwrite it later if we decide to encode as gray or
- // gray + alpha
- *out++ = idx;
- if (!match) {
- if (num_colors == 256) {
- if (kDebug) {
- printf("Found 257th color at %d, %d\n", i, j);
- }
- isPalette = false;
- } else {
- colors[num_colors++] = col;
- }
- }
- }
+ int odev = maxGrayDeviation;
+ maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
+ maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
+ maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
+ if (maxGrayDeviation > odev) {
+ if (kDebug) {
+ printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
+ maxGrayDeviation, i, j, rr, gg, bb, aa);
}
- }
+ }
- *paletteEntries = 0;
- *hasTransparency = !isOpaque;
- int bpp = isOpaque ? 3 : 4;
- int paletteSize = w * h + bpp * num_colors;
-
- if (kDebug) {
- printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
- printf("isOpaque = %s\n", isOpaque ? "true" : "false");
- printf("isPalette = %s\n", isPalette ? "true" : "false");
- printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
- paletteSize, 2 * w * h, bpp * w * h);
- printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance);
- }
-
- // Choose the best color type for the image.
- // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
- // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
- // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
- // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
- // small, otherwise use COLOR_TYPE_RGB{_ALPHA}
- if (isGrayscale) {
- if (isOpaque) {
- *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
- } else {
- // Use a simple heuristic to determine whether using a palette will
- // save space versus using gray + alpha for each pixel.
- // This doesn't take into account chunk overhead, filtering, LZ
- // compression, etc.
- if (isPalette && (paletteSize < 2 * w * h)) {
- *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
- } else {
- *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
- }
+ // Check if image is really grayscale
+ if (isGrayscale) {
+ if (rr != gg || rr != bb) {
+ if (kDebug) {
+ printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n", i, j,
+ rr, gg, bb, aa);
+ }
+ isGrayscale = false;
}
- } else if (isPalette && (paletteSize < bpp * w * h)) {
- *colorType = PNG_COLOR_TYPE_PALETTE;
+ }
+
+ // Check if image is really opaque
+ if (isOpaque) {
+ if (aa != 0xff) {
+ if (kDebug) {
+ printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n", i, j,
+ rr, gg, bb, aa);
+ }
+ isOpaque = false;
+ }
+ }
+
+ // Check if image is really <= 256 colors
+ if (isPalette) {
+ col = (uint32_t)((rr << 24) | (gg << 16) | (bb << 8) | aa);
+ bool match = false;
+ for (idx = 0; idx < num_colors; idx++) {
+ if (colors[idx] == col) {
+ match = true;
+ break;
+ }
+ }
+
+ // Write the palette index for the pixel to outRows optimistically
+ // We might overwrite it later if we decide to encode as gray or
+ // gray + alpha
+ *out++ = idx;
+ if (!match) {
+ if (num_colors == 256) {
+ if (kDebug) {
+ printf("Found 257th color at %d, %d\n", i, j);
+ }
+ isPalette = false;
+ } else {
+ colors[num_colors++] = col;
+ }
+ }
+ }
+ }
+ }
+
+ *paletteEntries = 0;
+ *hasTransparency = !isOpaque;
+ int bpp = isOpaque ? 3 : 4;
+ int paletteSize = w * h + bpp * num_colors;
+
+ if (kDebug) {
+ printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
+ printf("isOpaque = %s\n", isOpaque ? "true" : "false");
+ printf("isPalette = %s\n", isPalette ? "true" : "false");
+ printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n", paletteSize,
+ 2 * w * h, bpp * w * h);
+ printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation,
+ grayscaleTolerance);
+ }
+
+ // Choose the best color type for the image.
+ // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
+ // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct
+ // combinations
+ // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
+ // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is
+ // sufficiently
+ // small, otherwise use COLOR_TYPE_RGB{_ALPHA}
+ if (isGrayscale) {
+ if (isOpaque) {
+ *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
} else {
- if (maxGrayDeviation <= grayscaleTolerance) {
- diag->note(DiagMessage()
- << "forcing image to gray (max deviation = "
- << maxGrayDeviation << ")");
- *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
+ // Use a simple heuristic to determine whether using a palette will
+ // save space versus using gray + alpha for each pixel.
+ // This doesn't take into account chunk overhead, filtering, LZ
+ // compression, etc.
+ if (isPalette && (paletteSize < 2 * w * h)) {
+ *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
+ } else {
+ *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
+ }
+ }
+ } else if (isPalette && (paletteSize < bpp * w * h)) {
+ *colorType = PNG_COLOR_TYPE_PALETTE;
+ } else {
+ if (maxGrayDeviation <= grayscaleTolerance) {
+ diag->Note(DiagMessage() << "forcing image to gray (max deviation = "
+ << maxGrayDeviation << ")");
+ *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
+ } else {
+ *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+ }
+ }
+
+ // Perform postprocessing of the image or palette data based on the final
+ // color type chosen
+
+ if (*colorType == PNG_COLOR_TYPE_PALETTE) {
+ // Create separate RGB and Alpha palettes and set the number of colors
+ *paletteEntries = num_colors;
+
+ // Create the RGB and alpha palettes
+ for (int idx = 0; idx < num_colors; idx++) {
+ col = colors[idx];
+ rgbPalette[idx].red = (png_byte)((col >> 24) & 0xff);
+ rgbPalette[idx].green = (png_byte)((col >> 16) & 0xff);
+ rgbPalette[idx].blue = (png_byte)((col >> 8) & 0xff);
+ alphaPalette[idx] = (png_byte)(col & 0xff);
+ }
+ } else if (*colorType == PNG_COLOR_TYPE_GRAY ||
+ *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ // If the image is gray or gray + alpha, compact the pixels into outRows
+ for (j = 0; j < h; j++) {
+ const png_byte* row = imageInfo.rows[j];
+ png_bytep out = outRows[j];
+ for (i = 0; i < w; i++) {
+ rr = *row++;
+ gg = *row++;
+ bb = *row++;
+ aa = *row++;
+
+ if (isGrayscale) {
+ *out++ = rr;
} else {
- *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+ *out++ = (png_byte)(rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
}
+ if (!isOpaque) {
+ *out++ = aa;
+ }
+ }
}
-
- // Perform postprocessing of the image or palette data based on the final
- // color type chosen
-
- if (*colorType == PNG_COLOR_TYPE_PALETTE) {
- // Create separate RGB and Alpha palettes and set the number of colors
- *paletteEntries = num_colors;
-
- // Create the RGB and alpha palettes
- for (int idx = 0; idx < num_colors; idx++) {
- col = colors[idx];
- rgbPalette[idx].red = (png_byte) ((col >> 24) & 0xff);
- rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
- rgbPalette[idx].blue = (png_byte) ((col >> 8) & 0xff);
- alphaPalette[idx] = (png_byte) (col & 0xff);
- }
- } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
- // If the image is gray or gray + alpha, compact the pixels into outRows
- for (j = 0; j < h; j++) {
- const png_byte* row = imageInfo.rows[j];
- png_bytep out = outRows[j];
- for (i = 0; i < w; i++) {
- rr = *row++;
- gg = *row++;
- bb = *row++;
- aa = *row++;
-
- if (isGrayscale) {
- *out++ = rr;
- } else {
- *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
- }
- if (!isOpaque) {
- *out++ = aa;
- }
- }
- }
- }
+ }
}
-static bool writePng(IDiagnostics* diag, png_structp writePtr, png_infop infoPtr, PngInfo* info,
- int grayScaleTolerance) {
- if (setjmp(png_jmpbuf(writePtr))) {
- diag->error(DiagMessage() << "failed to write png");
- return false;
+static bool writePng(IDiagnostics* diag, png_structp writePtr,
+ png_infop infoPtr, PngInfo* info, int grayScaleTolerance) {
+ if (setjmp(png_jmpbuf(writePtr))) {
+ diag->Error(DiagMessage() << "failed to write png");
+ return false;
+ }
+
+ uint32_t width, height;
+ int colorType, bitDepth, interlaceType, compressionType;
+
+ png_unknown_chunk unknowns[3];
+ unknowns[0].data = nullptr;
+ unknowns[1].data = nullptr;
+ unknowns[2].data = nullptr;
+
+ png_bytepp outRows =
+ (png_bytepp)malloc((int)info->height * sizeof(png_bytep));
+ if (outRows == (png_bytepp)0) {
+ printf("Can't allocate output buffer!\n");
+ exit(1);
+ }
+ for (uint32_t i = 0; i < info->height; i++) {
+ outRows[i] = (png_bytep)malloc(2 * (int)info->width);
+ if (outRows[i] == (png_bytep)0) {
+ printf("Can't allocate output buffer!\n");
+ exit(1);
}
+ }
- uint32_t width, height;
- int colorType, bitDepth, interlaceType, compressionType;
+ png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
- png_unknown_chunk unknowns[3];
- unknowns[0].data = nullptr;
- unknowns[1].data = nullptr;
- unknowns[2].data = nullptr;
+ if (kDebug) {
+ diag->Note(DiagMessage() << "writing image: w = " << info->width
+ << ", h = " << info->height);
+ }
- png_bytepp outRows = (png_bytepp) malloc((int) info->height * sizeof(png_bytep));
- if (outRows == (png_bytepp) 0) {
- printf("Can't allocate output buffer!\n");
- exit(1);
+ png_color rgbPalette[256];
+ png_byte alphaPalette[256];
+ bool hasTransparency;
+ int paletteEntries;
+
+ analyze_image(diag, *info, grayScaleTolerance, rgbPalette, alphaPalette,
+ &paletteEntries, &hasTransparency, &colorType, outRows);
+
+ // If the image is a 9-patch, we need to preserve it as a ARGB file to make
+ // sure the pixels will not be pre-dithered/clamped until we decide they are
+ if (info->is9Patch &&
+ (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY ||
+ colorType == PNG_COLOR_TYPE_PALETTE)) {
+ colorType = PNG_COLOR_TYPE_RGB_ALPHA;
+ }
+
+ if (kDebug) {
+ switch (colorType) {
+ case PNG_COLOR_TYPE_PALETTE:
+ diag->Note(DiagMessage() << "has " << paletteEntries << " colors"
+ << (hasTransparency ? " (with alpha)" : "")
+ << ", using PNG_COLOR_TYPE_PALLETTE");
+ break;
+ case PNG_COLOR_TYPE_GRAY:
+ diag->Note(DiagMessage()
+ << "is opaque gray, using PNG_COLOR_TYPE_GRAY");
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ diag->Note(DiagMessage()
+ << "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA");
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ diag->Note(DiagMessage() << "is opaque RGB, using PNG_COLOR_TYPE_RGB");
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ diag->Note(DiagMessage()
+ << "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA");
+ break;
}
- for (uint32_t i = 0; i < info->height; i++) {
- outRows[i] = (png_bytep) malloc(2 * (int) info->width);
- if (outRows[i] == (png_bytep) 0) {
- printf("Can't allocate output buffer!\n");
- exit(1);
- }
+ }
+
+ png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ if (colorType == PNG_COLOR_TYPE_PALETTE) {
+ png_set_PLTE(writePtr, infoPtr, rgbPalette, paletteEntries);
+ if (hasTransparency) {
+ png_set_tRNS(writePtr, infoPtr, alphaPalette, paletteEntries,
+ (png_color_16p)0);
}
+ png_set_filter(writePtr, 0, PNG_NO_FILTERS);
+ } else {
+ png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
+ }
- png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
+ if (info->is9Patch) {
+ int chunkCount = 2 + (info->haveLayoutBounds ? 1 : 0);
+ int pIndex = info->haveLayoutBounds ? 2 : 1;
+ int bIndex = 1;
+ int oIndex = 0;
+ // Chunks ordered thusly because older platforms depend on the base 9 patch
+ // data being last
+ png_bytep chunkNames = info->haveLayoutBounds
+ ? (png_bytep) "npOl\0npLb\0npTc\0"
+ : (png_bytep) "npOl\0npTc";
+
+ // base 9 patch data
if (kDebug) {
- diag->note(DiagMessage()
- << "writing image: w = " << info->width
- << ", h = " << info->height);
+ diag->Note(DiagMessage() << "adding 9-patch info..");
+ }
+ strcpy((char*)unknowns[pIndex].name, "npTc");
+ unknowns[pIndex].data = (png_byte*)info->serialize9Patch();
+ unknowns[pIndex].size = info->info9Patch.serializedSize();
+ // TODO: remove the check below when everything works
+ checkNinePatchSerialization(&info->info9Patch, unknowns[pIndex].data);
+
+ // automatically generated 9 patch outline data
+ int chunkSize = sizeof(png_uint_32) * 6;
+ strcpy((char*)unknowns[oIndex].name, "npOl");
+ unknowns[oIndex].data = (png_byte*)calloc(chunkSize, 1);
+ png_byte outputData[chunkSize];
+ memcpy(&outputData, &info->outlineInsetsLeft, 4 * sizeof(png_uint_32));
+ ((float*)outputData)[4] = info->outlineRadius;
+ ((png_uint_32*)outputData)[5] = info->outlineAlpha;
+ memcpy(unknowns[oIndex].data, &outputData, chunkSize);
+ unknowns[oIndex].size = chunkSize;
+
+ // optional optical inset / layout bounds data
+ if (info->haveLayoutBounds) {
+ int chunkSize = sizeof(png_uint_32) * 4;
+ strcpy((char*)unknowns[bIndex].name, "npLb");
+ unknowns[bIndex].data = (png_byte*)calloc(chunkSize, 1);
+ memcpy(unknowns[bIndex].data, &info->layoutBoundsLeft, chunkSize);
+ unknowns[bIndex].size = chunkSize;
}
- png_color rgbPalette[256];
- png_byte alphaPalette[256];
- bool hasTransparency;
- int paletteEntries;
-
- analyze_image(diag, *info, grayScaleTolerance, rgbPalette, alphaPalette,
- &paletteEntries, &hasTransparency, &colorType, outRows);
-
- // If the image is a 9-patch, we need to preserve it as a ARGB file to make
- // sure the pixels will not be pre-dithered/clamped until we decide they are
- if (info->is9Patch && (colorType == PNG_COLOR_TYPE_RGB ||
- colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_PALETTE)) {
- colorType = PNG_COLOR_TYPE_RGB_ALPHA;
+ for (int i = 0; i < chunkCount; i++) {
+ unknowns[i].location = PNG_HAVE_PLTE;
}
-
- if (kDebug) {
- switch (colorType) {
- case PNG_COLOR_TYPE_PALETTE:
- diag->note(DiagMessage()
- << "has " << paletteEntries
- << " colors" << (hasTransparency ? " (with alpha)" : "")
- << ", using PNG_COLOR_TYPE_PALLETTE");
- break;
- case PNG_COLOR_TYPE_GRAY:
- diag->note(DiagMessage() << "is opaque gray, using PNG_COLOR_TYPE_GRAY");
- break;
- case PNG_COLOR_TYPE_GRAY_ALPHA:
- diag->note(DiagMessage() << "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA");
- break;
- case PNG_COLOR_TYPE_RGB:
- diag->note(DiagMessage() << "is opaque RGB, using PNG_COLOR_TYPE_RGB");
- break;
- case PNG_COLOR_TYPE_RGB_ALPHA:
- diag->note(DiagMessage() << "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA");
- break;
- }
- }
-
- png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType,
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
-
- if (colorType == PNG_COLOR_TYPE_PALETTE) {
- png_set_PLTE(writePtr, infoPtr, rgbPalette, paletteEntries);
- if (hasTransparency) {
- png_set_tRNS(writePtr, infoPtr, alphaPalette, paletteEntries, (png_color_16p) 0);
- }
- png_set_filter(writePtr, 0, PNG_NO_FILTERS);
- } else {
- png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
- }
-
- if (info->is9Patch) {
- int chunkCount = 2 + (info->haveLayoutBounds ? 1 : 0);
- int pIndex = info->haveLayoutBounds ? 2 : 1;
- int bIndex = 1;
- int oIndex = 0;
-
- // Chunks ordered thusly because older platforms depend on the base 9 patch data being last
- png_bytep chunkNames = info->haveLayoutBounds
- ? (png_bytep)"npOl\0npLb\0npTc\0"
- : (png_bytep)"npOl\0npTc";
-
- // base 9 patch data
- if (kDebug) {
- diag->note(DiagMessage() << "adding 9-patch info..");
- }
- strcpy((char*)unknowns[pIndex].name, "npTc");
- unknowns[pIndex].data = (png_byte*) info->serialize9Patch();
- unknowns[pIndex].size = info->info9Patch.serializedSize();
- // TODO: remove the check below when everything works
- checkNinePatchSerialization(&info->info9Patch, unknowns[pIndex].data);
-
- // automatically generated 9 patch outline data
- int chunkSize = sizeof(png_uint_32) * 6;
- strcpy((char*)unknowns[oIndex].name, "npOl");
- unknowns[oIndex].data = (png_byte*) calloc(chunkSize, 1);
- png_byte outputData[chunkSize];
- memcpy(&outputData, &info->outlineInsetsLeft, 4 * sizeof(png_uint_32));
- ((float*) outputData)[4] = info->outlineRadius;
- ((png_uint_32*) outputData)[5] = info->outlineAlpha;
- memcpy(unknowns[oIndex].data, &outputData, chunkSize);
- unknowns[oIndex].size = chunkSize;
-
- // optional optical inset / layout bounds data
- if (info->haveLayoutBounds) {
- int chunkSize = sizeof(png_uint_32) * 4;
- strcpy((char*)unknowns[bIndex].name, "npLb");
- unknowns[bIndex].data = (png_byte*) calloc(chunkSize, 1);
- memcpy(unknowns[bIndex].data, &info->layoutBoundsLeft, chunkSize);
- unknowns[bIndex].size = chunkSize;
- }
-
- for (int i = 0; i < chunkCount; i++) {
- unknowns[i].location = PNG_HAVE_PLTE;
- }
- png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS,
- chunkNames, chunkCount);
- png_set_unknown_chunks(writePtr, infoPtr, unknowns, chunkCount);
+ png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, chunkNames,
+ chunkCount);
+ png_set_unknown_chunks(writePtr, infoPtr, unknowns, chunkCount);
#if PNG_LIBPNG_VER < 10600
- // Deal with unknown chunk location bug in 1.5.x and earlier.
- png_set_unknown_chunk_location(writePtr, infoPtr, 0, PNG_HAVE_PLTE);
- if (info->haveLayoutBounds) {
- png_set_unknown_chunk_location(writePtr, infoPtr, 1, PNG_HAVE_PLTE);
- }
+ // Deal with unknown chunk location bug in 1.5.x and earlier.
+ png_set_unknown_chunk_location(writePtr, infoPtr, 0, PNG_HAVE_PLTE);
+ if (info->haveLayoutBounds) {
+ png_set_unknown_chunk_location(writePtr, infoPtr, 1, PNG_HAVE_PLTE);
+ }
#endif
+ }
+
+ png_write_info(writePtr, infoPtr);
+
+ png_bytepp rows;
+ if (colorType == PNG_COLOR_TYPE_RGB ||
+ colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
+ if (colorType == PNG_COLOR_TYPE_RGB) {
+ png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
}
+ rows = info->rows.data();
+ } else {
+ rows = outRows;
+ }
+ png_write_image(writePtr, rows);
- png_write_info(writePtr, infoPtr);
+ if (kDebug) {
+ printf("Final image data:\n");
+ // dump_image(info->width, info->height, rows, colorType);
+ }
- png_bytepp rows;
- if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
- if (colorType == PNG_COLOR_TYPE_RGB) {
- png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
- }
- rows = info->rows.data();
- } else {
- rows = outRows;
- }
- png_write_image(writePtr, rows);
+ png_write_end(writePtr, infoPtr);
- if (kDebug) {
- printf("Final image data:\n");
- //dump_image(info->width, info->height, rows, colorType);
- }
+ for (uint32_t i = 0; i < info->height; i++) {
+ free(outRows[i]);
+ }
+ free(outRows);
+ free(unknowns[0].data);
+ free(unknowns[1].data);
+ free(unknowns[2].data);
- png_write_end(writePtr, infoPtr);
+ png_get_IHDR(writePtr, infoPtr, &width, &height, &bitDepth, &colorType,
+ &interlaceType, &compressionType, nullptr);
- for (uint32_t i = 0; i < info->height; i++) {
- free(outRows[i]);
- }
- free(outRows);
- free(unknowns[0].data);
- free(unknowns[1].data);
- free(unknowns[2].data);
-
- png_get_IHDR(writePtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceType,
- &compressionType, nullptr);
-
- if (kDebug) {
- diag->note(DiagMessage()
- << "image written: w = " << width << ", h = " << height
- << ", d = " << bitDepth << ", colors = " << colorType
- << ", inter = " << interlaceType << ", comp = " << compressionType);
- }
- return true;
+ if (kDebug) {
+ diag->Note(DiagMessage() << "image written: w = " << width
+ << ", h = " << height << ", d = " << bitDepth
+ << ", colors = " << colorType
+ << ", inter = " << interlaceType
+ << ", comp = " << compressionType);
+ }
+ return true;
}
constexpr uint32_t kColorWhite = 0xffffffffu;
constexpr uint32_t kColorTick = 0xff000000u;
constexpr uint32_t kColorLayoutBoundsTick = 0xff0000ffu;
-enum class TickType {
- kNone,
- kTick,
- kLayoutBounds,
- kBoth
-};
+enum class TickType { kNone, kTick, kLayoutBounds, kBoth };
static TickType tickType(png_bytep p, bool transparent, const char** outError) {
- png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+ png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
- if (transparent) {
- if (p[3] == 0) {
- return TickType::kNone;
- }
- if (color == kColorLayoutBoundsTick) {
- return TickType::kLayoutBounds;
- }
- if (color == kColorTick) {
- return TickType::kTick;
- }
-
- // Error cases
- if (p[3] != 0xff) {
- *outError = "Frame pixels must be either solid or transparent "
- "(not intermediate alphas)";
- return TickType::kNone;
- }
-
- if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
- *outError = "Ticks in transparent frame must be black or red";
- }
- return TickType::kTick;
- }
-
- if (p[3] != 0xFF) {
- *outError = "White frame must be a solid color (no alpha)";
- }
- if (color == kColorWhite) {
- return TickType::kNone;
- }
- if (color == kColorTick) {
- return TickType::kTick;
+ if (transparent) {
+ if (p[3] == 0) {
+ return TickType::kNone;
}
if (color == kColorLayoutBoundsTick) {
- return TickType::kLayoutBounds;
+ return TickType::kLayoutBounds;
+ }
+ if (color == kColorTick) {
+ return TickType::kTick;
+ }
+
+ // Error cases
+ if (p[3] != 0xff) {
+ *outError =
+ "Frame pixels must be either solid or transparent "
+ "(not intermediate alphas)";
+ return TickType::kNone;
}
if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
- *outError = "Ticks in white frame must be black or red";
- return TickType::kNone;
+ *outError = "Ticks in transparent frame must be black or red";
}
return TickType::kTick;
+ }
+
+ if (p[3] != 0xFF) {
+ *outError = "White frame must be a solid color (no alpha)";
+ }
+ if (color == kColorWhite) {
+ return TickType::kNone;
+ }
+ if (color == kColorTick) {
+ return TickType::kTick;
+ }
+ if (color == kColorLayoutBoundsTick) {
+ return TickType::kLayoutBounds;
+ }
+
+ if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
+ *outError = "Ticks in white frame must be black or red";
+ return TickType::kNone;
+ }
+ return TickType::kTick;
}
-enum class TickState {
- kStart,
- kInside1,
- kOutside1
-};
+enum class TickState { kStart, kInside1, kOutside1 };
-static bool getHorizontalTicks(png_bytep row, int width, bool transparent, bool required,
- int32_t* outLeft, int32_t* outRight, const char** outError,
+static bool getHorizontalTicks(png_bytep row, int width, bool transparent,
+ bool required, int32_t* outLeft,
+ int32_t* outRight, const char** outError,
uint8_t* outDivs, bool multipleAllowed) {
- *outLeft = *outRight = -1;
- TickState state = TickState::kStart;
- bool found = false;
+ *outLeft = *outRight = -1;
+ TickState state = TickState::kStart;
+ bool found = false;
- for (int i = 1; i < width - 1; i++) {
- if (tickType(row+i*4, transparent, outError) == TickType::kTick) {
- if (state == TickState::kStart ||
- (state == TickState::kOutside1 && multipleAllowed)) {
- *outLeft = i-1;
- *outRight = width-2;
- found = true;
- if (outDivs != NULL) {
- *outDivs += 2;
- }
- state = TickState::kInside1;
- } else if (state == TickState::kOutside1) {
- *outError = "Can't have more than one marked region along edge";
- *outLeft = i;
- return false;
- }
- } else if (!*outError) {
- if (state == TickState::kInside1) {
- // We're done with this div. Move on to the next.
- *outRight = i-1;
- outRight += 2;
- outLeft += 2;
- state = TickState::kOutside1;
- }
- } else {
- *outLeft = i;
- return false;
+ for (int i = 1; i < width - 1; i++) {
+ if (tickType(row + i * 4, transparent, outError) == TickType::kTick) {
+ if (state == TickState::kStart ||
+ (state == TickState::kOutside1 && multipleAllowed)) {
+ *outLeft = i - 1;
+ *outRight = width - 2;
+ found = true;
+ if (outDivs != NULL) {
+ *outDivs += 2;
}
- }
-
- if (required && !found) {
- *outError = "No marked region found along edge";
- *outLeft = -1;
+ state = TickState::kInside1;
+ } else if (state == TickState::kOutside1) {
+ *outError = "Can't have more than one marked region along edge";
+ *outLeft = i;
return false;
+ }
+ } else if (!*outError) {
+ if (state == TickState::kInside1) {
+ // We're done with this div. Move on to the next.
+ *outRight = i - 1;
+ outRight += 2;
+ outLeft += 2;
+ state = TickState::kOutside1;
+ }
+ } else {
+ *outLeft = i;
+ return false;
}
- return true;
+ }
+
+ if (required && !found) {
+ *outError = "No marked region found along edge";
+ *outLeft = -1;
+ return false;
+ }
+ return true;
}
-static bool getVerticalTicks(png_bytepp rows, int offset, int height, bool transparent,
- bool required, int32_t* outTop, int32_t* outBottom,
- const char** outError, uint8_t* outDivs, bool multipleAllowed) {
- *outTop = *outBottom = -1;
- TickState state = TickState::kStart;
- bool found = false;
+static bool getVerticalTicks(png_bytepp rows, int offset, int height,
+ bool transparent, bool required, int32_t* outTop,
+ int32_t* outBottom, const char** outError,
+ uint8_t* outDivs, bool multipleAllowed) {
+ *outTop = *outBottom = -1;
+ TickState state = TickState::kStart;
+ bool found = false;
- for (int i = 1; i < height - 1; i++) {
- if (tickType(rows[i]+offset, transparent, outError) == TickType::kTick) {
- if (state == TickState::kStart ||
- (state == TickState::kOutside1 && multipleAllowed)) {
- *outTop = i-1;
- *outBottom = height-2;
- found = true;
- if (outDivs != NULL) {
- *outDivs += 2;
- }
- state = TickState::kInside1;
- } else if (state == TickState::kOutside1) {
- *outError = "Can't have more than one marked region along edge";
- *outTop = i;
- return false;
- }
- } else if (!*outError) {
- if (state == TickState::kInside1) {
- // We're done with this div. Move on to the next.
- *outBottom = i-1;
- outTop += 2;
- outBottom += 2;
- state = TickState::kOutside1;
- }
- } else {
- *outTop = i;
- return false;
+ for (int i = 1; i < height - 1; i++) {
+ if (tickType(rows[i] + offset, transparent, outError) == TickType::kTick) {
+ if (state == TickState::kStart ||
+ (state == TickState::kOutside1 && multipleAllowed)) {
+ *outTop = i - 1;
+ *outBottom = height - 2;
+ found = true;
+ if (outDivs != NULL) {
+ *outDivs += 2;
}
- }
-
- if (required && !found) {
- *outError = "No marked region found along edge";
- *outTop = -1;
+ state = TickState::kInside1;
+ } else if (state == TickState::kOutside1) {
+ *outError = "Can't have more than one marked region along edge";
+ *outTop = i;
return false;
+ }
+ } else if (!*outError) {
+ if (state == TickState::kInside1) {
+ // We're done with this div. Move on to the next.
+ *outBottom = i - 1;
+ outTop += 2;
+ outBottom += 2;
+ state = TickState::kOutside1;
+ }
+ } else {
+ *outTop = i;
+ return false;
}
- return true;
+ }
+
+ if (required && !found) {
+ *outError = "No marked region found along edge";
+ *outTop = -1;
+ return false;
+ }
+ return true;
}
-static bool getHorizontalLayoutBoundsTicks(png_bytep row, int width, bool transparent,
- bool /* required */, int32_t* outLeft,
- int32_t* outRight, const char** outError) {
- *outLeft = *outRight = 0;
+static bool getHorizontalLayoutBoundsTicks(png_bytep row, int width,
+ bool transparent,
+ bool /* required */,
+ int32_t* outLeft, int32_t* outRight,
+ const char** outError) {
+ *outLeft = *outRight = 0;
- // Look for left tick
- if (tickType(row + 4, transparent, outError) == TickType::kLayoutBounds) {
- // Starting with a layout padding tick
- int i = 1;
- while (i < width - 1) {
- (*outLeft)++;
- i++;
- if (tickType(row + i * 4, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
+ // Look for left tick
+ if (tickType(row + 4, transparent, outError) == TickType::kLayoutBounds) {
+ // Starting with a layout padding tick
+ int i = 1;
+ while (i < width - 1) {
+ (*outLeft)++;
+ i++;
+ if (tickType(row + i * 4, transparent, outError) !=
+ TickType::kLayoutBounds) {
+ break;
+ }
}
+ }
- // Look for right tick
- if (tickType(row + (width - 2) * 4, transparent, outError) == TickType::kLayoutBounds) {
- // Ending with a layout padding tick
- int i = width - 2;
- while (i > 1) {
- (*outRight)++;
- i--;
- if (tickType(row+i*4, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
+ // Look for right tick
+ if (tickType(row + (width - 2) * 4, transparent, outError) ==
+ TickType::kLayoutBounds) {
+ // Ending with a layout padding tick
+ int i = width - 2;
+ while (i > 1) {
+ (*outRight)++;
+ i--;
+ if (tickType(row + i * 4, transparent, outError) !=
+ TickType::kLayoutBounds) {
+ break;
+ }
}
- return true;
+ }
+ return true;
}
-static bool getVerticalLayoutBoundsTicks(png_bytepp rows, int offset, int height, bool transparent,
- bool /* required */, int32_t* outTop, int32_t* outBottom,
+static bool getVerticalLayoutBoundsTicks(png_bytepp rows, int offset,
+ int height, bool transparent,
+ bool /* required */, int32_t* outTop,
+ int32_t* outBottom,
const char** outError) {
- *outTop = *outBottom = 0;
+ *outTop = *outBottom = 0;
- // Look for top tick
- if (tickType(rows[1] + offset, transparent, outError) == TickType::kLayoutBounds) {
- // Starting with a layout padding tick
- int i = 1;
- while (i < height - 1) {
- (*outTop)++;
- i++;
- if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
+ // Look for top tick
+ if (tickType(rows[1] + offset, transparent, outError) ==
+ TickType::kLayoutBounds) {
+ // Starting with a layout padding tick
+ int i = 1;
+ while (i < height - 1) {
+ (*outTop)++;
+ i++;
+ if (tickType(rows[i] + offset, transparent, outError) !=
+ TickType::kLayoutBounds) {
+ break;
+ }
}
+ }
- // Look for bottom tick
- if (tickType(rows[height - 2] + offset, transparent, outError) == TickType::kLayoutBounds) {
- // Ending with a layout padding tick
- int i = height - 2;
- while (i > 1) {
- (*outBottom)++;
- i--;
- if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
+ // Look for bottom tick
+ if (tickType(rows[height - 2] + offset, transparent, outError) ==
+ TickType::kLayoutBounds) {
+ // Ending with a layout padding tick
+ int i = height - 2;
+ while (i > 1) {
+ (*outBottom)++;
+ i--;
+ if (tickType(rows[i] + offset, transparent, outError) !=
+ TickType::kLayoutBounds) {
+ break;
+ }
}
- return true;
+ }
+ return true;
}
-static void findMaxOpacity(png_bytepp rows, int startX, int startY, int endX, int endY,
- int dX, int dY, int* outInset) {
- uint8_t maxOpacity = 0;
- int inset = 0;
- *outInset = 0;
- for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
- png_byte* color = rows[y] + x * 4;
- uint8_t opacity = color[3];
- if (opacity > maxOpacity) {
- maxOpacity = opacity;
- *outInset = inset;
- }
- if (opacity == 0xff) return;
+static void findMaxOpacity(png_bytepp rows, int startX, int startY, int endX,
+ int endY, int dX, int dY, int* outInset) {
+ uint8_t maxOpacity = 0;
+ int inset = 0;
+ *outInset = 0;
+ for (int x = startX, y = startY; x != endX && y != endY;
+ x += dX, y += dY, inset++) {
+ png_byte* color = rows[y] + x * 4;
+ uint8_t opacity = color[3];
+ if (opacity > maxOpacity) {
+ maxOpacity = opacity;
+ *outInset = inset;
}
+ if (opacity == 0xff) return;
+ }
}
static uint8_t maxAlphaOverRow(png_bytep row, int startX, int endX) {
- uint8_t maxAlpha = 0;
- for (int x = startX; x < endX; x++) {
- uint8_t alpha = (row + x * 4)[3];
- if (alpha > maxAlpha) maxAlpha = alpha;
- }
- return maxAlpha;
+ uint8_t maxAlpha = 0;
+ for (int x = startX; x < endX; x++) {
+ uint8_t alpha = (row + x * 4)[3];
+ if (alpha > maxAlpha) maxAlpha = alpha;
+ }
+ return maxAlpha;
}
-static uint8_t maxAlphaOverCol(png_bytepp rows, int offsetX, int startY, int endY) {
- uint8_t maxAlpha = 0;
- for (int y = startY; y < endY; y++) {
- uint8_t alpha = (rows[y] + offsetX * 4)[3];
- if (alpha > maxAlpha) maxAlpha = alpha;
- }
- return maxAlpha;
+static uint8_t maxAlphaOverCol(png_bytepp rows, int offsetX, int startY,
+ int endY) {
+ uint8_t maxAlpha = 0;
+ for (int y = startY; y < endY; y++) {
+ uint8_t alpha = (rows[y] + offsetX * 4)[3];
+ if (alpha > maxAlpha) maxAlpha = alpha;
+ }
+ return maxAlpha;
}
static void getOutline(PngInfo* image) {
- int midX = image->width / 2;
- int midY = image->height / 2;
- int endX = image->width - 2;
- int endY = image->height - 2;
+ int midX = image->width / 2;
+ int midY = image->height / 2;
+ int endX = image->width - 2;
+ int endY = image->height - 2;
- // find left and right extent of nine patch content on center row
- if (image->width > 4) {
- findMaxOpacity(image->rows.data(), 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft);
- findMaxOpacity(image->rows.data(), endX, midY, midX, -1, -1, 0,
- &image->outlineInsetsRight);
- } else {
- image->outlineInsetsLeft = 0;
- image->outlineInsetsRight = 0;
- }
+ // find left and right extent of nine patch content on center row
+ if (image->width > 4) {
+ findMaxOpacity(image->rows.data(), 1, midY, midX, -1, 1, 0,
+ &image->outlineInsetsLeft);
+ findMaxOpacity(image->rows.data(), endX, midY, midX, -1, -1, 0,
+ &image->outlineInsetsRight);
+ } else {
+ image->outlineInsetsLeft = 0;
+ image->outlineInsetsRight = 0;
+ }
- // find top and bottom extent of nine patch content on center column
- if (image->height > 4) {
- findMaxOpacity(image->rows.data(), midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop);
- findMaxOpacity(image->rows.data(), midX, endY, -1, midY, 0, -1,
- &image->outlineInsetsBottom);
- } else {
- image->outlineInsetsTop = 0;
- image->outlineInsetsBottom = 0;
- }
+ // find top and bottom extent of nine patch content on center column
+ if (image->height > 4) {
+ findMaxOpacity(image->rows.data(), midX, 1, -1, midY, 0, 1,
+ &image->outlineInsetsTop);
+ findMaxOpacity(image->rows.data(), midX, endY, -1, midY, 0, -1,
+ &image->outlineInsetsBottom);
+ } else {
+ image->outlineInsetsTop = 0;
+ image->outlineInsetsBottom = 0;
+ }
- int innerStartX = 1 + image->outlineInsetsLeft;
- int innerStartY = 1 + image->outlineInsetsTop;
- int innerEndX = endX - image->outlineInsetsRight;
- int innerEndY = endY - image->outlineInsetsBottom;
- int innerMidX = (innerEndX + innerStartX) / 2;
- int innerMidY = (innerEndY + innerStartY) / 2;
+ int innerStartX = 1 + image->outlineInsetsLeft;
+ int innerStartY = 1 + image->outlineInsetsTop;
+ int innerEndX = endX - image->outlineInsetsRight;
+ int innerEndY = endY - image->outlineInsetsBottom;
+ int innerMidX = (innerEndX + innerStartX) / 2;
+ int innerMidY = (innerEndY + innerStartY) / 2;
- // assuming the image is a round rect, compute the radius by marching
- // diagonally from the top left corner towards the center
- image->outlineAlpha = std::max(
- maxAlphaOverRow(image->rows[innerMidY], innerStartX, innerEndX),
- maxAlphaOverCol(image->rows.data(), innerMidX, innerStartY, innerStartY));
+ // assuming the image is a round rect, compute the radius by marching
+ // diagonally from the top left corner towards the center
+ image->outlineAlpha = std::max(
+ maxAlphaOverRow(image->rows[innerMidY], innerStartX, innerEndX),
+ maxAlphaOverCol(image->rows.data(), innerMidX, innerStartY, innerStartY));
- int diagonalInset = 0;
- findMaxOpacity(image->rows.data(), innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
- &diagonalInset);
+ int diagonalInset = 0;
+ findMaxOpacity(image->rows.data(), innerStartX, innerStartY, innerMidX,
+ innerMidY, 1, 1, &diagonalInset);
- /* Determine source radius based upon inset:
- * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
- * sqrt(2) * r = sqrt(2) * i + r
- * (sqrt(2) - 1) * r = sqrt(2) * i
- * r = sqrt(2) / (sqrt(2) - 1) * i
- */
- image->outlineRadius = 3.4142f * diagonalInset;
+ /* Determine source radius based upon inset:
+ * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
+ * sqrt(2) * r = sqrt(2) * i + r
+ * (sqrt(2) - 1) * r = sqrt(2) * i
+ * r = sqrt(2) / (sqrt(2) - 1) * i
+ */
+ image->outlineRadius = 3.4142f * diagonalInset;
- if (kDebug) {
- printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
- image->outlineInsetsLeft,
- image->outlineInsetsTop,
- image->outlineInsetsRight,
- image->outlineInsetsBottom,
- image->outlineRadius,
- image->outlineAlpha);
- }
+ if (kDebug) {
+ printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
+ image->outlineInsetsLeft, image->outlineInsetsTop,
+ image->outlineInsetsRight, image->outlineInsetsBottom,
+ image->outlineRadius, image->outlineAlpha);
+ }
}
-static uint32_t getColor(png_bytepp rows, int left, int top, int right, int bottom) {
- png_bytep color = rows[top] + left*4;
+static uint32_t getColor(png_bytepp rows, int left, int top, int right,
+ int bottom) {
+ png_bytep color = rows[top] + left * 4;
- if (left > right || top > bottom) {
- return android::Res_png_9patch::TRANSPARENT_COLOR;
- }
+ if (left > right || top > bottom) {
+ return android::Res_png_9patch::TRANSPARENT_COLOR;
+ }
- while (top <= bottom) {
- for (int i = left; i <= right; i++) {
- png_bytep p = rows[top]+i*4;
- if (color[3] == 0) {
- if (p[3] != 0) {
- return android::Res_png_9patch::NO_COLOR;
- }
- } else if (p[0] != color[0] || p[1] != color[1] ||
- p[2] != color[2] || p[3] != color[3]) {
- return android::Res_png_9patch::NO_COLOR;
- }
+ while (top <= bottom) {
+ for (int i = left; i <= right; i++) {
+ png_bytep p = rows[top] + i * 4;
+ if (color[3] == 0) {
+ if (p[3] != 0) {
+ return android::Res_png_9patch::NO_COLOR;
}
- top++;
+ } else if (p[0] != color[0] || p[1] != color[1] || p[2] != color[2] ||
+ p[3] != color[3]) {
+ return android::Res_png_9patch::NO_COLOR;
+ }
}
+ top++;
+ }
- if (color[3] == 0) {
- return android::Res_png_9patch::TRANSPARENT_COLOR;
- }
- return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
+ if (color[3] == 0) {
+ return android::Res_png_9patch::TRANSPARENT_COLOR;
+ }
+ return (color[3] << 24) | (color[0] << 16) | (color[1] << 8) | color[2];
}
static bool do9Patch(PngInfo* image, std::string* outError) {
- image->is9Patch = true;
+ image->is9Patch = true;
- int W = image->width;
- int H = image->height;
- int i, j;
+ int W = image->width;
+ int H = image->height;
+ int i, j;
- const int maxSizeXDivs = W * sizeof(int32_t);
- const int maxSizeYDivs = H * sizeof(int32_t);
- int32_t* xDivs = image->xDivs = new int32_t[W];
- int32_t* yDivs = image->yDivs = new int32_t[H];
- uint8_t numXDivs = 0;
- uint8_t numYDivs = 0;
+ const int maxSizeXDivs = W * sizeof(int32_t);
+ const int maxSizeYDivs = H * sizeof(int32_t);
+ int32_t* xDivs = image->xDivs = new int32_t[W];
+ int32_t* yDivs = image->yDivs = new int32_t[H];
+ uint8_t numXDivs = 0;
+ uint8_t numYDivs = 0;
- int8_t numColors;
- int numRows;
- int numCols;
- int top;
- int left;
- int right;
- int bottom;
- memset(xDivs, -1, maxSizeXDivs);
- memset(yDivs, -1, maxSizeYDivs);
- image->info9Patch.paddingLeft = image->info9Patch.paddingRight = -1;
- image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
- image->layoutBoundsLeft = image->layoutBoundsRight = 0;
- image->layoutBoundsTop = image->layoutBoundsBottom = 0;
+ int8_t numColors;
+ int numRows;
+ int numCols;
+ int top;
+ int left;
+ int right;
+ int bottom;
+ memset(xDivs, -1, maxSizeXDivs);
+ memset(yDivs, -1, maxSizeYDivs);
+ image->info9Patch.paddingLeft = image->info9Patch.paddingRight = -1;
+ image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
+ image->layoutBoundsLeft = image->layoutBoundsRight = 0;
+ image->layoutBoundsTop = image->layoutBoundsBottom = 0;
- png_bytep p = image->rows[0];
- bool transparent = p[3] == 0;
- bool hasColor = false;
+ png_bytep p = image->rows[0];
+ bool transparent = p[3] == 0;
+ bool hasColor = false;
- const char* errorMsg = nullptr;
- int errorPixel = -1;
- const char* errorEdge = nullptr;
+ const char* errorMsg = nullptr;
+ int errorPixel = -1;
+ const char* errorEdge = nullptr;
- int colorIndex = 0;
- std::vector<png_bytep> newRows;
+ int colorIndex = 0;
+ std::vector<png_bytep> newRows;
- // Validate size...
- if (W < 3 || H < 3) {
- errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
- goto getout;
+ // Validate size...
+ if (W < 3 || H < 3) {
+ errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
+ goto getout;
+ }
+
+ // Validate frame...
+ if (!transparent &&
+ (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
+ errorMsg = "Must have one-pixel frame that is either transparent or white";
+ goto getout;
+ }
+
+ // Find left and right of sizing areas...
+ if (!getHorizontalTicks(p, W, transparent, true, &xDivs[0], &xDivs[1],
+ &errorMsg, &numXDivs, true)) {
+ errorPixel = xDivs[0];
+ errorEdge = "top";
+ goto getout;
+ }
+
+ // Find top and bottom of sizing areas...
+ if (!getVerticalTicks(image->rows.data(), 0, H, transparent, true, &yDivs[0],
+ &yDivs[1], &errorMsg, &numYDivs, true)) {
+ errorPixel = yDivs[0];
+ errorEdge = "left";
+ goto getout;
+ }
+
+ // Copy patch size data into image...
+ image->info9Patch.numXDivs = numXDivs;
+ image->info9Patch.numYDivs = numYDivs;
+
+ // Find left and right of padding area...
+ if (!getHorizontalTicks(image->rows[H - 1], W, transparent, false,
+ &image->info9Patch.paddingLeft,
+ &image->info9Patch.paddingRight, &errorMsg, nullptr,
+ false)) {
+ errorPixel = image->info9Patch.paddingLeft;
+ errorEdge = "bottom";
+ goto getout;
+ }
+
+ // Find top and bottom of padding area...
+ if (!getVerticalTicks(image->rows.data(), (W - 1) * 4, H, transparent, false,
+ &image->info9Patch.paddingTop,
+ &image->info9Patch.paddingBottom, &errorMsg, nullptr,
+ false)) {
+ errorPixel = image->info9Patch.paddingTop;
+ errorEdge = "right";
+ goto getout;
+ }
+
+ // Find left and right of layout padding...
+ getHorizontalLayoutBoundsTicks(image->rows[H - 1], W, transparent, false,
+ &image->layoutBoundsLeft,
+ &image->layoutBoundsRight, &errorMsg);
+
+ getVerticalLayoutBoundsTicks(image->rows.data(), (W - 1) * 4, H, transparent,
+ false, &image->layoutBoundsTop,
+ &image->layoutBoundsBottom, &errorMsg);
+
+ image->haveLayoutBounds =
+ image->layoutBoundsLeft != 0 || image->layoutBoundsRight != 0 ||
+ image->layoutBoundsTop != 0 || image->layoutBoundsBottom != 0;
+
+ if (image->haveLayoutBounds) {
+ if (kDebug) {
+ printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft,
+ image->layoutBoundsTop, image->layoutBoundsRight,
+ image->layoutBoundsBottom);
}
+ }
- // Validate frame...
- if (!transparent &&
- (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
- errorMsg = "Must have one-pixel frame that is either transparent or white";
- goto getout;
- }
+ // use opacity of pixels to estimate the round rect outline
+ getOutline(image);
- // Find left and right of sizing areas...
- if (!getHorizontalTicks(p, W, transparent, true, &xDivs[0], &xDivs[1], &errorMsg, &numXDivs,
- true)) {
- errorPixel = xDivs[0];
- errorEdge = "top";
- goto getout;
- }
+ // If padding is not yet specified, take values from size.
+ if (image->info9Patch.paddingLeft < 0) {
+ image->info9Patch.paddingLeft = xDivs[0];
+ image->info9Patch.paddingRight = W - 2 - xDivs[1];
+ } else {
+ // Adjust value to be correct!
+ image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
+ }
+ if (image->info9Patch.paddingTop < 0) {
+ image->info9Patch.paddingTop = yDivs[0];
+ image->info9Patch.paddingBottom = H - 2 - yDivs[1];
+ } else {
+ // Adjust value to be correct!
+ image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
+ }
- // Find top and bottom of sizing areas...
- if (!getVerticalTicks(image->rows.data(), 0, H, transparent, true, &yDivs[0], &yDivs[1],
- &errorMsg, &numYDivs, true)) {
- errorPixel = yDivs[0];
- errorEdge = "left";
- goto getout;
- }
+ /* if (kDebug) {
+ printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
+ xDivs[0], xDivs[1],
+ yDivs[0], yDivs[1]);
+ printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
+ image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
+ image->info9Patch.paddingTop,
+ image->info9Patch.paddingBottom);
+ }*/
- // Copy patch size data into image...
- image->info9Patch.numXDivs = numXDivs;
- image->info9Patch.numYDivs = numYDivs;
+ // Remove frame from image.
+ newRows.resize(H - 2);
+ for (i = 0; i < H - 2; i++) {
+ newRows[i] = image->rows[i + 1];
+ memmove(newRows[i], newRows[i] + 4, (W - 2) * 4);
+ }
+ image->rows.swap(newRows);
- // Find left and right of padding area...
- if (!getHorizontalTicks(image->rows[H-1], W, transparent, false,
- &image->info9Patch.paddingLeft, &image->info9Patch.paddingRight,
- &errorMsg, nullptr, false)) {
- errorPixel = image->info9Patch.paddingLeft;
- errorEdge = "bottom";
- goto getout;
- }
+ image->width -= 2;
+ W = image->width;
+ image->height -= 2;
+ H = image->height;
- // Find top and bottom of padding area...
- if (!getVerticalTicks(image->rows.data(), (W-1)*4, H, transparent, false,
- &image->info9Patch.paddingTop, &image->info9Patch.paddingBottom,
- &errorMsg, nullptr, false)) {
- errorPixel = image->info9Patch.paddingTop;
- errorEdge = "right";
- goto getout;
- }
+ // Figure out the number of rows and columns in the N-patch
+ numCols = numXDivs + 1;
+ if (xDivs[0] == 0) { // Column 1 is strechable
+ numCols--;
+ }
+ if (xDivs[numXDivs - 1] == W) {
+ numCols--;
+ }
+ numRows = numYDivs + 1;
+ if (yDivs[0] == 0) { // Row 1 is strechable
+ numRows--;
+ }
+ if (yDivs[numYDivs - 1] == H) {
+ numRows--;
+ }
- // Find left and right of layout padding...
- getHorizontalLayoutBoundsTicks(image->rows[H-1], W, transparent, false,
- &image->layoutBoundsLeft, &image->layoutBoundsRight, &errorMsg);
+ // Make sure the amount of rows and columns will fit in the number of
+ // colors we can use in the 9-patch format.
+ if (numRows * numCols > 0x7F) {
+ errorMsg = "Too many rows and columns in 9-patch perimeter";
+ goto getout;
+ }
- getVerticalLayoutBoundsTicks(image->rows.data(), (W-1)*4, H, transparent, false,
- &image->layoutBoundsTop, &image->layoutBoundsBottom, &errorMsg);
+ numColors = numRows * numCols;
+ image->info9Patch.numColors = numColors;
+ image->colors.resize(numColors);
- image->haveLayoutBounds = image->layoutBoundsLeft != 0
- || image->layoutBoundsRight != 0
- || image->layoutBoundsTop != 0
- || image->layoutBoundsBottom != 0;
+ // Fill in color information for each patch.
- if (image->haveLayoutBounds) {
- if (kDebug) {
- printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
- image->layoutBoundsRight, image->layoutBoundsBottom);
- }
- }
+ uint32_t c;
+ top = 0;
- // use opacity of pixels to estimate the round rect outline
- getOutline(image);
+ // The first row always starts with the top being at y=0 and the bottom
+ // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case
+ // the first row is stretchable along the Y axis, otherwise it is fixed.
+ // The last row always ends with the bottom being bitmap.height and the top
+ // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
+ // yDivs[numYDivs-1]. In the former case the last row is stretchable along
+ // the Y axis, otherwise it is fixed.
+ //
+ // The first and last columns are similarly treated with respect to the X
+ // axis.
+ //
+ // The above is to help explain some of the special casing that goes on the
+ // code below.
- // If padding is not yet specified, take values from size.
- if (image->info9Patch.paddingLeft < 0) {
- image->info9Patch.paddingLeft = xDivs[0];
- image->info9Patch.paddingRight = W - 2 - xDivs[1];
+ // The initial yDiv and whether the first row is considered stretchable or
+ // not depends on whether yDiv[0] was zero or not.
+ for (j = (yDivs[0] == 0 ? 1 : 0); j <= numYDivs && top < H; j++) {
+ if (j == numYDivs) {
+ bottom = H;
} else {
- // Adjust value to be correct!
- image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
+ bottom = yDivs[j];
}
- if (image->info9Patch.paddingTop < 0) {
- image->info9Patch.paddingTop = yDivs[0];
- image->info9Patch.paddingBottom = H - 2 - yDivs[1];
- } else {
- // Adjust value to be correct!
- image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
- }
-
-/* if (kDebug) {
- printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
- xDivs[0], xDivs[1],
- yDivs[0], yDivs[1]);
- printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
- image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
- image->info9Patch.paddingTop, image->info9Patch.paddingBottom);
- }*/
-
- // Remove frame from image.
- newRows.resize(H - 2);
- for (i = 0; i < H - 2; i++) {
- newRows[i] = image->rows[i + 1];
- memmove(newRows[i], newRows[i] + 4, (W - 2) * 4);
- }
- image->rows.swap(newRows);
-
- image->width -= 2;
- W = image->width;
- image->height -= 2;
- H = image->height;
-
- // Figure out the number of rows and columns in the N-patch
- numCols = numXDivs + 1;
- if (xDivs[0] == 0) { // Column 1 is strechable
- numCols--;
- }
- if (xDivs[numXDivs - 1] == W) {
- numCols--;
- }
- numRows = numYDivs + 1;
- if (yDivs[0] == 0) { // Row 1 is strechable
- numRows--;
- }
- if (yDivs[numYDivs - 1] == H) {
- numRows--;
- }
-
- // Make sure the amount of rows and columns will fit in the number of
- // colors we can use in the 9-patch format.
- if (numRows * numCols > 0x7F) {
- errorMsg = "Too many rows and columns in 9-patch perimeter";
- goto getout;
- }
-
- numColors = numRows * numCols;
- image->info9Patch.numColors = numColors;
- image->colors.resize(numColors);
-
- // Fill in color information for each patch.
-
- uint32_t c;
- top = 0;
-
- // The first row always starts with the top being at y=0 and the bottom
- // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case
- // the first row is stretchable along the Y axis, otherwise it is fixed.
- // The last row always ends with the bottom being bitmap.height and the top
- // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
- // yDivs[numYDivs-1]. In the former case the last row is stretchable along
- // the Y axis, otherwise it is fixed.
- //
- // The first and last columns are similarly treated with respect to the X
- // axis.
- //
- // The above is to help explain some of the special casing that goes on the
- // code below.
-
- // The initial yDiv and whether the first row is considered stretchable or
- // not depends on whether yDiv[0] was zero or not.
- for (j = (yDivs[0] == 0 ? 1 : 0); j <= numYDivs && top < H; j++) {
- if (j == numYDivs) {
- bottom = H;
- } else {
- bottom = yDivs[j];
+ left = 0;
+ // The initial xDiv and whether the first column is considered
+ // stretchable or not depends on whether xDiv[0] was zero or not.
+ for (i = xDivs[0] == 0 ? 1 : 0; i <= numXDivs && left < W; i++) {
+ if (i == numXDivs) {
+ right = W;
+ } else {
+ right = xDivs[i];
+ }
+ c = getColor(image->rows.data(), left, top, right - 1, bottom - 1);
+ image->colors[colorIndex++] = c;
+ if (kDebug) {
+ if (c != android::Res_png_9patch::NO_COLOR) {
+ hasColor = true;
}
- left = 0;
- // The initial xDiv and whether the first column is considered
- // stretchable or not depends on whether xDiv[0] was zero or not.
- for (i = xDivs[0] == 0 ? 1 : 0; i <= numXDivs && left < W; i++) {
- if (i == numXDivs) {
- right = W;
- } else {
- right = xDivs[i];
- }
- c = getColor(image->rows.data(), left, top, right - 1, bottom - 1);
- image->colors[colorIndex++] = c;
- if (kDebug) {
- if (c != android::Res_png_9patch::NO_COLOR) {
- hasColor = true;
- }
- }
- left = right;
- }
- top = bottom;
+ }
+ left = right;
}
+ top = bottom;
+ }
- assert(colorIndex == numColors);
+ assert(colorIndex == numColors);
- if (kDebug && hasColor) {
- for (i = 0; i < numColors; i++) {
- if (i == 0) printf("Colors:\n");
- printf(" #%08x", image->colors[i]);
- if (i == numColors - 1) printf("\n");
- }
+ if (kDebug && hasColor) {
+ for (i = 0; i < numColors; i++) {
+ if (i == 0) printf("Colors:\n");
+ printf(" #%08x", image->colors[i]);
+ if (i == numColors - 1) printf("\n");
}
+ }
getout:
- if (errorMsg) {
- std::stringstream err;
- err << "9-patch malformed: " << errorMsg;
- if (errorEdge) {
- err << "." << std::endl;
- if (errorPixel >= 0) {
- err << "Found at pixel #" << errorPixel << " along " << errorEdge << " edge";
- } else {
- err << "Found along " << errorEdge << " edge";
- }
- }
- *outError = err.str();
- return false;
+ if (errorMsg) {
+ std::stringstream err;
+ err << "9-patch malformed: " << errorMsg;
+ if (errorEdge) {
+ err << "." << std::endl;
+ if (errorPixel >= 0) {
+ err << "Found at pixel #" << errorPixel << " along " << errorEdge
+ << " edge";
+ } else {
+ err << "Found along " << errorEdge << " edge";
+ }
}
- return true;
+ *outError = err.str();
+ return false;
+ }
+ return true;
}
+bool Png::process(const Source& source, std::istream* input,
+ BigBuffer* outBuffer, const PngOptions& options) {
+ png_byte signature[kPngSignatureSize];
-bool Png::process(const Source& source, std::istream* input, BigBuffer* outBuffer,
- const PngOptions& options) {
- png_byte signature[kPngSignatureSize];
+ // Read the PNG signature first.
+ if (!input->read(reinterpret_cast<char*>(signature), kPngSignatureSize)) {
+ mDiag->Error(DiagMessage() << strerror(errno));
+ return false;
+ }
- // Read the PNG signature first.
- if (!input->read(reinterpret_cast<char*>(signature), kPngSignatureSize)) {
- mDiag->error(DiagMessage() << strerror(errno));
- return false;
+ // If the PNG signature doesn't match, bail early.
+ if (png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
+ mDiag->Error(DiagMessage() << "not a valid png file");
+ return false;
+ }
+
+ bool result = false;
+ png_structp readPtr = nullptr;
+ png_infop infoPtr = nullptr;
+ png_structp writePtr = nullptr;
+ png_infop writeInfoPtr = nullptr;
+ PngInfo pngInfo = {};
+
+ readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
+ if (!readPtr) {
+ mDiag->Error(DiagMessage() << "failed to allocate read ptr");
+ goto bail;
+ }
+
+ infoPtr = png_create_info_struct(readPtr);
+ if (!infoPtr) {
+ mDiag->Error(DiagMessage() << "failed to allocate info ptr");
+ goto bail;
+ }
+
+ png_set_error_fn(readPtr, reinterpret_cast<png_voidp>(mDiag), nullptr,
+ logWarning);
+
+ // Set the read function to read from std::istream.
+ png_set_read_fn(readPtr, (png_voidp)input, readDataFromStream);
+
+ if (!readPng(mDiag, readPtr, infoPtr, &pngInfo)) {
+ goto bail;
+ }
+
+ if (util::EndsWith(source.path, ".9.png")) {
+ std::string errorMsg;
+ if (!do9Patch(&pngInfo, &errorMsg)) {
+ mDiag->Error(DiagMessage() << errorMsg);
+ goto bail;
}
+ }
- // If the PNG signature doesn't match, bail early.
- if (png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
- mDiag->error(DiagMessage() << "not a valid png file");
- return false;
- }
+ writePtr =
+ png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
+ if (!writePtr) {
+ mDiag->Error(DiagMessage() << "failed to allocate write ptr");
+ goto bail;
+ }
- bool result = false;
- png_structp readPtr = nullptr;
- png_infop infoPtr = nullptr;
- png_structp writePtr = nullptr;
- png_infop writeInfoPtr = nullptr;
- PngInfo pngInfo = {};
+ writeInfoPtr = png_create_info_struct(writePtr);
+ if (!writeInfoPtr) {
+ mDiag->Error(DiagMessage() << "failed to allocate write info ptr");
+ goto bail;
+ }
- readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
- if (!readPtr) {
- mDiag->error(DiagMessage() << "failed to allocate read ptr");
- goto bail;
- }
+ png_set_error_fn(writePtr, nullptr, nullptr, logWarning);
- infoPtr = png_create_info_struct(readPtr);
- if (!infoPtr) {
- mDiag->error(DiagMessage() << "failed to allocate info ptr");
- goto bail;
- }
+ // Set the write function to write to std::ostream.
+ png_set_write_fn(writePtr, (png_voidp)outBuffer, writeDataToStream,
+ flushDataToStream);
- png_set_error_fn(readPtr, reinterpret_cast<png_voidp>(mDiag), nullptr, logWarning);
+ if (!writePng(mDiag, writePtr, writeInfoPtr, &pngInfo,
+ options.grayscale_tolerance)) {
+ goto bail;
+ }
- // Set the read function to read from std::istream.
- png_set_read_fn(readPtr, (png_voidp) input, readDataFromStream);
-
- if (!readPng(mDiag, readPtr, infoPtr, &pngInfo)) {
- goto bail;
- }
-
- if (util::stringEndsWith(source.path, ".9.png")) {
- std::string errorMsg;
- if (!do9Patch(&pngInfo, &errorMsg)) {
- mDiag->error(DiagMessage() << errorMsg);
- goto bail;
- }
- }
-
- writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
- if (!writePtr) {
- mDiag->error(DiagMessage() << "failed to allocate write ptr");
- goto bail;
- }
-
- writeInfoPtr = png_create_info_struct(writePtr);
- if (!writeInfoPtr) {
- mDiag->error(DiagMessage() << "failed to allocate write info ptr");
- goto bail;
- }
-
- png_set_error_fn(writePtr, nullptr, nullptr, logWarning);
-
- // Set the write function to write to std::ostream.
- png_set_write_fn(writePtr, (png_voidp)outBuffer, writeDataToStream, flushDataToStream);
-
- if (!writePng(mDiag, writePtr, writeInfoPtr, &pngInfo, options.grayScaleTolerance)) {
- goto bail;
- }
-
- result = true;
+ result = true;
bail:
- if (readPtr) {
- png_destroy_read_struct(&readPtr, &infoPtr, nullptr);
- }
+ if (readPtr) {
+ png_destroy_read_struct(&readPtr, &infoPtr, nullptr);
+ }
- if (writePtr) {
- png_destroy_write_struct(&writePtr, &writeInfoPtr);
- }
- return result;
+ if (writePtr) {
+ png_destroy_write_struct(&writePtr, &writeInfoPtr);
+ }
+ return result;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/Png.h b/tools/aapt2/compile/Png.h
index 4a15d95..01c9adb 100644
--- a/tools/aapt2/compile/Png.h
+++ b/tools/aapt2/compile/Png.h
@@ -17,6 +17,11 @@
#ifndef AAPT_PNG_H
#define AAPT_PNG_H
+#include <iostream>
+#include <string>
+
+#include "android-base/macros.h"
+
#include "Diagnostics.h"
#include "Source.h"
#include "compile/Image.h"
@@ -24,71 +29,69 @@
#include "process/IResourceTableConsumer.h"
#include "util/BigBuffer.h"
-#include <android-base/macros.h>
-#include <iostream>
-#include <string>
-
namespace aapt {
struct PngOptions {
- int grayScaleTolerance = 0;
+ int grayscale_tolerance = 0;
};
+/**
+ * Deprecated. Removing once new PNG crunching code is proved to be correct.
+ */
class Png {
-public:
- explicit Png(IDiagnostics* diag) : mDiag(diag) {
- }
+ public:
+ explicit Png(IDiagnostics* diag) : mDiag(diag) {}
- bool process(const Source& source, std::istream* input, BigBuffer* outBuffer,
- const PngOptions& options);
+ bool process(const Source& source, std::istream* input, BigBuffer* outBuffer,
+ const PngOptions& options);
-private:
- IDiagnostics* mDiag;
+ private:
+ IDiagnostics* mDiag;
- DISALLOW_COPY_AND_ASSIGN(Png);
+ DISALLOW_COPY_AND_ASSIGN(Png);
};
/**
* An InputStream that filters out unimportant PNG chunks.
*/
class PngChunkFilter : public io::InputStream {
-public:
- explicit PngChunkFilter(const StringPiece& data);
+ public:
+ explicit PngChunkFilter(const StringPiece& data);
- bool Next(const void** buffer, int* len) override;
- void BackUp(int count) override;
- bool Skip(int count) override;
+ bool Next(const void** buffer, int* len) override;
+ void BackUp(int count) override;
+ bool Skip(int count) override;
- int64_t ByteCount() const override {
- return static_cast<int64_t>(mWindowStart);
- }
+ int64_t ByteCount() const override {
+ return static_cast<int64_t>(window_start_);
+ }
- bool HadError() const override {
- return mError;
- }
+ bool HadError() const override { return error_; }
-private:
- bool consumeWindow(const void** buffer, int* len);
+ private:
+ bool ConsumeWindow(const void** buffer, int* len);
- StringPiece mData;
- size_t mWindowStart = 0;
- size_t mWindowEnd = 0;
- bool mError = false;
+ StringPiece data_;
+ size_t window_start_ = 0;
+ size_t window_end_ = 0;
+ bool error_ = false;
- DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
+ DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
};
/**
* Reads a PNG from the InputStream into memory as an RGBA Image.
*/
-std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in);
+std::unique_ptr<Image> ReadPng(IAaptContext* context, io::InputStream* in);
/**
- * Writes the RGBA Image, with optional 9-patch meta-data, into the OutputStream as a PNG.
+ * Writes the RGBA Image, with optional 9-patch meta-data, into the OutputStream
+ * as a PNG.
*/
-bool writePng(IAaptContext* context, const Image* image, const NinePatch* ninePatch,
- io::OutputStream* out, const PngOptions& options);
+bool WritePng(IAaptContext* context, const Image* image,
+ const NinePatch* nine_patch, io::OutputStream* out,
+ const PngOptions& options);
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_PNG_H
+#endif // AAPT_PNG_H
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/tools/aapt2/compile/PngChunkFilter.cpp
index 70a881f..4cbefb9 100644
--- a/tools/aapt2/compile/PngChunkFilter.cpp
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -15,6 +15,7 @@
*/
#include "compile/Png.h"
+
#include "io/Io.h"
#include "util/StringPiece.h"
@@ -25,149 +26,149 @@
// Useful helper function that encodes individual bytes into a uint32
// at compile time.
constexpr uint32_t u32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
- return (((uint32_t) a) << 24)
- | (((uint32_t) b) << 16)
- | (((uint32_t) c) << 8)
- | ((uint32_t) d);
+ return (((uint32_t)a) << 24) | (((uint32_t)b) << 16) | (((uint32_t)c) << 8) |
+ ((uint32_t)d);
}
// Whitelist of PNG chunk types that we want to keep in the resulting PNG.
enum PngChunkTypes {
- kPngChunkIHDR = u32(73, 72, 68, 82),
- kPngChunkIDAT = u32(73, 68, 65, 84),
- kPngChunkIEND = u32(73, 69, 78, 68),
- kPngChunkPLTE = u32(80, 76, 84, 69),
- kPngChunktRNS = u32(116, 82, 78, 83),
- kPngChunksRGB = u32(115, 82, 71, 66),
+ kPngChunkIHDR = u32(73, 72, 68, 82),
+ kPngChunkIDAT = u32(73, 68, 65, 84),
+ kPngChunkIEND = u32(73, 69, 78, 68),
+ kPngChunkPLTE = u32(80, 76, 84, 69),
+ kPngChunktRNS = u32(116, 82, 78, 83),
+ kPngChunksRGB = u32(115, 82, 71, 66),
};
-static uint32_t peek32LE(const char* data) {
- uint32_t word = ((uint32_t) data[0]) & 0x000000ff;
- word <<= 8;
- word |= ((uint32_t) data[1]) & 0x000000ff;
- word <<= 8;
- word |= ((uint32_t) data[2]) & 0x000000ff;
- word <<= 8;
- word |= ((uint32_t) data[3]) & 0x000000ff;
- return word;
+static uint32_t Peek32LE(const char* data) {
+ uint32_t word = ((uint32_t)data[0]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t)data[1]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t)data[2]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t)data[3]) & 0x000000ff;
+ return word;
}
-static bool isPngChunkWhitelisted(uint32_t type) {
- switch (type) {
+static bool IsPngChunkWhitelisted(uint32_t type) {
+ switch (type) {
case kPngChunkIHDR:
case kPngChunkIDAT:
case kPngChunkIEND:
case kPngChunkPLTE:
case kPngChunktRNS:
case kPngChunksRGB:
- return true;
+ return true;
default:
- return false;
- }
+ return false;
+ }
}
-PngChunkFilter::PngChunkFilter(const StringPiece& data) : mData(data) {
- if (util::stringStartsWith(mData, kPngSignature)) {
- mWindowStart = 0;
- mWindowEnd = strlen(kPngSignature);
- } else {
- mError = true;
- }
+PngChunkFilter::PngChunkFilter(const StringPiece& data) : data_(data) {
+ if (util::StartsWith(data_, kPngSignature)) {
+ window_start_ = 0;
+ window_end_ = strlen(kPngSignature);
+ } else {
+ error_ = true;
+ }
}
-bool PngChunkFilter::consumeWindow(const void** buffer, int* len) {
- if (mWindowStart != mWindowEnd) {
- // We have bytes to give from our window.
- const int bytesRead = (int) (mWindowEnd - mWindowStart);
- *buffer = mData.data() + mWindowStart;
- *len = bytesRead;
- mWindowStart = mWindowEnd;
- return true;
- }
- return false;
+bool PngChunkFilter::ConsumeWindow(const void** buffer, int* len) {
+ if (window_start_ != window_end_) {
+ // We have bytes to give from our window.
+ const int bytes_read = (int)(window_end_ - window_start_);
+ *buffer = data_.data() + window_start_;
+ *len = bytes_read;
+ window_start_ = window_end_;
+ return true;
+ }
+ return false;
}
bool PngChunkFilter::Next(const void** buffer, int* len) {
- if (mError) {
- return false;
- }
-
- // In case BackUp was called, we must consume the window.
- if (consumeWindow(buffer, len)) {
- return true;
- }
-
- // Advance the window as far as possible (until we meet a chunk that
- // we want to strip).
- while (mWindowEnd < mData.size()) {
- // Chunk length (4 bytes) + type (4 bytes) + crc32 (4 bytes) = 12 bytes.
- const size_t kMinChunkHeaderSize = 3 * sizeof(uint32_t);
-
- // Is there enough room for a chunk header?
- if (mData.size() - mWindowStart < kMinChunkHeaderSize) {
- mError = true;
- return false;
- }
-
- // Verify the chunk length.
- const uint32_t chunkLen = peek32LE(mData.data() + mWindowEnd);
- if (((uint64_t) chunkLen) + ((uint64_t) mWindowEnd) + sizeof(uint32_t) > mData.size()) {
- // Overflow.
- mError = true;
- return false;
- }
-
- // Do we strip this chunk?
- const uint32_t chunkType = peek32LE(mData.data() + mWindowEnd + sizeof(uint32_t));
- if (isPngChunkWhitelisted(chunkType)) {
- // Advance the window to include this chunk.
- mWindowEnd += kMinChunkHeaderSize + chunkLen;
- } else {
- // We want to strip this chunk. If we accumulated a window,
- // we must return the window now.
- if (mWindowStart != mWindowEnd) {
- break;
- }
-
- // The window is empty, so we can advance past this chunk
- // and keep looking for the next good chunk,
- mWindowEnd += kMinChunkHeaderSize + chunkLen;
- mWindowStart = mWindowEnd;
- }
- }
-
- if (consumeWindow(buffer, len)) {
- return true;
- }
+ if (error_) {
return false;
+ }
+
+ // In case BackUp was called, we must consume the window.
+ if (ConsumeWindow(buffer, len)) {
+ return true;
+ }
+
+ // Advance the window as far as possible (until we meet a chunk that
+ // we want to strip).
+ while (window_end_ < data_.size()) {
+ // Chunk length (4 bytes) + type (4 bytes) + crc32 (4 bytes) = 12 bytes.
+ const size_t kMinChunkHeaderSize = 3 * sizeof(uint32_t);
+
+ // Is there enough room for a chunk header?
+ if (data_.size() - window_start_ < kMinChunkHeaderSize) {
+ error_ = true;
+ return false;
+ }
+
+ // Verify the chunk length.
+ const uint32_t chunk_len = Peek32LE(data_.data() + window_end_);
+ if (((uint64_t)chunk_len) + ((uint64_t)window_end_) + sizeof(uint32_t) >
+ data_.size()) {
+ // Overflow.
+ error_ = true;
+ return false;
+ }
+
+ // Do we strip this chunk?
+ const uint32_t chunk_type =
+ Peek32LE(data_.data() + window_end_ + sizeof(uint32_t));
+ if (IsPngChunkWhitelisted(chunk_type)) {
+ // Advance the window to include this chunk.
+ window_end_ += kMinChunkHeaderSize + chunk_len;
+ } else {
+ // We want to strip this chunk. If we accumulated a window,
+ // we must return the window now.
+ if (window_start_ != window_end_) {
+ break;
+ }
+
+ // The window is empty, so we can advance past this chunk
+ // and keep looking for the next good chunk,
+ window_end_ += kMinChunkHeaderSize + chunk_len;
+ window_start_ = window_end_;
+ }
+ }
+
+ if (ConsumeWindow(buffer, len)) {
+ return true;
+ }
+ return false;
}
void PngChunkFilter::BackUp(int count) {
- if (mError) {
- return;
- }
- mWindowStart -= count;
+ if (error_) {
+ return;
+ }
+ window_start_ -= count;
}
bool PngChunkFilter::Skip(int count) {
- if (mError) {
- return false;
- }
+ if (error_) {
+ return false;
+ }
- const void* buffer;
- int len;
- while (count > 0) {
- if (!Next(&buffer, &len)) {
- return false;
- }
- if (len > count) {
- BackUp(len - count);
- count = 0;
- } else {
- count -= len;
- }
+ const void* buffer;
+ int len;
+ while (count > 0) {
+ if (!Next(&buffer, &len)) {
+ return false;
}
- return true;
+ if (len > count) {
+ BackUp(len - count);
+ count = 0;
+ } else {
+ count -= len;
+ }
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/PngCrunch.cpp b/tools/aapt2/compile/PngCrunch.cpp
index a2e3f4f..3b46d8b 100644
--- a/tools/aapt2/compile/PngCrunch.cpp
+++ b/tools/aapt2/compile/PngCrunch.cpp
@@ -16,13 +16,16 @@
#include "compile/Png.h"
-#include <algorithm>
-#include <android-base/errors.h>
-#include <android-base/macros.h>
#include <png.h>
+#include <zlib.h>
+
+#include <algorithm>
#include <unordered_map>
#include <unordered_set>
-#include <zlib.h>
+
+#include "android-base/errors.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
namespace aapt {
@@ -33,250 +36,264 @@
* Custom deleter that destroys libpng read and info structs.
*/
class PngReadStructDeleter {
-public:
- explicit PngReadStructDeleter(png_structp readPtr, png_infop infoPtr) :
- mReadPtr(readPtr), mInfoPtr(infoPtr) {
- }
+ public:
+ PngReadStructDeleter(png_structp read_ptr, png_infop info_ptr)
+ : read_ptr_(read_ptr), info_ptr_(info_ptr) {}
- ~PngReadStructDeleter() {
- png_destroy_read_struct(&mReadPtr, &mInfoPtr, nullptr);
- }
+ ~PngReadStructDeleter() {
+ png_destroy_read_struct(&read_ptr_, &info_ptr_, nullptr);
+ }
-private:
- png_structp mReadPtr;
- png_infop mInfoPtr;
+ private:
+ png_structp read_ptr_;
+ png_infop info_ptr_;
- DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
+ DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
};
/**
* Custom deleter that destroys libpng write and info structs.
*/
class PngWriteStructDeleter {
-public:
- explicit PngWriteStructDeleter(png_structp writePtr, png_infop infoPtr) :
- mWritePtr(writePtr), mInfoPtr(infoPtr) {
- }
+ public:
+ PngWriteStructDeleter(png_structp write_ptr, png_infop info_ptr)
+ : write_ptr_(write_ptr), info_ptr_(info_ptr) {}
- ~PngWriteStructDeleter() {
- png_destroy_write_struct(&mWritePtr, &mInfoPtr);
- }
+ ~PngWriteStructDeleter() {
+ png_destroy_write_struct(&write_ptr_, &info_ptr_);
+ }
-private:
- png_structp mWritePtr;
- png_infop mInfoPtr;
+ private:
+ png_structp write_ptr_;
+ png_infop info_ptr_;
- DISALLOW_COPY_AND_ASSIGN(PngWriteStructDeleter);
+ DISALLOW_COPY_AND_ASSIGN(PngWriteStructDeleter);
};
// Custom warning logging method that uses IDiagnostics.
-static void logWarning(png_structp pngPtr, png_const_charp warningMsg) {
- IDiagnostics* diag = (IDiagnostics*) png_get_error_ptr(pngPtr);
- diag->warn(DiagMessage() << warningMsg);
+static void LogWarning(png_structp png_ptr, png_const_charp warning_msg) {
+ IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(png_ptr);
+ diag->Warn(DiagMessage() << warning_msg);
}
// Custom error logging method that uses IDiagnostics.
-static void logError(png_structp pngPtr, png_const_charp errorMsg) {
- IDiagnostics* diag = (IDiagnostics*) png_get_error_ptr(pngPtr);
- diag->error(DiagMessage() << errorMsg);
+static void LogError(png_structp png_ptr, png_const_charp error_msg) {
+ IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(png_ptr);
+ diag->Error(DiagMessage() << error_msg);
}
-static void readDataFromStream(png_structp pngPtr, png_bytep buffer, png_size_t len) {
- io::InputStream* in = (io::InputStream*) png_get_io_ptr(pngPtr);
+static void ReadDataFromStream(png_structp png_ptr, png_bytep buffer,
+ png_size_t len) {
+ io::InputStream* in = (io::InputStream*)png_get_io_ptr(png_ptr);
- const void* inBuffer;
- int inLen;
- if (!in->Next(&inBuffer, &inLen)) {
- if (in->HadError()) {
- std::string err = in->GetError();
- png_error(pngPtr, err.c_str());
- }
- return;
+ const void* in_buffer;
+ int in_len;
+ if (!in->Next(&in_buffer, &in_len)) {
+ if (in->HadError()) {
+ std::string err = in->GetError();
+ png_error(png_ptr, err.c_str());
}
+ return;
+ }
- const size_t bytesRead = std::min(static_cast<size_t>(inLen), len);
- memcpy(buffer, inBuffer, bytesRead);
- if (bytesRead != static_cast<size_t>(inLen)) {
- in->BackUp(inLen - static_cast<int>(bytesRead));
- }
+ const size_t bytes_read = std::min(static_cast<size_t>(in_len), len);
+ memcpy(buffer, in_buffer, bytes_read);
+ if (bytes_read != static_cast<size_t>(in_len)) {
+ in->BackUp(in_len - static_cast<int>(bytes_read));
+ }
}
-static void writeDataToStream(png_structp pngPtr, png_bytep buffer, png_size_t len) {
- io::OutputStream* out = (io::OutputStream*) png_get_io_ptr(pngPtr);
+static void WriteDataToStream(png_structp png_ptr, png_bytep buffer,
+ png_size_t len) {
+ io::OutputStream* out = (io::OutputStream*)png_get_io_ptr(png_ptr);
- void* outBuffer;
- int outLen;
- while (len > 0) {
- if (!out->Next(&outBuffer, &outLen)) {
- if (out->HadError()) {
- std::string err = out->GetError();
- png_error(pngPtr, err.c_str());
- }
- return;
- }
-
- const size_t bytesWritten = std::min(static_cast<size_t>(outLen), len);
- memcpy(outBuffer, buffer, bytesWritten);
-
- // Advance the input buffer.
- buffer += bytesWritten;
- len -= bytesWritten;
-
- // Advance the output buffer.
- outLen -= static_cast<int>(bytesWritten);
+ void* out_buffer;
+ int out_len;
+ while (len > 0) {
+ if (!out->Next(&out_buffer, &out_len)) {
+ if (out->HadError()) {
+ std::string err = out->GetError();
+ png_error(png_ptr, err.c_str());
+ }
+ return;
}
- // If the entire output buffer wasn't used, backup.
- if (outLen > 0) {
- out->BackUp(outLen);
- }
+ const size_t bytes_written = std::min(static_cast<size_t>(out_len), len);
+ memcpy(out_buffer, buffer, bytes_written);
+
+ // Advance the input buffer.
+ buffer += bytes_written;
+ len -= bytes_written;
+
+ // Advance the output buffer.
+ out_len -= static_cast<int>(bytes_written);
+ }
+
+ // If the entire output buffer wasn't used, backup.
+ if (out_len > 0) {
+ out->BackUp(out_len);
+ }
}
-std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in) {
- // Read the first 8 bytes of the file looking for the PNG signature.
- // Bail early if it does not match.
- const png_byte* signature;
- int bufferSize;
- if (!in->Next((const void**) &signature, &bufferSize)) {
- context->getDiagnostics()->error(DiagMessage()
- << android::base::SystemErrorCodeToString(errno));
- return {};
- }
+std::unique_ptr<Image> ReadPng(IAaptContext* context, io::InputStream* in) {
+ // Read the first 8 bytes of the file looking for the PNG signature.
+ // Bail early if it does not match.
+ const png_byte* signature;
+ int buffer_size;
+ if (!in->Next((const void**)&signature, &buffer_size)) {
+ context->GetDiagnostics()->Error(
+ DiagMessage() << android::base::SystemErrorCodeToString(errno));
+ return {};
+ }
- if (static_cast<size_t>(bufferSize) < kPngSignatureSize
- || png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
- context->getDiagnostics()->error(DiagMessage()
- << "file signature does not match PNG signature");
- return {};
- }
+ if (static_cast<size_t>(buffer_size) < kPngSignatureSize ||
+ png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
+ context->GetDiagnostics()->Error(
+ DiagMessage() << "file signature does not match PNG signature");
+ return {};
+ }
- // Start at the beginning of the first chunk.
- in->BackUp(bufferSize - static_cast<int>(kPngSignatureSize));
+ // Start at the beginning of the first chunk.
+ in->BackUp(buffer_size - static_cast<int>(kPngSignatureSize));
- // Create and initialize the png_struct with the default error and warning handlers.
- // The header version is also passed in to ensure that this was built against the same
- // version of libpng.
- png_structp readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
- if (readPtr == nullptr) {
- context->getDiagnostics()->error(DiagMessage()
- << "failed to create libpng read png_struct");
- return {};
- }
+ // Create and initialize the png_struct with the default error and warning
+ // handlers.
+ // The header version is also passed in to ensure that this was built against
+ // the same
+ // version of libpng.
+ png_structp read_ptr =
+ png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ if (read_ptr == nullptr) {
+ context->GetDiagnostics()->Error(
+ DiagMessage() << "failed to create libpng read png_struct");
+ return {};
+ }
- // Create and initialize the memory for image header and data.
- png_infop infoPtr = png_create_info_struct(readPtr);
- if (infoPtr == nullptr) {
- context->getDiagnostics()->error(DiagMessage() << "failed to create libpng read png_info");
- png_destroy_read_struct(&readPtr, nullptr, nullptr);
- return {};
- }
+ // Create and initialize the memory for image header and data.
+ png_infop info_ptr = png_create_info_struct(read_ptr);
+ if (info_ptr == nullptr) {
+ context->GetDiagnostics()->Error(
+ DiagMessage() << "failed to create libpng read png_info");
+ png_destroy_read_struct(&read_ptr, nullptr, nullptr);
+ return {};
+ }
- // Automatically release PNG resources at end of scope.
- PngReadStructDeleter pngReadDeleter(readPtr, infoPtr);
+ // Automatically release PNG resources at end of scope.
+ PngReadStructDeleter png_read_deleter(read_ptr, info_ptr);
- // libpng uses longjmp to jump to an error handling routine.
- // setjmp will only return true if it was jumped to, aka there was
- // an error.
- if (setjmp(png_jmpbuf(readPtr))) {
- return {};
- }
+ // libpng uses longjmp to jump to an error handling routine.
+ // setjmp will only return true if it was jumped to, aka there was
+ // an error.
+ if (setjmp(png_jmpbuf(read_ptr))) {
+ return {};
+ }
- // Handle warnings ourselves via IDiagnostics.
- png_set_error_fn(readPtr, (png_voidp) context->getDiagnostics(), logError, logWarning);
+ // Handle warnings ourselves via IDiagnostics.
+ png_set_error_fn(read_ptr, (png_voidp)context->GetDiagnostics(), LogError,
+ LogWarning);
- // Set up the read functions which read from our custom data sources.
- png_set_read_fn(readPtr, (png_voidp) in, readDataFromStream);
+ // Set up the read functions which read from our custom data sources.
+ png_set_read_fn(read_ptr, (png_voidp)in, ReadDataFromStream);
- // Skip the signature that we already read.
- png_set_sig_bytes(readPtr, kPngSignatureSize);
+ // Skip the signature that we already read.
+ png_set_sig_bytes(read_ptr, kPngSignatureSize);
- // Read the chunk headers.
- png_read_info(readPtr, infoPtr);
+ // Read the chunk headers.
+ png_read_info(read_ptr, info_ptr);
- // Extract image meta-data from the various chunk headers.
- uint32_t width, height;
- int bitDepth, colorType, interlaceMethod, compressionMethod, filterMethod;
- png_get_IHDR(readPtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceMethod,
- &compressionMethod, &filterMethod);
+ // Extract image meta-data from the various chunk headers.
+ uint32_t width, height;
+ int bit_depth, color_type, interlace_method, compression_method,
+ filter_method;
+ png_get_IHDR(read_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+ &interlace_method, &compression_method, &filter_method);
- // When the image is read, expand it so that it is in RGBA 8888 format
- // so that image handling is uniform.
+ // When the image is read, expand it so that it is in RGBA 8888 format
+ // so that image handling is uniform.
- if (colorType == PNG_COLOR_TYPE_PALETTE) {
- png_set_palette_to_rgb(readPtr);
- }
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(read_ptr);
+ }
- if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
- png_set_expand_gray_1_2_4_to_8(readPtr);
- }
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+ png_set_expand_gray_1_2_4_to_8(read_ptr);
+ }
- if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
- png_set_tRNS_to_alpha(readPtr);
- }
+ if (png_get_valid(read_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(read_ptr);
+ }
- if (bitDepth == 16) {
- png_set_strip_16(readPtr);
- }
+ if (bit_depth == 16) {
+ png_set_strip_16(read_ptr);
+ }
- if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
- png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
- }
+ if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
+ png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
+ }
- if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
- png_set_gray_to_rgb(readPtr);
- }
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(read_ptr);
+ }
- if (interlaceMethod != PNG_INTERLACE_NONE) {
- png_set_interlace_handling(readPtr);
- }
+ if (interlace_method != PNG_INTERLACE_NONE) {
+ png_set_interlace_handling(read_ptr);
+ }
- // Once all the options for reading have been set, we need to flush
- // them to libpng.
- png_read_update_info(readPtr, infoPtr);
+ // Once all the options for reading have been set, we need to flush
+ // them to libpng.
+ png_read_update_info(read_ptr, info_ptr);
- // 9-patch uses int32_t to index images, so we cap the image dimensions to something
- // that can always be represented by 9-patch.
- if (width > std::numeric_limits<int32_t>::max() ||
- height > std::numeric_limits<int32_t>::max()) {
- context->getDiagnostics()->error(DiagMessage() << "PNG image dimensions are too large: "
- << width << "x" << height);
- return {};
- }
+ // 9-patch uses int32_t to index images, so we cap the image dimensions to
+ // something
+ // that can always be represented by 9-patch.
+ if (width > std::numeric_limits<int32_t>::max() ||
+ height > std::numeric_limits<int32_t>::max()) {
+ context->GetDiagnostics()->Error(DiagMessage()
+ << "PNG image dimensions are too large: "
+ << width << "x" << height);
+ return {};
+ }
- std::unique_ptr<Image> outputImage = util::make_unique<Image>();
- outputImage->width = static_cast<int32_t>(width);
- outputImage->height = static_cast<int32_t>(height);
+ std::unique_ptr<Image> output_image = util::make_unique<Image>();
+ output_image->width = static_cast<int32_t>(width);
+ output_image->height = static_cast<int32_t>(height);
- const size_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
- assert(rowBytes == 4 * width); // RGBA
+ const size_t row_bytes = png_get_rowbytes(read_ptr, info_ptr);
+ CHECK(row_bytes == 4 * width); // RGBA
- // Allocate one large block to hold the image.
- outputImage->data = std::unique_ptr<uint8_t[]>(new uint8_t[height * rowBytes]);
+ // Allocate one large block to hold the image.
+ output_image->data =
+ std::unique_ptr<uint8_t[]>(new uint8_t[height * row_bytes]);
- // Create an array of rows that index into the data block.
- outputImage->rows = std::unique_ptr<uint8_t*[]>(new uint8_t*[height]);
- for (uint32_t h = 0; h < height; h++) {
- outputImage->rows[h] = outputImage->data.get() + (h * rowBytes);
- }
+ // Create an array of rows that index into the data block.
+ output_image->rows = std::unique_ptr<uint8_t* []>(new uint8_t*[height]);
+ for (uint32_t h = 0; h < height; h++) {
+ output_image->rows[h] = output_image->data.get() + (h * row_bytes);
+ }
- // Actually read the image pixels.
- png_read_image(readPtr, outputImage->rows.get());
+ // Actually read the image pixels.
+ png_read_image(read_ptr, output_image->rows.get());
- // Finish reading. This will read any other chunks after the image data.
- png_read_end(readPtr, infoPtr);
+ // Finish reading. This will read any other chunks after the image data.
+ png_read_end(read_ptr, info_ptr);
- return outputImage;
+ return output_image;
}
/**
- * Experimentally chosen constant to be added to the overhead of using color type
- * PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette chunk.
- * Without this, many small PNGs encoded with palettes are larger after compression than
+ * Experimentally chosen constant to be added to the overhead of using color
+ * type
+ * PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette
+ * chunk.
+ * Without this, many small PNGs encoded with palettes are larger after
+ * compression than
* the same PNGs encoded as RGBA.
*/
constexpr static const size_t kPaletteOverheadConstant = 1024u * 10u;
-// Pick a color type by which to encode the image, based on which color type will take
+// Pick a color type by which to encode the image, based on which color type
+// will take
// the least amount of disk space.
//
// 9-patch images traditionally have not been encoded with palettes.
@@ -298,427 +315,453 @@
// - Grayscale + cheap alpha
// - Grayscale + alpha
//
-static int pickColorType(int32_t width, int32_t height,
- bool grayScale, bool convertibleToGrayScale, bool hasNinePatch,
- size_t colorPaletteSize, size_t alphaPaletteSize) {
- const size_t paletteChunkSize = 16 + colorPaletteSize * 3;
- const size_t alphaChunkSize = 16 + alphaPaletteSize;
- const size_t colorAlphaDataChunkSize = 16 + 4 * width * height;
- const size_t colorDataChunkSize = 16 + 3 * width * height;
- const size_t grayScaleAlphaDataChunkSize = 16 + 2 * width * height;
- const size_t paletteDataChunkSize = 16 + width * height;
+static int PickColorType(int32_t width, int32_t height, bool grayscale,
+ bool convertible_to_grayscale, bool has_nine_patch,
+ size_t color_palette_size, size_t alpha_palette_size) {
+ const size_t palette_chunk_size = 16 + color_palette_size * 3;
+ const size_t alpha_chunk_size = 16 + alpha_palette_size;
+ const size_t color_alpha_data_chunk_size = 16 + 4 * width * height;
+ const size_t color_data_chunk_size = 16 + 3 * width * height;
+ const size_t grayscale_alpha_data_chunk_size = 16 + 2 * width * height;
+ const size_t palette_data_chunk_size = 16 + width * height;
- if (grayScale) {
- if (alphaPaletteSize == 0) {
- // This is the smallest the data can be.
- return PNG_COLOR_TYPE_GRAY;
- } else if (colorPaletteSize <= 256 && !hasNinePatch) {
- // This grayscale has alpha and can fit within a palette.
- // See if it is worth fitting into a palette.
- const size_t paletteThreshold = paletteChunkSize + alphaChunkSize +
- paletteDataChunkSize + kPaletteOverheadConstant;
- if (grayScaleAlphaDataChunkSize > paletteThreshold) {
- return PNG_COLOR_TYPE_PALETTE;
- }
- }
- return PNG_COLOR_TYPE_GRAY_ALPHA;
+ if (grayscale) {
+ if (alpha_palette_size == 0) {
+ // This is the smallest the data can be.
+ return PNG_COLOR_TYPE_GRAY;
+ } else if (color_palette_size <= 256 && !has_nine_patch) {
+ // This grayscale has alpha and can fit within a palette.
+ // See if it is worth fitting into a palette.
+ const size_t palette_threshold = palette_chunk_size + alpha_chunk_size +
+ palette_data_chunk_size +
+ kPaletteOverheadConstant;
+ if (grayscale_alpha_data_chunk_size > palette_threshold) {
+ return PNG_COLOR_TYPE_PALETTE;
+ }
+ }
+ return PNG_COLOR_TYPE_GRAY_ALPHA;
+ }
+
+ if (color_palette_size <= 256 && !has_nine_patch) {
+ // This image can fit inside a palette. Let's see if it is worth it.
+ size_t total_size_with_palette =
+ palette_data_chunk_size + palette_chunk_size;
+ size_t total_size_without_palette = color_data_chunk_size;
+ if (alpha_palette_size > 0) {
+ total_size_with_palette += alpha_palette_size;
+ total_size_without_palette = color_alpha_data_chunk_size;
}
-
- if (colorPaletteSize <= 256 && !hasNinePatch) {
- // This image can fit inside a palette. Let's see if it is worth it.
- size_t totalSizeWithPalette = paletteDataChunkSize + paletteChunkSize;
- size_t totalSizeWithoutPalette = colorDataChunkSize;
- if (alphaPaletteSize > 0) {
- totalSizeWithPalette += alphaPaletteSize;
- totalSizeWithoutPalette = colorAlphaDataChunkSize;
- }
-
- if (totalSizeWithoutPalette > totalSizeWithPalette + kPaletteOverheadConstant) {
- return PNG_COLOR_TYPE_PALETTE;
- }
+ if (total_size_without_palette >
+ total_size_with_palette + kPaletteOverheadConstant) {
+ return PNG_COLOR_TYPE_PALETTE;
}
+ }
- if (convertibleToGrayScale) {
- if (alphaPaletteSize == 0) {
- return PNG_COLOR_TYPE_GRAY;
- } else {
- return PNG_COLOR_TYPE_GRAY_ALPHA;
- }
+ if (convertible_to_grayscale) {
+ if (alpha_palette_size == 0) {
+ return PNG_COLOR_TYPE_GRAY;
+ } else {
+ return PNG_COLOR_TYPE_GRAY_ALPHA;
}
+ }
- if (alphaPaletteSize == 0) {
- return PNG_COLOR_TYPE_RGB;
- }
- return PNG_COLOR_TYPE_RGBA;
+ if (alpha_palette_size == 0) {
+ return PNG_COLOR_TYPE_RGB;
+ }
+ return PNG_COLOR_TYPE_RGBA;
}
-// Assigns indices to the color and alpha palettes, encodes them, and then invokes
+// Assigns indices to the color and alpha palettes, encodes them, and then
+// invokes
// png_set_PLTE/png_set_tRNS.
// This must be done before writing image data.
-// Image data must be transformed to use the indices assigned within the palette.
-static void writePalette(png_structp writePtr, png_infop writeInfoPtr,
- std::unordered_map<uint32_t, int>* colorPalette,
- std::unordered_set<uint32_t>* alphaPalette) {
- assert(colorPalette->size() <= 256);
- assert(alphaPalette->size() <= 256);
+// Image data must be transformed to use the indices assigned within the
+// palette.
+static void WritePalette(png_structp write_ptr, png_infop write_info_ptr,
+ std::unordered_map<uint32_t, int>* color_palette,
+ std::unordered_set<uint32_t>* alpha_palette) {
+ CHECK(color_palette->size() <= 256);
+ CHECK(alpha_palette->size() <= 256);
- // Populate the PNG palette struct and assign indices to the color
- // palette.
+ // Populate the PNG palette struct and assign indices to the color
+ // palette.
- // Colors in the alpha palette should have smaller indices.
- // This will ensure that we can truncate the alpha palette if it is
- // smaller than the color palette.
- int index = 0;
- for (uint32_t color : *alphaPalette) {
- (*colorPalette)[color] = index++;
+ // Colors in the alpha palette should have smaller indices.
+ // This will ensure that we can truncate the alpha palette if it is
+ // smaller than the color palette.
+ int index = 0;
+ for (uint32_t color : *alpha_palette) {
+ (*color_palette)[color] = index++;
+ }
+
+ // Assign the rest of the entries.
+ for (auto& entry : *color_palette) {
+ if (entry.second == -1) {
+ entry.second = index++;
}
+ }
- // Assign the rest of the entries.
- for (auto& entry : *colorPalette) {
- if (entry.second == -1) {
- entry.second = index++;
- }
+ // Create the PNG color palette struct.
+ auto color_palette_bytes =
+ std::unique_ptr<png_color[]>(new png_color[color_palette->size()]);
+
+ std::unique_ptr<png_byte[]> alpha_palette_bytes;
+ if (!alpha_palette->empty()) {
+ alpha_palette_bytes =
+ std::unique_ptr<png_byte[]>(new png_byte[alpha_palette->size()]);
+ }
+
+ for (const auto& entry : *color_palette) {
+ const uint32_t color = entry.first;
+ const int index = entry.second;
+ CHECK(index >= 0);
+ CHECK(static_cast<size_t>(index) < color_palette->size());
+
+ png_colorp slot = color_palette_bytes.get() + index;
+ slot->red = color >> 24;
+ slot->green = color >> 16;
+ slot->blue = color >> 8;
+
+ const png_byte alpha = color & 0x000000ff;
+ if (alpha != 0xff && alpha_palette_bytes) {
+ CHECK(static_cast<size_t>(index) < alpha_palette->size());
+ alpha_palette_bytes[index] = alpha;
}
+ }
- // Create the PNG color palette struct.
- auto colorPaletteBytes = std::unique_ptr<png_color[]>(new png_color[colorPalette->size()]);
+ // The bytes get copied here, so it is safe to release color_palette_bytes at
+ // the end of function
+ // scope.
+ png_set_PLTE(write_ptr, write_info_ptr, color_palette_bytes.get(),
+ color_palette->size());
- std::unique_ptr<png_byte[]> alphaPaletteBytes;
- if (!alphaPalette->empty()) {
- alphaPaletteBytes = std::unique_ptr<png_byte[]>(new png_byte[alphaPalette->size()]);
- }
-
- for (const auto& entry : *colorPalette) {
- const uint32_t color = entry.first;
- const int index = entry.second;
- assert(index >= 0);
- assert(static_cast<size_t>(index) < colorPalette->size());
-
- png_colorp slot = colorPaletteBytes.get() + index;
- slot->red = color >> 24;
- slot->green = color >> 16;
- slot->blue = color >> 8;
-
- const png_byte alpha = color & 0x000000ff;
- if (alpha != 0xff && alphaPaletteBytes) {
- assert(static_cast<size_t>(index) < alphaPalette->size());
- alphaPaletteBytes[index] = alpha;
- }
- }
-
- // The bytes get copied here, so it is safe to release colorPaletteBytes at the end of function
- // scope.
- png_set_PLTE(writePtr, writeInfoPtr, colorPaletteBytes.get(), colorPalette->size());
-
- if (alphaPaletteBytes) {
- png_set_tRNS(writePtr, writeInfoPtr, alphaPaletteBytes.get(), alphaPalette->size(),
- nullptr);
- }
+ if (alpha_palette_bytes) {
+ png_set_tRNS(write_ptr, write_info_ptr, alpha_palette_bytes.get(),
+ alpha_palette->size(), nullptr);
+ }
}
-// Write the 9-patch custom PNG chunks to writeInfoPtr. This must be done before
+// Write the 9-patch custom PNG chunks to write_info_ptr. This must be done
+// before
// writing image data.
-static void writeNinePatch(png_structp writePtr, png_infop writeInfoPtr,
- const NinePatch* ninePatch) {
- // The order of the chunks is important.
- // 9-patch code in older platforms expects the 9-patch chunk to
- // be last.
+static void WriteNinePatch(png_structp write_ptr, png_infop write_info_ptr,
+ const NinePatch* nine_patch) {
+ // The order of the chunks is important.
+ // 9-patch code in older platforms expects the 9-patch chunk to
+ // be last.
- png_unknown_chunk unknownChunks[3];
- memset(unknownChunks, 0, sizeof(unknownChunks));
+ png_unknown_chunk unknown_chunks[3];
+ memset(unknown_chunks, 0, sizeof(unknown_chunks));
- size_t index = 0;
- size_t chunkLen = 0;
+ size_t index = 0;
+ size_t chunk_len = 0;
- std::unique_ptr<uint8_t[]> serializedOutline =
- ninePatch->serializeRoundedRectOutline(&chunkLen);
- strcpy((char*) unknownChunks[index].name, "npOl");
- unknownChunks[index].size = chunkLen;
- unknownChunks[index].data = (png_bytep) serializedOutline.get();
- unknownChunks[index].location = PNG_HAVE_PLTE;
+ std::unique_ptr<uint8_t[]> serialized_outline =
+ nine_patch->SerializeRoundedRectOutline(&chunk_len);
+ strcpy((char*)unknown_chunks[index].name, "npOl");
+ unknown_chunks[index].size = chunk_len;
+ unknown_chunks[index].data = (png_bytep)serialized_outline.get();
+ unknown_chunks[index].location = PNG_HAVE_PLTE;
+ index++;
+
+ std::unique_ptr<uint8_t[]> serialized_layout_bounds;
+ if (nine_patch->layout_bounds.nonZero()) {
+ serialized_layout_bounds = nine_patch->SerializeLayoutBounds(&chunk_len);
+ strcpy((char*)unknown_chunks[index].name, "npLb");
+ unknown_chunks[index].size = chunk_len;
+ unknown_chunks[index].data = (png_bytep)serialized_layout_bounds.get();
+ unknown_chunks[index].location = PNG_HAVE_PLTE;
index++;
+ }
- std::unique_ptr<uint8_t[]> serializedLayoutBounds;
- if (ninePatch->layoutBounds.nonZero()) {
- serializedLayoutBounds = ninePatch->serializeLayoutBounds(&chunkLen);
- strcpy((char*) unknownChunks[index].name, "npLb");
- unknownChunks[index].size = chunkLen;
- unknownChunks[index].data = (png_bytep) serializedLayoutBounds.get();
- unknownChunks[index].location = PNG_HAVE_PLTE;
- index++;
- }
+ std::unique_ptr<uint8_t[]> serialized_nine_patch =
+ nine_patch->SerializeBase(&chunk_len);
+ strcpy((char*)unknown_chunks[index].name, "npTc");
+ unknown_chunks[index].size = chunk_len;
+ unknown_chunks[index].data = (png_bytep)serialized_nine_patch.get();
+ unknown_chunks[index].location = PNG_HAVE_PLTE;
+ index++;
- std::unique_ptr<uint8_t[]> serializedNinePatch = ninePatch->serializeBase(&chunkLen);
- strcpy((char*) unknownChunks[index].name, "npTc");
- unknownChunks[index].size = chunkLen;
- unknownChunks[index].data = (png_bytep) serializedNinePatch.get();
- unknownChunks[index].location = PNG_HAVE_PLTE;
- index++;
+ // Handle all unknown chunks. We are manually setting the chunks here,
+ // so we will only ever handle our custom chunks.
+ png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
- // Handle all unknown chunks. We are manually setting the chunks here,
- // so we will only ever handle our custom chunks.
- png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
-
- // Set the actual chunks here. The data gets copied, so our buffers can
- // safely go out of scope.
- png_set_unknown_chunks(writePtr, writeInfoPtr, unknownChunks, index);
+ // Set the actual chunks here. The data gets copied, so our buffers can
+ // safely go out of scope.
+ png_set_unknown_chunks(write_ptr, write_info_ptr, unknown_chunks, index);
}
-bool writePng(IAaptContext* context, const Image* image, const NinePatch* ninePatch,
- io::OutputStream* out, const PngOptions& options) {
- // Create and initialize the write png_struct with the default error and warning handlers.
- // The header version is also passed in to ensure that this was built against the same
- // version of libpng.
- png_structp writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
- nullptr, nullptr, nullptr);
- if (writePtr == nullptr) {
- context->getDiagnostics()->error(DiagMessage()
- << "failed to create libpng write png_struct");
- return false;
+bool WritePng(IAaptContext* context, const Image* image,
+ const NinePatch* nine_patch, io::OutputStream* out,
+ const PngOptions& options) {
+ // Create and initialize the write png_struct with the default error and
+ // warning handlers.
+ // The header version is also passed in to ensure that this was built against
+ // the same
+ // version of libpng.
+ png_structp write_ptr =
+ png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ if (write_ptr == nullptr) {
+ context->GetDiagnostics()->Error(
+ DiagMessage() << "failed to create libpng write png_struct");
+ return false;
+ }
+
+ // Allocate memory to store image header data.
+ png_infop write_info_ptr = png_create_info_struct(write_ptr);
+ if (write_info_ptr == nullptr) {
+ context->GetDiagnostics()->Error(
+ DiagMessage() << "failed to create libpng write png_info");
+ png_destroy_write_struct(&write_ptr, nullptr);
+ return false;
+ }
+
+ // Automatically release PNG resources at end of scope.
+ PngWriteStructDeleter png_write_deleter(write_ptr, write_info_ptr);
+
+ // libpng uses longjmp to jump to error handling routines.
+ // setjmp will return true only if it was jumped to, aka, there was an error.
+ if (setjmp(png_jmpbuf(write_ptr))) {
+ return false;
+ }
+
+ // Handle warnings with our IDiagnostics.
+ png_set_error_fn(write_ptr, (png_voidp)context->GetDiagnostics(), LogError,
+ LogWarning);
+
+ // Set up the write functions which write to our custom data sources.
+ png_set_write_fn(write_ptr, (png_voidp)out, WriteDataToStream, nullptr);
+
+ // We want small files and can take the performance hit to achieve this goal.
+ png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
+
+ // Begin analysis of the image data.
+ // Scan the entire image and determine if:
+ // 1. Every pixel has R == G == B (grayscale)
+ // 2. Every pixel has A == 255 (opaque)
+ // 3. There are no more than 256 distinct RGBA colors (palette).
+ std::unordered_map<uint32_t, int> color_palette;
+ std::unordered_set<uint32_t> alpha_palette;
+ bool needs_to_zero_rgb_channels_of_transparent_pixels = false;
+ bool grayscale = true;
+ int max_gray_deviation = 0;
+
+ for (int32_t y = 0; y < image->height; y++) {
+ const uint8_t* row = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int red = *row++;
+ int green = *row++;
+ int blue = *row++;
+ int alpha = *row++;
+
+ if (alpha == 0) {
+ // The color is completely transparent.
+ // For purposes of palettes and grayscale optimization,
+ // treat all channels as 0x00.
+ needs_to_zero_rgb_channels_of_transparent_pixels =
+ needs_to_zero_rgb_channels_of_transparent_pixels ||
+ (red != 0 || green != 0 || blue != 0);
+ red = green = blue = 0;
+ }
+
+ // Insert the color into the color palette.
+ const uint32_t color = red << 24 | green << 16 | blue << 8 | alpha;
+ color_palette[color] = -1;
+
+ // If the pixel has non-opaque alpha, insert it into the
+ // alpha palette.
+ if (alpha != 0xff) {
+ alpha_palette.insert(color);
+ }
+
+ // Check if the image is indeed grayscale.
+ if (grayscale) {
+ if (red != green || red != blue) {
+ grayscale = false;
+ }
+ }
+
+ // Calculate the gray scale deviation so that it can be compared
+ // with the threshold.
+ max_gray_deviation = std::max(std::abs(red - green), max_gray_deviation);
+ max_gray_deviation = std::max(std::abs(green - blue), max_gray_deviation);
+ max_gray_deviation = std::max(std::abs(blue - red), max_gray_deviation);
}
+ }
- // Allocate memory to store image header data.
- png_infop writeInfoPtr = png_create_info_struct(writePtr);
- if (writeInfoPtr == nullptr) {
- context->getDiagnostics()->error(DiagMessage() << "failed to create libpng write png_info");
- png_destroy_write_struct(&writePtr, nullptr);
- return false;
+ if (context->IsVerbose()) {
+ DiagMessage msg;
+ msg << " paletteSize=" << color_palette.size()
+ << " alphaPaletteSize=" << alpha_palette.size()
+ << " maxGrayDeviation=" << max_gray_deviation
+ << " grayScale=" << (grayscale ? "true" : "false");
+ context->GetDiagnostics()->Note(msg);
+ }
+
+ const bool convertible_to_grayscale =
+ max_gray_deviation <= options.grayscale_tolerance;
+
+ const int new_color_type = PickColorType(
+ image->width, image->height, grayscale, convertible_to_grayscale,
+ nine_patch != nullptr, color_palette.size(), alpha_palette.size());
+
+ if (context->IsVerbose()) {
+ DiagMessage msg;
+ msg << "encoding PNG ";
+ if (nine_patch) {
+ msg << "(with 9-patch) as ";
}
-
- // Automatically release PNG resources at end of scope.
- PngWriteStructDeleter pngWriteDeleter(writePtr, writeInfoPtr);
-
- // libpng uses longjmp to jump to error handling routines.
- // setjmp will return true only if it was jumped to, aka, there was an error.
- if (setjmp(png_jmpbuf(writePtr))) {
- return false;
+ switch (new_color_type) {
+ case PNG_COLOR_TYPE_GRAY:
+ msg << "GRAY";
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ msg << "GRAY + ALPHA";
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ msg << "RGB";
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ msg << "RGBA";
+ break;
+ case PNG_COLOR_TYPE_PALETTE:
+ msg << "PALETTE";
+ break;
+ default:
+ msg << "unknown type " << new_color_type;
+ break;
}
+ context->GetDiagnostics()->Note(msg);
+ }
- // Handle warnings with our IDiagnostics.
- png_set_error_fn(writePtr, (png_voidp) context->getDiagnostics(), logError, logWarning);
+ png_set_IHDR(write_ptr, write_info_ptr, image->width, image->height, 8,
+ new_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
- // Set up the write functions which write to our custom data sources.
- png_set_write_fn(writePtr, (png_voidp) out, writeDataToStream, nullptr);
+ if (new_color_type & PNG_COLOR_MASK_PALETTE) {
+ // Assigns indices to the palette, and writes the encoded palette to the
+ // libpng writePtr.
+ WritePalette(write_ptr, write_info_ptr, &color_palette, &alpha_palette);
+ png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
+ } else {
+ png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
+ }
- // We want small files and can take the performance hit to achieve this goal.
- png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
+ if (nine_patch) {
+ WriteNinePatch(write_ptr, write_info_ptr, nine_patch);
+ }
- // Begin analysis of the image data.
- // Scan the entire image and determine if:
- // 1. Every pixel has R == G == B (grayscale)
- // 2. Every pixel has A == 255 (opaque)
- // 3. There are no more than 256 distinct RGBA colors (palette).
- std::unordered_map<uint32_t, int> colorPalette;
- std::unordered_set<uint32_t> alphaPalette;
- bool needsToZeroRGBChannelsOfTransparentPixels = false;
- bool grayScale = true;
- int maxGrayDeviation = 0;
+ // Flush our updates to the header.
+ png_write_info(write_ptr, write_info_ptr);
+
+ // Write out each row of image data according to its encoding.
+ if (new_color_type == PNG_COLOR_TYPE_PALETTE) {
+ // 1 byte/pixel.
+ auto out_row = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
for (int32_t y = 0; y < image->height; y++) {
- const uint8_t* row = image->rows[y];
- for (int32_t x = 0; x < image->width; x++) {
- int red = *row++;
- int green = *row++;
- int blue = *row++;
- int alpha = *row++;
-
- if (alpha == 0) {
- // The color is completely transparent.
- // For purposes of palettes and grayscale optimization,
- // treat all channels as 0x00.
- needsToZeroRGBChannelsOfTransparentPixels =
- needsToZeroRGBChannelsOfTransparentPixels ||
- (red != 0 || green != 0 || blue != 0);
- red = green = blue = 0;
- }
-
- // Insert the color into the color palette.
- const uint32_t color = red << 24 | green << 16 | blue << 8 | alpha;
- colorPalette[color] = -1;
-
- // If the pixel has non-opaque alpha, insert it into the
- // alpha palette.
- if (alpha != 0xff) {
- alphaPalette.insert(color);
- }
-
- // Check if the image is indeed grayscale.
- if (grayScale) {
- if (red != green || red != blue) {
- grayScale = false;
- }
- }
-
- // Calculate the gray scale deviation so that it can be compared
- // with the threshold.
- maxGrayDeviation = std::max(std::abs(red - green), maxGrayDeviation);
- maxGrayDeviation = std::max(std::abs(green - blue), maxGrayDeviation);
- maxGrayDeviation = std::max(std::abs(blue - red), maxGrayDeviation);
+ png_const_bytep in_row = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = *in_row++;
+ int gg = *in_row++;
+ int bb = *in_row++;
+ int aa = *in_row++;
+ if (aa == 0) {
+ // Zero out color channels when transparent.
+ rr = gg = bb = 0;
}
+
+ const uint32_t color = rr << 24 | gg << 16 | bb << 8 | aa;
+ const int idx = color_palette[color];
+ CHECK(idx != -1);
+ out_row[x] = static_cast<png_byte>(idx);
+ }
+ png_write_row(write_ptr, out_row.get());
}
+ } else if (new_color_type == PNG_COLOR_TYPE_GRAY ||
+ new_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ const size_t bpp = new_color_type == PNG_COLOR_TYPE_GRAY ? 1 : 2;
+ auto out_row =
+ std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
- if (context->verbose()) {
- DiagMessage msg;
- msg << " paletteSize=" << colorPalette.size()
- << " alphaPaletteSize=" << alphaPalette.size()
- << " maxGrayDeviation=" << maxGrayDeviation
- << " grayScale=" << (grayScale ? "true" : "false");
- context->getDiagnostics()->note(msg);
- }
-
- const bool convertibleToGrayScale = maxGrayDeviation <= options.grayScaleTolerance;
-
- const int newColorType = pickColorType(image->width, image->height, grayScale,
- convertibleToGrayScale, ninePatch != nullptr,
- colorPalette.size(), alphaPalette.size());
-
- if (context->verbose()) {
- DiagMessage msg;
- msg << "encoding PNG ";
- if (ninePatch) {
- msg << "(with 9-patch) as ";
+ for (int32_t y = 0; y < image->height; y++) {
+ png_const_bytep in_row = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = in_row[x * 4];
+ int gg = in_row[x * 4 + 1];
+ int bb = in_row[x * 4 + 2];
+ int aa = in_row[x * 4 + 3];
+ if (aa == 0) {
+ // Zero out the gray channel when transparent.
+ rr = gg = bb = 0;
}
- switch (newColorType) {
- case PNG_COLOR_TYPE_GRAY:
- msg << "GRAY";
- break;
- case PNG_COLOR_TYPE_GRAY_ALPHA:
- msg << "GRAY + ALPHA";
- break;
- case PNG_COLOR_TYPE_RGB:
- msg << "RGB";
- break;
- case PNG_COLOR_TYPE_RGB_ALPHA:
- msg << "RGBA";
- break;
- case PNG_COLOR_TYPE_PALETTE:
- msg << "PALETTE";
- break;
- default:
- msg << "unknown type " << newColorType;
- break;
- }
- context->getDiagnostics()->note(msg);
- }
- png_set_IHDR(writePtr, writeInfoPtr, image->width, image->height, 8, newColorType,
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
-
- if (newColorType & PNG_COLOR_MASK_PALETTE) {
- // Assigns indices to the palette, and writes the encoded palette to the libpng writePtr.
- writePalette(writePtr, writeInfoPtr, &colorPalette, &alphaPalette);
- png_set_filter(writePtr, 0, PNG_NO_FILTERS);
- } else {
- png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
- }
-
- if (ninePatch) {
- writeNinePatch(writePtr, writeInfoPtr, ninePatch);
- }
-
- // Flush our updates to the header.
- png_write_info(writePtr, writeInfoPtr);
-
- // Write out each row of image data according to its encoding.
- if (newColorType == PNG_COLOR_TYPE_PALETTE) {
- // 1 byte/pixel.
- auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
-
- for (int32_t y = 0; y < image->height; y++) {
- png_const_bytep inRow = image->rows[y];
- for (int32_t x = 0; x < image->width; x++) {
- int rr = *inRow++;
- int gg = *inRow++;
- int bb = *inRow++;
- int aa = *inRow++;
- if (aa == 0) {
- // Zero out color channels when transparent.
- rr = gg = bb = 0;
- }
-
- const uint32_t color = rr << 24 | gg << 16 | bb << 8 | aa;
- const int idx = colorPalette[color];
- assert(idx != -1);
- outRow[x] = static_cast<png_byte>(idx);
- }
- png_write_row(writePtr, outRow.get());
- }
- } else if (newColorType == PNG_COLOR_TYPE_GRAY || newColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
- const size_t bpp = newColorType == PNG_COLOR_TYPE_GRAY ? 1 : 2;
- auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
-
- for (int32_t y = 0; y < image->height; y++) {
- png_const_bytep inRow = image->rows[y];
- for (int32_t x = 0; x < image->width; x++) {
- int rr = inRow[x * 4];
- int gg = inRow[x * 4 + 1];
- int bb = inRow[x * 4 + 2];
- int aa = inRow[x * 4 + 3];
- if (aa == 0) {
- // Zero out the gray channel when transparent.
- rr = gg = bb = 0;
- }
-
- if (grayScale) {
- // The image was already grayscale, red == green == blue.
- outRow[x * bpp] = inRow[x * 4];
- } else {
- // The image is convertible to grayscale, use linear-luminance of
- // sRGB colorspace: https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
- outRow[x * bpp] = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
- }
-
- if (bpp == 2) {
- // Write out alpha if we have it.
- outRow[x * bpp + 1] = aa;
- }
- }
- png_write_row(writePtr, outRow.get());
- }
- } else if (newColorType == PNG_COLOR_TYPE_RGB || newColorType == PNG_COLOR_TYPE_RGBA) {
- const size_t bpp = newColorType == PNG_COLOR_TYPE_RGB ? 3 : 4;
- if (needsToZeroRGBChannelsOfTransparentPixels) {
- // The source RGBA data can't be used as-is, because we need to zero out the RGB
- // values of transparent pixels.
- auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
-
- for (int32_t y = 0; y < image->height; y++) {
- png_const_bytep inRow = image->rows[y];
- for (int32_t x = 0; x < image->width; x++) {
- int rr = *inRow++;
- int gg = *inRow++;
- int bb = *inRow++;
- int aa = *inRow++;
- if (aa == 0) {
- // Zero out the RGB channels when transparent.
- rr = gg = bb = 0;
- }
- outRow[x * bpp] = rr;
- outRow[x * bpp + 1] = gg;
- outRow[x * bpp + 2] = bb;
- if (bpp == 4) {
- outRow[x * bpp + 3] = aa;
- }
- }
- png_write_row(writePtr, outRow.get());
- }
+ if (grayscale) {
+ // The image was already grayscale, red == green == blue.
+ out_row[x * bpp] = in_row[x * 4];
} else {
- // The source image can be used as-is, just tell libpng whether or not to ignore
- // the alpha channel.
- if (newColorType == PNG_COLOR_TYPE_RGB) {
- // Delete the extraneous alpha values that we appended to our buffer
- // when reading the original values.
- png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
- }
- png_write_image(writePtr, image->rows.get());
+ // The image is convertible to grayscale, use linear-luminance of
+ // sRGB colorspace:
+ // https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
+ out_row[x * bpp] =
+ (png_byte)(rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
}
- } else {
- assert(false && "unreachable");
- }
- png_write_end(writePtr, writeInfoPtr);
- return true;
+ if (bpp == 2) {
+ // Write out alpha if we have it.
+ out_row[x * bpp + 1] = aa;
+ }
+ }
+ png_write_row(write_ptr, out_row.get());
+ }
+ } else if (new_color_type == PNG_COLOR_TYPE_RGB ||
+ new_color_type == PNG_COLOR_TYPE_RGBA) {
+ const size_t bpp = new_color_type == PNG_COLOR_TYPE_RGB ? 3 : 4;
+ if (needs_to_zero_rgb_channels_of_transparent_pixels) {
+ // The source RGBA data can't be used as-is, because we need to zero out
+ // the RGB
+ // values of transparent pixels.
+ auto out_row =
+ std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
+
+ for (int32_t y = 0; y < image->height; y++) {
+ png_const_bytep in_row = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = *in_row++;
+ int gg = *in_row++;
+ int bb = *in_row++;
+ int aa = *in_row++;
+ if (aa == 0) {
+ // Zero out the RGB channels when transparent.
+ rr = gg = bb = 0;
+ }
+ out_row[x * bpp] = rr;
+ out_row[x * bpp + 1] = gg;
+ out_row[x * bpp + 2] = bb;
+ if (bpp == 4) {
+ out_row[x * bpp + 3] = aa;
+ }
+ }
+ png_write_row(write_ptr, out_row.get());
+ }
+ } else {
+ // The source image can be used as-is, just tell libpng whether or not to
+ // ignore
+ // the alpha channel.
+ if (new_color_type == PNG_COLOR_TYPE_RGB) {
+ // Delete the extraneous alpha values that we appended to our buffer
+ // when reading the original values.
+ png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
+ }
+ png_write_image(write_ptr, image->rows.get());
+ }
+ } else {
+ LOG(FATAL) << "unreachable";
+ }
+
+ png_write_end(write_ptr, write_info_ptr);
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 732101f..055a725 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -14,235 +14,249 @@
* limitations under the License.
*/
-#include "ResourceTable.h"
-#include "ResourceValues.h"
-#include "ValueVisitor.h"
#include "compile/PseudolocaleGenerator.h"
-#include "compile/Pseudolocalizer.h"
#include <algorithm>
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
+#include "compile/Pseudolocalizer.h"
+
namespace aapt {
-std::unique_ptr<StyledString> pseudolocalizeStyledString(StyledString* string,
- Pseudolocalizer::Method method,
- StringPool* pool) {
- Pseudolocalizer localizer(method);
+std::unique_ptr<StyledString> PseudolocalizeStyledString(
+ StyledString* string, Pseudolocalizer::Method method, StringPool* pool) {
+ Pseudolocalizer localizer(method);
- const StringPiece originalText = *string->value->str;
+ const StringPiece original_text = *string->value->str;
- StyleString localized;
+ StyleString localized;
- // Copy the spans. We will update their offsets when we localize.
- localized.spans.reserve(string->value->spans.size());
- for (const StringPool::Span& span : string->value->spans) {
- localized.spans.push_back(Span{ *span.name, span.firstChar, span.lastChar });
+ // Copy the spans. We will update their offsets when we localize.
+ localized.spans.reserve(string->value->spans.size());
+ for (const StringPool::Span& span : string->value->spans) {
+ localized.spans.push_back(
+ Span{*span.name, span.first_char, span.last_char});
+ }
+
+ // The ranges are all represented with a single value. This is the start of
+ // one range and
+ // end of another.
+ struct Range {
+ size_t start;
+
+ // Once the new string is localized, these are the pointers to the spans to
+ // adjust.
+ // Since this struct represents the start of one range and end of another,
+ // we have
+ // the two pointers respectively.
+ uint32_t* update_start;
+ uint32_t* update_end;
+ };
+
+ auto cmp = [](const Range& r, size_t index) -> bool {
+ return r.start < index;
+ };
+
+ // Construct the ranges. The ranges are represented like so: [0, 2, 5, 7]
+ // The ranges are the spaces in between. In this example, with a total string
+ // length of 9,
+ // the vector represents: (0,1], (2,4], (5,6], (7,9]
+ //
+ std::vector<Range> ranges;
+ ranges.push_back(Range{0});
+ ranges.push_back(Range{original_text.size() - 1});
+ for (size_t i = 0; i < string->value->spans.size(); i++) {
+ const StringPool::Span& span = string->value->spans[i];
+
+ // Insert or update the Range marker for the start of this span.
+ auto iter =
+ std::lower_bound(ranges.begin(), ranges.end(), span.first_char, cmp);
+ if (iter != ranges.end() && iter->start == span.first_char) {
+ iter->update_start = &localized.spans[i].first_char;
+ } else {
+ ranges.insert(iter, Range{span.first_char, &localized.spans[i].first_char,
+ nullptr});
}
- // The ranges are all represented with a single value. This is the start of one range and
- // end of another.
- struct Range {
- size_t start;
+ // Insert or update the Range marker for the end of this span.
+ iter = std::lower_bound(ranges.begin(), ranges.end(), span.last_char, cmp);
+ if (iter != ranges.end() && iter->start == span.last_char) {
+ iter->update_end = &localized.spans[i].last_char;
+ } else {
+ ranges.insert(
+ iter, Range{span.last_char, nullptr, &localized.spans[i].last_char});
+ }
+ }
- // Once the new string is localized, these are the pointers to the spans to adjust.
- // Since this struct represents the start of one range and end of another, we have
- // the two pointers respectively.
- uint32_t* updateStart;
- uint32_t* updateEnd;
- };
+ localized.str += localizer.Start();
- auto cmp = [](const Range& r, size_t index) -> bool {
- return r.start < index;
- };
-
- // Construct the ranges. The ranges are represented like so: [0, 2, 5, 7]
- // The ranges are the spaces in between. In this example, with a total string length of 9,
- // the vector represents: (0,1], (2,4], (5,6], (7,9]
- //
- std::vector<Range> ranges;
- ranges.push_back(Range{ 0 });
- ranges.push_back(Range{ originalText.size() - 1 });
- for (size_t i = 0; i < string->value->spans.size(); i++) {
- const StringPool::Span& span = string->value->spans[i];
-
- // Insert or update the Range marker for the start of this span.
- auto iter = std::lower_bound(ranges.begin(), ranges.end(), span.firstChar, cmp);
- if (iter != ranges.end() && iter->start == span.firstChar) {
- iter->updateStart = &localized.spans[i].firstChar;
- } else {
- ranges.insert(iter,
- Range{ span.firstChar, &localized.spans[i].firstChar, nullptr });
- }
-
- // Insert or update the Range marker for the end of this span.
- iter = std::lower_bound(ranges.begin(), ranges.end(), span.lastChar, cmp);
- if (iter != ranges.end() && iter->start == span.lastChar) {
- iter->updateEnd = &localized.spans[i].lastChar;
- } else {
- ranges.insert(iter,
- Range{ span.lastChar, nullptr, &localized.spans[i].lastChar });
- }
+ // Iterate over the ranges and localize each section.
+ for (size_t i = 0; i < ranges.size(); i++) {
+ const size_t start = ranges[i].start;
+ size_t len = original_text.size() - start;
+ if (i + 1 < ranges.size()) {
+ len = ranges[i + 1].start - start;
}
- localized.str += localizer.start();
-
- // Iterate over the ranges and localize each section.
- for (size_t i = 0; i < ranges.size(); i++) {
- const size_t start = ranges[i].start;
- size_t len = originalText.size() - start;
- if (i + 1 < ranges.size()) {
- len = ranges[i + 1].start - start;
- }
-
- if (ranges[i].updateStart) {
- *ranges[i].updateStart = localized.str.size();
- }
-
- if (ranges[i].updateEnd) {
- *ranges[i].updateEnd = localized.str.size();
- }
-
- localized.str += localizer.text(originalText.substr(start, len));
+ if (ranges[i].update_start) {
+ *ranges[i].update_start = localized.str.size();
}
- localized.str += localizer.end();
+ if (ranges[i].update_end) {
+ *ranges[i].update_end = localized.str.size();
+ }
- std::unique_ptr<StyledString> localizedString = util::make_unique<StyledString>(
- pool->makeRef(localized));
- localizedString->setSource(string->getSource());
- return localizedString;
+ localized.str += localizer.Text(original_text.substr(start, len));
+ }
+
+ localized.str += localizer.End();
+
+ std::unique_ptr<StyledString> localized_string =
+ util::make_unique<StyledString>(pool->MakeRef(localized));
+ localized_string->SetSource(string->GetSource());
+ return localized_string;
}
namespace {
-struct Visitor : public RawValueVisitor {
- StringPool* mPool;
- Pseudolocalizer::Method mMethod;
- Pseudolocalizer mLocalizer;
+class Visitor : public RawValueVisitor {
+ public:
+ // Either value or item will be populated upon visiting the value.
+ std::unique_ptr<Value> value;
+ std::unique_ptr<Item> item;
- // Either value or item will be populated upon visiting the value.
- std::unique_ptr<Value> mValue;
- std::unique_ptr<Item> mItem;
+ Visitor(StringPool* pool, Pseudolocalizer::Method method)
+ : pool_(pool), method_(method), localizer_(method) {}
- Visitor(StringPool* pool, Pseudolocalizer::Method method) :
- mPool(pool), mMethod(method), mLocalizer(method) {
- }
-
- void visit(Plural* plural) override {
- std::unique_ptr<Plural> localized = util::make_unique<Plural>();
- for (size_t i = 0; i < plural->values.size(); i++) {
- Visitor subVisitor(mPool, mMethod);
- if (plural->values[i]) {
- plural->values[i]->accept(&subVisitor);
- if (subVisitor.mValue) {
- localized->values[i] = std::move(subVisitor.mItem);
- } else {
- localized->values[i] = std::unique_ptr<Item>(plural->values[i]->clone(mPool));
- }
- }
+ void Visit(Plural* plural) override {
+ std::unique_ptr<Plural> localized = util::make_unique<Plural>();
+ for (size_t i = 0; i < plural->values.size(); i++) {
+ Visitor sub_visitor(pool_, method_);
+ if (plural->values[i]) {
+ plural->values[i]->Accept(&sub_visitor);
+ if (sub_visitor.value) {
+ localized->values[i] = std::move(sub_visitor.item);
+ } else {
+ localized->values[i] =
+ std::unique_ptr<Item>(plural->values[i]->Clone(pool_));
}
- localized->setSource(plural->getSource());
- localized->setWeak(true);
- mValue = std::move(localized);
+ }
}
+ localized->SetSource(plural->GetSource());
+ localized->SetWeak(true);
+ value = std::move(localized);
+ }
- void visit(String* string) override {
- std::string result = mLocalizer.start() + mLocalizer.text(*string->value) +
- mLocalizer.end();
- std::unique_ptr<String> localized = util::make_unique<String>(mPool->makeRef(result));
- localized->setSource(string->getSource());
- localized->setWeak(true);
- mItem = std::move(localized);
- }
+ void Visit(String* string) override {
+ std::string result =
+ localizer_.Start() + localizer_.Text(*string->value) + localizer_.End();
+ std::unique_ptr<String> localized =
+ util::make_unique<String>(pool_->MakeRef(result));
+ localized->SetSource(string->GetSource());
+ localized->SetWeak(true);
+ item = std::move(localized);
+ }
- void visit(StyledString* string) override {
- mItem = pseudolocalizeStyledString(string, mMethod, mPool);
- mItem->setWeak(true);
- }
+ void Visit(StyledString* string) override {
+ item = PseudolocalizeStyledString(string, method_, pool_);
+ item->SetWeak(true);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Visitor);
+
+ StringPool* pool_;
+ Pseudolocalizer::Method method_;
+ Pseudolocalizer localizer_;
};
-ConfigDescription modifyConfigForPseudoLocale(const ConfigDescription& base,
+ConfigDescription ModifyConfigForPseudoLocale(const ConfigDescription& base,
Pseudolocalizer::Method m) {
- ConfigDescription modified = base;
- switch (m) {
+ ConfigDescription modified = base;
+ switch (m) {
case Pseudolocalizer::Method::kAccent:
- modified.language[0] = 'e';
- modified.language[1] = 'n';
- modified.country[0] = 'X';
- modified.country[1] = 'A';
- break;
+ modified.language[0] = 'e';
+ modified.language[1] = 'n';
+ modified.country[0] = 'X';
+ modified.country[1] = 'A';
+ break;
case Pseudolocalizer::Method::kBidi:
- modified.language[0] = 'a';
- modified.language[1] = 'r';
- modified.country[0] = 'X';
- modified.country[1] = 'B';
- break;
+ modified.language[0] = 'a';
+ modified.language[1] = 'r';
+ modified.country[0] = 'X';
+ modified.country[1] = 'B';
+ break;
default:
- break;
- }
- return modified;
+ break;
+ }
+ return modified;
}
-void pseudolocalizeIfNeeded(const Pseudolocalizer::Method method,
- ResourceConfigValue* originalValue,
- StringPool* pool,
- ResourceEntry* entry) {
- Visitor visitor(pool, method);
- originalValue->value->accept(&visitor);
+void PseudolocalizeIfNeeded(const Pseudolocalizer::Method method,
+ ResourceConfigValue* original_value,
+ StringPool* pool, ResourceEntry* entry) {
+ Visitor visitor(pool, method);
+ original_value->value->Accept(&visitor);
- std::unique_ptr<Value> localizedValue;
- if (visitor.mValue) {
- localizedValue = std::move(visitor.mValue);
- } else if (visitor.mItem) {
- localizedValue = std::move(visitor.mItem);
- }
+ std::unique_ptr<Value> localized_value;
+ if (visitor.value) {
+ localized_value = std::move(visitor.value);
+ } else if (visitor.item) {
+ localized_value = std::move(visitor.item);
+ }
- if (!localizedValue) {
- return;
- }
+ if (!localized_value) {
+ return;
+ }
- ConfigDescription configWithAccent = modifyConfigForPseudoLocale(
- originalValue->config, method);
+ ConfigDescription config_with_accent =
+ ModifyConfigForPseudoLocale(original_value->config, method);
- ResourceConfigValue* newConfigValue = entry->findOrCreateValue(
- configWithAccent, originalValue->product);
- if (!newConfigValue->value) {
- // Only use auto-generated pseudo-localization if none is defined.
- newConfigValue->value = std::move(localizedValue);
- }
+ ResourceConfigValue* new_config_value =
+ entry->FindOrCreateValue(config_with_accent, original_value->product);
+ if (!new_config_value->value) {
+ // Only use auto-generated pseudo-localization if none is defined.
+ new_config_value->value = std::move(localized_value);
+ }
}
/**
- * A value is pseudolocalizable if it does not define a locale (or is the default locale)
+ * A value is pseudolocalizable if it does not define a locale (or is the
+ * default locale)
* and is translateable.
*/
-static bool isPseudolocalizable(ResourceConfigValue* configValue) {
- const int diff = configValue->config.diff(ConfigDescription::defaultConfig());
- if (diff & ConfigDescription::CONFIG_LOCALE) {
- return false;
- }
- return configValue->value->isTranslateable();
+static bool IsPseudolocalizable(ResourceConfigValue* config_value) {
+ const int diff =
+ config_value->config.diff(ConfigDescription::DefaultConfig());
+ if (diff & ConfigDescription::CONFIG_LOCALE) {
+ return false;
+ }
+ return config_value->value->IsTranslateable();
}
-} // namespace
+} // namespace
-bool PseudolocaleGenerator::consume(IAaptContext* context, ResourceTable* table) {
- for (auto& package : table->packages) {
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- std::vector<ResourceConfigValue*> values = entry->findValuesIf(isPseudolocalizable);
+bool PseudolocaleGenerator::Consume(IAaptContext* context,
+ ResourceTable* table) {
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ std::vector<ResourceConfigValue*> values =
+ entry->FindValuesIf(IsPseudolocalizable);
- for (ResourceConfigValue* value : values) {
- pseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
- &table->stringPool, entry.get());
- pseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
- &table->stringPool, entry.get());
- }
- }
+ for (ResourceConfigValue* value : values) {
+ PseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
+ &table->string_pool, entry.get());
+ PseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
+ &table->string_pool, entry.get());
}
+ }
}
- return true;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.h b/tools/aapt2/compile/PseudolocaleGenerator.h
index 4fbc516..ace3786 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.h
+++ b/tools/aapt2/compile/PseudolocaleGenerator.h
@@ -23,14 +23,13 @@
namespace aapt {
-std::unique_ptr<StyledString> pseudolocalizeStyledString(StyledString* string,
- Pseudolocalizer::Method method,
- StringPool* pool);
+std::unique_ptr<StyledString> PseudolocalizeStyledString(
+ StyledString* string, Pseudolocalizer::Method method, StringPool* pool);
struct PseudolocaleGenerator : public IResourceTableConsumer {
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_COMPILE_PSEUDOLOCALEGENERATOR_H */
diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
index 1f62f90..5a9884d 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
@@ -15,107 +15,119 @@
*/
#include "compile/PseudolocaleGenerator.h"
+
#include "test/Test.h"
#include "util/Util.h"
-#include <androidfw/ResourceTypes.h>
-
namespace aapt {
TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) {
- StringPool pool;
- StyleString originalStyle;
- originalStyle.str = "Hello world!";
- originalStyle.spans = { Span{ "b", 2, 3 }, Span{ "b", 6, 7 }, Span{ "i", 1, 10 } };
+ StringPool pool;
+ StyleString original_style;
+ original_style.str = "Hello world!";
+ original_style.spans = {Span{"b", 2, 3}, Span{"b", 6, 7}, Span{"i", 1, 10}};
- std::unique_ptr<StyledString> newString = pseudolocalizeStyledString(
- util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(),
- Pseudolocalizer::Method::kNone, &pool);
+ std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
+ util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
+ Pseudolocalizer::Method::kNone, &pool);
- EXPECT_EQ(originalStyle.str, *newString->value->str);
- ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size());
+ EXPECT_EQ(original_style.str, *new_string->value->str);
+ ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
- EXPECT_EQ(std::string("He").size(), newString->value->spans[0].firstChar);
- EXPECT_EQ(std::string("Hel").size(), newString->value->spans[0].lastChar);
- EXPECT_EQ(std::string("b"), *newString->value->spans[0].name);
+ EXPECT_EQ(std::string("He").size(), new_string->value->spans[0].first_char);
+ EXPECT_EQ(std::string("Hel").size(), new_string->value->spans[0].last_char);
+ EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
- EXPECT_EQ(std::string("Hello ").size(), newString->value->spans[1].firstChar);
- EXPECT_EQ(std::string("Hello w").size(), newString->value->spans[1].lastChar);
- EXPECT_EQ(std::string("b"), *newString->value->spans[1].name);
+ EXPECT_EQ(std::string("Hello ").size(),
+ new_string->value->spans[1].first_char);
+ EXPECT_EQ(std::string("Hello w").size(),
+ new_string->value->spans[1].last_char);
+ EXPECT_EQ(std::string("b"), *new_string->value->spans[1].name);
- EXPECT_EQ(std::string("H").size(), newString->value->spans[2].firstChar);
- EXPECT_EQ(std::string("Hello worl").size(), newString->value->spans[2].lastChar);
- EXPECT_EQ(std::string("i"), *newString->value->spans[2].name);
+ EXPECT_EQ(std::string("H").size(), new_string->value->spans[2].first_char);
+ EXPECT_EQ(std::string("Hello worl").size(),
+ new_string->value->spans[2].last_char);
+ EXPECT_EQ(std::string("i"), *new_string->value->spans[2].name);
- originalStyle.spans.push_back(Span{ "em", 0, 11u });
+ original_style.spans.push_back(Span{"em", 0, 11u});
- newString = pseudolocalizeStyledString(
- util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(),
- Pseudolocalizer::Method::kAccent, &pool);
+ new_string = PseudolocalizeStyledString(
+ util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
+ Pseudolocalizer::Method::kAccent, &pool);
- EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *newString->value->str);
- ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size());
+ EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *new_string->value->str);
+ ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
- EXPECT_EQ(std::string("[Ĥé").size(), newString->value->spans[0].firstChar);
- EXPECT_EQ(std::string("[Ĥéļ").size(), newString->value->spans[0].lastChar);
+ EXPECT_EQ(std::string("[Ĥé").size(), new_string->value->spans[0].first_char);
+ EXPECT_EQ(std::string("[Ĥéļ").size(), new_string->value->spans[0].last_char);
- EXPECT_EQ(std::string("[Ĥéļļö ").size(), newString->value->spans[1].firstChar);
- EXPECT_EQ(std::string("[Ĥéļļö ŵ").size(), newString->value->spans[1].lastChar);
+ EXPECT_EQ(std::string("[Ĥéļļö ").size(),
+ new_string->value->spans[1].first_char);
+ EXPECT_EQ(std::string("[Ĥéļļö ŵ").size(),
+ new_string->value->spans[1].last_char);
- EXPECT_EQ(std::string("[Ĥ").size(), newString->value->spans[2].firstChar);
- EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļ").size(), newString->value->spans[2].lastChar);
+ EXPECT_EQ(std::string("[Ĥ").size(), new_string->value->spans[2].first_char);
+ EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļ").size(),
+ new_string->value->spans[2].last_char);
- EXPECT_EQ(std::string("[").size(), newString->value->spans[3].firstChar);
- EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð").size(), newString->value->spans[3].lastChar);
+ EXPECT_EQ(std::string("[").size(), new_string->value->spans[3].first_char);
+ EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð").size(),
+ new_string->value->spans[3].last_char);
}
TEST(PseudolocaleGeneratorTest, PseudolocalizeOnlyDefaultConfigs) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addString("android:string/one", "one")
- .addString("android:string/two", ResourceId{}, test::parseConfigOrDie("en"), "two")
- .addString("android:string/three", "three")
- .addString("android:string/three", ResourceId{}, test::parseConfigOrDie("en-rXA"),
- "three")
- .addString("android:string/four", "four")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddString("android:string/one", "one")
+ .AddString("android:string/two", ResourceId{},
+ test::ParseConfigOrDie("en"), "two")
+ .AddString("android:string/three", "three")
+ .AddString("android:string/three", ResourceId{},
+ test::ParseConfigOrDie("en-rXA"), "three")
+ .AddString("android:string/four", "four")
+ .Build();
- String* val = test::getValue<String>(table.get(), "android:string/four");
- ASSERT_NE(nullptr, val);
- val->setTranslateable(false);
+ String* val = test::GetValue<String>(table.get(), "android:string/four");
+ ASSERT_NE(nullptr, val);
+ val->SetTranslateable(false);
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- PseudolocaleGenerator generator;
- ASSERT_TRUE(generator.consume(context.get(), table.get()));
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ PseudolocaleGenerator generator;
+ ASSERT_TRUE(generator.Consume(context.get(), table.get()));
- // Normal pseudolocalization should take place.
- ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), "android:string/one",
- test::parseConfigOrDie("en-rXA")));
- ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), "android:string/one",
- test::parseConfigOrDie("ar-rXB")));
+ // Normal pseudolocalization should take place.
+ ASSERT_NE(nullptr,
+ test::GetValueForConfig<String>(table.get(), "android:string/one",
+ test::ParseConfigOrDie("en-rXA")));
+ ASSERT_NE(nullptr,
+ test::GetValueForConfig<String>(table.get(), "android:string/one",
+ test::ParseConfigOrDie("ar-rXB")));
- // No default config for android:string/two, so no pseudlocales should exist.
- ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), "android:string/two",
- test::parseConfigOrDie("en-rXA")));
- ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), "android:string/two",
- test::parseConfigOrDie("ar-rXB")));
+ // No default config for android:string/two, so no pseudlocales should exist.
+ ASSERT_EQ(nullptr,
+ test::GetValueForConfig<String>(table.get(), "android:string/two",
+ test::ParseConfigOrDie("en-rXA")));
+ ASSERT_EQ(nullptr,
+ test::GetValueForConfig<String>(table.get(), "android:string/two",
+ test::ParseConfigOrDie("ar-rXB")));
+ // Check that we didn't override manual pseudolocalization.
+ val = test::GetValueForConfig<String>(table.get(), "android:string/three",
+ test::ParseConfigOrDie("en-rXA"));
+ ASSERT_NE(nullptr, val);
+ EXPECT_EQ(std::string("three"), *val->value);
- // Check that we didn't override manual pseudolocalization.
- val = test::getValueForConfig<String>(table.get(), "android:string/three",
- test::parseConfigOrDie("en-rXA"));
- ASSERT_NE(nullptr, val);
- EXPECT_EQ(std::string("three"), *val->value);
+ ASSERT_NE(nullptr,
+ test::GetValueForConfig<String>(table.get(), "android:string/three",
+ test::ParseConfigOrDie("ar-rXB")));
- ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), "android:string/three",
- test::parseConfigOrDie("ar-rXB")));
-
- // Check that four's translateable marker was honored.
- ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), "android:string/four",
- test::parseConfigOrDie("en-rXA")));
- ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), "android:string/four",
- test::parseConfigOrDie("ar-rXB")));
-
+ // Check that four's translateable marker was honored.
+ ASSERT_EQ(nullptr,
+ test::GetValueForConfig<String>(table.get(), "android:string/four",
+ test::ParseConfigOrDie("en-rXA")));
+ ASSERT_EQ(nullptr,
+ test::GetValueForConfig<String>(table.get(), "android:string/four",
+ test::ParseConfigOrDie("ar-rXB")));
}
-} // namespace aapt
-
+} // namespace aapt
diff --git a/tools/aapt2/compile/Pseudolocalizer.cpp b/tools/aapt2/compile/Pseudolocalizer.cpp
index 90d0d85..f89288f 100644
--- a/tools/aapt2/compile/Pseudolocalizer.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer.cpp
@@ -15,251 +15,333 @@
*/
#include "compile/Pseudolocalizer.h"
+
#include "util/Util.h"
namespace aapt {
// String basis to generate expansion
-static const std::string k_expansion_string = "one two three "
- "four five six seven eight nine ten eleven twelve thirteen "
- "fourteen fiveteen sixteen seventeen nineteen twenty";
+static const std::string kExpansionString =
+ "one two three "
+ "four five six seven eight nine ten eleven twelve thirteen "
+ "fourteen fiveteen sixteen seventeen nineteen twenty";
// Special unicode characters to override directionality of the words
-static const std::string k_rlm = "\u200f";
-static const std::string k_rlo = "\u202e";
-static const std::string k_pdf = "\u202c";
+static const std::string kRlm = "\u200f";
+static const std::string kRlo = "\u202e";
+static const std::string kPdf = "\u202c";
// Placeholder marks
-static const std::string k_placeholder_open = "\u00bb";
-static const std::string k_placeholder_close = "\u00ab";
+static const std::string kPlaceholderOpen = "\u00bb";
+static const std::string kPlaceholderClose = "\u00ab";
-static const char k_arg_start = '{';
-static const char k_arg_end = '}';
+static const char kArgStart = '{';
+static const char kArgEnd = '}';
class PseudoMethodNone : public PseudoMethodImpl {
-public:
- std::string text(const StringPiece& text) override { return text.toString(); }
- std::string placeholder(const StringPiece& text) override { return text.toString(); }
+ public:
+ std::string Text(const StringPiece& text) override { return text.ToString(); }
+ std::string Placeholder(const StringPiece& text) override {
+ return text.ToString();
+ }
};
class PseudoMethodBidi : public PseudoMethodImpl {
-public:
- std::string text(const StringPiece& text) override;
- std::string placeholder(const StringPiece& text) override;
+ public:
+ std::string Text(const StringPiece& text) override;
+ std::string Placeholder(const StringPiece& text) override;
};
class PseudoMethodAccent : public PseudoMethodImpl {
-public:
- PseudoMethodAccent() : mDepth(0), mWordCount(0), mLength(0) {}
- std::string start() override;
- std::string end() override;
- std::string text(const StringPiece& text) override;
- std::string placeholder(const StringPiece& text) override;
-private:
- size_t mDepth;
- size_t mWordCount;
- size_t mLength;
+ public:
+ PseudoMethodAccent() : depth_(0), word_count_(0), length_(0) {}
+ std::string Start() override;
+ std::string End() override;
+ std::string Text(const StringPiece& text) override;
+ std::string Placeholder(const StringPiece& text) override;
+
+ private:
+ size_t depth_;
+ size_t word_count_;
+ size_t length_;
};
-Pseudolocalizer::Pseudolocalizer(Method method) : mLastDepth(0) {
- setMethod(method);
+Pseudolocalizer::Pseudolocalizer(Method method) : last_depth_(0) {
+ SetMethod(method);
}
-void Pseudolocalizer::setMethod(Method method) {
- switch (method) {
+void Pseudolocalizer::SetMethod(Method method) {
+ switch (method) {
case Method::kNone:
- mImpl = util::make_unique<PseudoMethodNone>();
- break;
+ impl_ = util::make_unique<PseudoMethodNone>();
+ break;
case Method::kAccent:
- mImpl = util::make_unique<PseudoMethodAccent>();
- break;
+ impl_ = util::make_unique<PseudoMethodAccent>();
+ break;
case Method::kBidi:
- mImpl = util::make_unique<PseudoMethodBidi>();
+ impl_ = util::make_unique<PseudoMethodBidi>();
+ break;
+ }
+}
+
+std::string Pseudolocalizer::Text(const StringPiece& text) {
+ std::string out;
+ size_t depth = last_depth_;
+ size_t lastpos, pos;
+ const size_t length = text.size();
+ const char* str = text.data();
+ bool escaped = false;
+ for (lastpos = pos = 0; pos < length; pos++) {
+ char16_t c = str[pos];
+ if (escaped) {
+ escaped = false;
+ continue;
+ }
+ if (c == '\'') {
+ escaped = true;
+ continue;
+ }
+
+ if (c == kArgStart) {
+ depth++;
+ } else if (c == kArgEnd && depth) {
+ depth--;
+ }
+
+ if (last_depth_ != depth || pos == length - 1) {
+ bool pseudo = ((last_depth_ % 2) == 0);
+ size_t nextpos = pos;
+ if (!pseudo || depth == last_depth_) {
+ nextpos++;
+ }
+ size_t size = nextpos - lastpos;
+ if (size) {
+ std::string chunk = text.substr(lastpos, size).ToString();
+ if (pseudo) {
+ chunk = impl_->Text(chunk);
+ } else if (str[lastpos] == kArgStart && str[nextpos - 1] == kArgEnd) {
+ chunk = impl_->Placeholder(chunk);
+ }
+ out.append(chunk);
+ }
+ if (pseudo && depth < last_depth_) { // End of message
+ out.append(impl_->End());
+ } else if (!pseudo && depth > last_depth_) { // Start of message
+ out.append(impl_->Start());
+ }
+ lastpos = nextpos;
+ last_depth_ = depth;
+ }
+ }
+ return out;
+}
+
+static const char* PseudolocalizeChar(const char c) {
+ switch (c) {
+ case 'a':
+ return "\u00e5";
+ case 'b':
+ return "\u0253";
+ case 'c':
+ return "\u00e7";
+ case 'd':
+ return "\u00f0";
+ case 'e':
+ return "\u00e9";
+ case 'f':
+ return "\u0192";
+ case 'g':
+ return "\u011d";
+ case 'h':
+ return "\u0125";
+ case 'i':
+ return "\u00ee";
+ case 'j':
+ return "\u0135";
+ case 'k':
+ return "\u0137";
+ case 'l':
+ return "\u013c";
+ case 'm':
+ return "\u1e3f";
+ case 'n':
+ return "\u00f1";
+ case 'o':
+ return "\u00f6";
+ case 'p':
+ return "\u00fe";
+ case 'q':
+ return "\u0051";
+ case 'r':
+ return "\u0155";
+ case 's':
+ return "\u0161";
+ case 't':
+ return "\u0163";
+ case 'u':
+ return "\u00fb";
+ case 'v':
+ return "\u0056";
+ case 'w':
+ return "\u0175";
+ case 'x':
+ return "\u0445";
+ case 'y':
+ return "\u00fd";
+ case 'z':
+ return "\u017e";
+ case 'A':
+ return "\u00c5";
+ case 'B':
+ return "\u03b2";
+ case 'C':
+ return "\u00c7";
+ case 'D':
+ return "\u00d0";
+ case 'E':
+ return "\u00c9";
+ case 'G':
+ return "\u011c";
+ case 'H':
+ return "\u0124";
+ case 'I':
+ return "\u00ce";
+ case 'J':
+ return "\u0134";
+ case 'K':
+ return "\u0136";
+ case 'L':
+ return "\u013b";
+ case 'M':
+ return "\u1e3e";
+ case 'N':
+ return "\u00d1";
+ case 'O':
+ return "\u00d6";
+ case 'P':
+ return "\u00de";
+ case 'Q':
+ return "\u0071";
+ case 'R':
+ return "\u0154";
+ case 'S':
+ return "\u0160";
+ case 'T':
+ return "\u0162";
+ case 'U':
+ return "\u00db";
+ case 'V':
+ return "\u03bd";
+ case 'W':
+ return "\u0174";
+ case 'X':
+ return "\u00d7";
+ case 'Y':
+ return "\u00dd";
+ case 'Z':
+ return "\u017d";
+ case '!':
+ return "\u00a1";
+ case '?':
+ return "\u00bf";
+ case '$':
+ return "\u20ac";
+ default:
+ return nullptr;
+ }
+}
+
+static bool IsPossibleNormalPlaceholderEnd(const char c) {
+ switch (c) {
+ case 's':
+ return true;
+ case 'S':
+ return true;
+ case 'c':
+ return true;
+ case 'C':
+ return true;
+ case 'd':
+ return true;
+ case 'o':
+ return true;
+ case 'x':
+ return true;
+ case 'X':
+ return true;
+ case 'f':
+ return true;
+ case 'e':
+ return true;
+ case 'E':
+ return true;
+ case 'g':
+ return true;
+ case 'G':
+ return true;
+ case 'a':
+ return true;
+ case 'A':
+ return true;
+ case 'b':
+ return true;
+ case 'B':
+ return true;
+ case 'h':
+ return true;
+ case 'H':
+ return true;
+ case '%':
+ return true;
+ case 'n':
+ return true;
+ default:
+ return false;
+ }
+}
+
+static std::string PseudoGenerateExpansion(const unsigned int length) {
+ std::string result = kExpansionString;
+ const char* s = result.data();
+ if (result.size() < length) {
+ result += " ";
+ result += PseudoGenerateExpansion(length - result.size());
+ } else {
+ int ext = 0;
+ // Should contain only whole words, so looking for a space
+ for (unsigned int i = length + 1; i < result.size(); ++i) {
+ ++ext;
+ if (s[i] == ' ') {
break;
+ }
}
+ result = result.substr(0, length + ext);
+ }
+ return result;
}
-std::string Pseudolocalizer::text(const StringPiece& text) {
- std::string out;
- size_t depth = mLastDepth;
- size_t lastpos, pos;
- const size_t length = text.size();
- const char* str = text.data();
- bool escaped = false;
- for (lastpos = pos = 0; pos < length; pos++) {
- char16_t c = str[pos];
- if (escaped) {
- escaped = false;
- continue;
- }
- if (c == '\'') {
- escaped = true;
- continue;
- }
-
- if (c == k_arg_start) {
- depth++;
- } else if (c == k_arg_end && depth) {
- depth--;
- }
-
- if (mLastDepth != depth || pos == length - 1) {
- bool pseudo = ((mLastDepth % 2) == 0);
- size_t nextpos = pos;
- if (!pseudo || depth == mLastDepth) {
- nextpos++;
- }
- size_t size = nextpos - lastpos;
- if (size) {
- std::string chunk = text.substr(lastpos, size).toString();
- if (pseudo) {
- chunk = mImpl->text(chunk);
- } else if (str[lastpos] == k_arg_start && str[nextpos - 1] == k_arg_end) {
- chunk = mImpl->placeholder(chunk);
- }
- out.append(chunk);
- }
- if (pseudo && depth < mLastDepth) { // End of message
- out.append(mImpl->end());
- } else if (!pseudo && depth > mLastDepth) { // Start of message
- out.append(mImpl->start());
- }
- lastpos = nextpos;
- mLastDepth = depth;
- }
- }
- return out;
+std::string PseudoMethodAccent::Start() {
+ std::string result;
+ if (depth_ == 0) {
+ result = "[";
+ }
+ word_count_ = length_ = 0;
+ depth_++;
+ return result;
}
-static const char* pseudolocalizeChar(const char c) {
- switch (c) {
- case 'a': return "\u00e5";
- case 'b': return "\u0253";
- case 'c': return "\u00e7";
- case 'd': return "\u00f0";
- case 'e': return "\u00e9";
- case 'f': return "\u0192";
- case 'g': return "\u011d";
- case 'h': return "\u0125";
- case 'i': return "\u00ee";
- case 'j': return "\u0135";
- case 'k': return "\u0137";
- case 'l': return "\u013c";
- case 'm': return "\u1e3f";
- case 'n': return "\u00f1";
- case 'o': return "\u00f6";
- case 'p': return "\u00fe";
- case 'q': return "\u0051";
- case 'r': return "\u0155";
- case 's': return "\u0161";
- case 't': return "\u0163";
- case 'u': return "\u00fb";
- case 'v': return "\u0056";
- case 'w': return "\u0175";
- case 'x': return "\u0445";
- case 'y': return "\u00fd";
- case 'z': return "\u017e";
- case 'A': return "\u00c5";
- case 'B': return "\u03b2";
- case 'C': return "\u00c7";
- case 'D': return "\u00d0";
- case 'E': return "\u00c9";
- case 'G': return "\u011c";
- case 'H': return "\u0124";
- case 'I': return "\u00ce";
- case 'J': return "\u0134";
- case 'K': return "\u0136";
- case 'L': return "\u013b";
- case 'M': return "\u1e3e";
- case 'N': return "\u00d1";
- case 'O': return "\u00d6";
- case 'P': return "\u00de";
- case 'Q': return "\u0071";
- case 'R': return "\u0154";
- case 'S': return "\u0160";
- case 'T': return "\u0162";
- case 'U': return "\u00db";
- case 'V': return "\u03bd";
- case 'W': return "\u0174";
- case 'X': return "\u00d7";
- case 'Y': return "\u00dd";
- case 'Z': return "\u017d";
- case '!': return "\u00a1";
- case '?': return "\u00bf";
- case '$': return "\u20ac";
- default: return nullptr;
- }
-}
-
-static bool isPossibleNormalPlaceholderEnd(const char c) {
- switch (c) {
- case 's': return true;
- case 'S': return true;
- case 'c': return true;
- case 'C': return true;
- case 'd': return true;
- case 'o': return true;
- case 'x': return true;
- case 'X': return true;
- case 'f': return true;
- case 'e': return true;
- case 'E': return true;
- case 'g': return true;
- case 'G': return true;
- case 'a': return true;
- case 'A': return true;
- case 'b': return true;
- case 'B': return true;
- case 'h': return true;
- case 'H': return true;
- case '%': return true;
- case 'n': return true;
- default: return false;
- }
-}
-
-static std::string pseudoGenerateExpansion(const unsigned int length) {
- std::string result = k_expansion_string;
- const char* s = result.data();
- if (result.size() < length) {
- result += " ";
- result += pseudoGenerateExpansion(length - result.size());
- } else {
- int ext = 0;
- // Should contain only whole words, so looking for a space
- for (unsigned int i = length + 1; i < result.size(); ++i) {
- ++ext;
- if (s[i] == ' ') {
- break;
- }
- }
- result = result.substr(0, length + ext);
- }
- return result;
-}
-
-std::string PseudoMethodAccent::start() {
- std::string result;
- if (mDepth == 0) {
- result = "[";
- }
- mWordCount = mLength = 0;
- mDepth++;
- return result;
-}
-
-std::string PseudoMethodAccent::end() {
- std::string result;
- if (mLength) {
- result += " ";
- result += pseudoGenerateExpansion(mWordCount > 3 ? mLength : mLength / 2);
- }
- mWordCount = mLength = 0;
- mDepth--;
- if (mDepth == 0) {
- result += "]";
- }
- return result;
+std::string PseudoMethodAccent::End() {
+ std::string result;
+ if (length_) {
+ result += " ";
+ result += PseudoGenerateExpansion(word_count_ > 3 ? length_ : length_ / 2);
+ }
+ word_count_ = length_ = 0;
+ depth_--;
+ if (depth_ == 0) {
+ result += "]";
+ }
+ return result;
}
/**
@@ -267,128 +349,125 @@
*
* Note: This leaves placeholder syntax untouched.
*/
-std::string PseudoMethodAccent::text(const StringPiece& source)
-{
- const char* s = source.data();
- std::string result;
- const size_t I = source.size();
- bool lastspace = true;
- for (size_t i = 0; i < I; i++) {
- char c = s[i];
- if (c == '%') {
- // Placeholder syntax, no need to pseudolocalize
- std::string chunk;
- bool end = false;
- chunk.append(&c, 1);
- while (!end && i + 1 < I) {
- ++i;
- c = s[i];
- chunk.append(&c, 1);
- if (isPossibleNormalPlaceholderEnd(c)) {
- end = true;
- } else if (i + 1 < I && c == 't') {
- ++i;
- c = s[i];
- chunk.append(&c, 1);
- end = true;
- }
- }
- // Treat chunk as a placeholder unless it ends with %.
- result += ((c == '%') ? chunk : placeholder(chunk));
- } else if (c == '<' || c == '&') {
- // html syntax, no need to pseudolocalize
- bool tag_closed = false;
- while (!tag_closed && i < I) {
- if (c == '&') {
- std::string escapeText;
- escapeText.append(&c, 1);
- bool end = false;
- size_t htmlCodePos = i;
- while (!end && htmlCodePos < I) {
- ++htmlCodePos;
- c = s[htmlCodePos];
- escapeText.append(&c, 1);
- // Valid html code
- if (c == ';') {
- end = true;
- i = htmlCodePos;
- }
- // Wrong html code
- else if (!((c == '#' ||
- (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9')))) {
- end = true;
- }
- }
- result += escapeText;
- if (escapeText != "<") {
- tag_closed = true;
- }
- continue;
- }
- if (c == '>') {
- tag_closed = true;
- result.append(&c, 1);
- continue;
- }
- result.append(&c, 1);
- i++;
- c = s[i];
- }
- } else {
- // This is a pure text that should be pseudolocalized
- const char* p = pseudolocalizeChar(c);
- if (p != nullptr) {
- result += p;
- } else {
- bool space = isspace(c);
- if (lastspace && !space) {
- mWordCount++;
- }
- lastspace = space;
- result.append(&c, 1);
- }
- // Count only pseudolocalizable chars and delimiters
- mLength++;
+std::string PseudoMethodAccent::Text(const StringPiece& source) {
+ const char* s = source.data();
+ std::string result;
+ const size_t I = source.size();
+ bool lastspace = true;
+ for (size_t i = 0; i < I; i++) {
+ char c = s[i];
+ if (c == '%') {
+ // Placeholder syntax, no need to pseudolocalize
+ std::string chunk;
+ bool end = false;
+ chunk.append(&c, 1);
+ while (!end && i + 1 < I) {
+ ++i;
+ c = s[i];
+ chunk.append(&c, 1);
+ if (IsPossibleNormalPlaceholderEnd(c)) {
+ end = true;
+ } else if (i + 1 < I && c == 't') {
+ ++i;
+ c = s[i];
+ chunk.append(&c, 1);
+ end = true;
}
- }
- return result;
-}
-
-std::string PseudoMethodAccent::placeholder(const StringPiece& source) {
- // Surround a placeholder with brackets
- return k_placeholder_open + source.toString() + k_placeholder_close;
-}
-
-std::string PseudoMethodBidi::text(const StringPiece& source) {
- const char* s = source.data();
- std::string result;
- bool lastspace = true;
- bool space = true;
- for (size_t i = 0; i < source.size(); i++) {
- char c = s[i];
- space = isspace(c);
+ }
+ // Treat chunk as a placeholder unless it ends with %.
+ result += ((c == '%') ? chunk : Placeholder(chunk));
+ } else if (c == '<' || c == '&') {
+ // html syntax, no need to pseudolocalize
+ bool tag_closed = false;
+ while (!tag_closed && i < I) {
+ if (c == '&') {
+ std::string escape_text;
+ escape_text.append(&c, 1);
+ bool end = false;
+ size_t html_code_pos = i;
+ while (!end && html_code_pos < I) {
+ ++html_code_pos;
+ c = s[html_code_pos];
+ escape_text.append(&c, 1);
+ // Valid html code
+ if (c == ';') {
+ end = true;
+ i = html_code_pos;
+ }
+ // Wrong html code
+ else if (!((c == '#' || (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))) {
+ end = true;
+ }
+ }
+ result += escape_text;
+ if (escape_text != "<") {
+ tag_closed = true;
+ }
+ continue;
+ }
+ if (c == '>') {
+ tag_closed = true;
+ result.append(&c, 1);
+ continue;
+ }
+ result.append(&c, 1);
+ i++;
+ c = s[i];
+ }
+ } else {
+ // This is a pure text that should be pseudolocalized
+ const char* p = PseudolocalizeChar(c);
+ if (p != nullptr) {
+ result += p;
+ } else {
+ bool space = isspace(c);
if (lastspace && !space) {
- // Word start
- result += k_rlm + k_rlo;
- } else if (!lastspace && space) {
- // Word end
- result += k_pdf + k_rlm;
+ word_count_++;
}
lastspace = space;
result.append(&c, 1);
+ }
+ // Count only pseudolocalizable chars and delimiters
+ length_++;
}
- if (!lastspace) {
- // End of last word
- result += k_pdf + k_rlm;
- }
- return result;
+ }
+ return result;
}
-std::string PseudoMethodBidi::placeholder(const StringPiece& source) {
- // Surround a placeholder with directionality change sequence
- return k_rlm + k_rlo + source.toString() + k_pdf + k_rlm;
+std::string PseudoMethodAccent::Placeholder(const StringPiece& source) {
+ // Surround a placeholder with brackets
+ return kPlaceholderOpen + source.ToString() + kPlaceholderClose;
}
-} // namespace aapt
+std::string PseudoMethodBidi::Text(const StringPiece& source) {
+ const char* s = source.data();
+ std::string result;
+ bool lastspace = true;
+ bool space = true;
+ for (size_t i = 0; i < source.size(); i++) {
+ char c = s[i];
+ space = isspace(c);
+ if (lastspace && !space) {
+ // Word start
+ result += kRlm + kRlo;
+ } else if (!lastspace && space) {
+ // Word end
+ result += kPdf + kRlm;
+ }
+ lastspace = space;
+ result.append(&c, 1);
+ }
+ if (!lastspace) {
+ // End of last word
+ result += kPdf + kRlm;
+ }
+ return result;
+}
+
+std::string PseudoMethodBidi::Placeholder(const StringPiece& source) {
+ // Surround a placeholder with directionality change sequence
+ return kRlm + kRlo + source.ToString() + kPdf + kRlm;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/Pseudolocalizer.h b/tools/aapt2/compile/Pseudolocalizer.h
index 91d17d1..a6d2ad0 100644
--- a/tools/aapt2/compile/Pseudolocalizer.h
+++ b/tools/aapt2/compile/Pseudolocalizer.h
@@ -17,42 +17,44 @@
#ifndef AAPT_COMPILE_PSEUDOLOCALIZE_H
#define AAPT_COMPILE_PSEUDOLOCALIZE_H
+#include <memory>
+
+#include "android-base/macros.h"
+
#include "ResourceValues.h"
#include "StringPool.h"
#include "util/StringPiece.h"
-#include <android-base/macros.h>
-#include <memory>
-
namespace aapt {
class PseudoMethodImpl {
-public:
- virtual ~PseudoMethodImpl() {}
- virtual std::string start() { return {}; }
- virtual std::string end() { return {}; }
- virtual std::string text(const StringPiece& text) = 0;
- virtual std::string placeholder(const StringPiece& text) = 0;
+ public:
+ virtual ~PseudoMethodImpl() {}
+ virtual std::string Start() { return {}; }
+ virtual std::string End() { return {}; }
+ virtual std::string Text(const StringPiece& text) = 0;
+ virtual std::string Placeholder(const StringPiece& text) = 0;
};
class Pseudolocalizer {
-public:
- enum class Method {
- kNone,
- kAccent,
- kBidi,
- };
+ public:
+ enum class Method {
+ kNone,
+ kAccent,
+ kBidi,
+ };
- explicit Pseudolocalizer(Method method);
- void setMethod(Method method);
- std::string start() { return mImpl->start(); }
- std::string end() { return mImpl->end(); }
- std::string text(const StringPiece& text);
-private:
- std::unique_ptr<PseudoMethodImpl> mImpl;
- size_t mLastDepth;
+ explicit Pseudolocalizer(Method method);
+ void SetMethod(Method method);
+ std::string Start() { return impl_->Start(); }
+ std::string End() { return impl_->End(); }
+ std::string Text(const StringPiece& text);
+
+ private:
+ std::unique_ptr<PseudoMethodImpl> impl_;
+ size_t last_depth_;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_COMPILE_PSEUDOLOCALIZE_H */
diff --git a/tools/aapt2/compile/Pseudolocalizer_test.cpp b/tools/aapt2/compile/Pseudolocalizer_test.cpp
index c33e152..92eb3b5 100644
--- a/tools/aapt2/compile/Pseudolocalizer_test.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer_test.cpp
@@ -15,209 +15,216 @@
*/
#include "compile/Pseudolocalizer.h"
-#include "util/Util.h"
-#include <androidfw/ResourceTypes.h>
-#include <gtest/gtest.h>
+#include "test/Test.h"
+#include "util/Util.h"
namespace aapt {
// In this context, 'Axis' represents a particular field in the configuration,
// such as language or density.
-static ::testing::AssertionResult simpleHelper(const char* input, const char* expected,
+static ::testing::AssertionResult SimpleHelper(const char* input,
+ const char* expected,
Pseudolocalizer::Method method) {
- Pseudolocalizer pseudo(method);
- std::string result = pseudo.start() + pseudo.text(input) + pseudo.end();
- if (result != expected) {
- return ::testing::AssertionFailure() << expected << " != " << result;
- }
- return ::testing::AssertionSuccess();
+ Pseudolocalizer pseudo(method);
+ std::string result = pseudo.Start() + pseudo.Text(input) + pseudo.End();
+ if (result != expected) {
+ return ::testing::AssertionFailure() << expected << " != " << result;
+ }
+ return ::testing::AssertionSuccess();
}
-static ::testing::AssertionResult compoundHelper(const char* in1, const char* in2, const char *in3,
- const char* expected,
- Pseudolocalizer::Method method) {
- Pseudolocalizer pseudo(method);
- std::string result = pseudo.start() + pseudo.text(in1) + pseudo.text(in2) + pseudo.text(in3) +
- pseudo.end();
- if (result != expected) {
- return ::testing::AssertionFailure() << expected << " != " << result;
- }
- return ::testing::AssertionSuccess();
+static ::testing::AssertionResult CompoundHelper(
+ const char* in1, const char* in2, const char* in3, const char* expected,
+ Pseudolocalizer::Method method) {
+ Pseudolocalizer pseudo(method);
+ std::string result = pseudo.Start() + pseudo.Text(in1) + pseudo.Text(in2) +
+ pseudo.Text(in3) + pseudo.End();
+ if (result != expected) {
+ return ::testing::AssertionFailure() << expected << " != " << result;
+ }
+ return ::testing::AssertionSuccess();
}
TEST(PseudolocalizerTest, NoPseudolocalization) {
- EXPECT_TRUE(simpleHelper("", "", Pseudolocalizer::Method::kNone));
- EXPECT_TRUE(simpleHelper("Hello, world", "Hello, world", Pseudolocalizer::Method::kNone));
+ EXPECT_TRUE(SimpleHelper("", "", Pseudolocalizer::Method::kNone));
+ EXPECT_TRUE(SimpleHelper("Hello, world", "Hello, world",
+ Pseudolocalizer::Method::kNone));
- EXPECT_TRUE(compoundHelper("Hello,", " world", "",
- "Hello, world", Pseudolocalizer::Method::kNone));
+ EXPECT_TRUE(CompoundHelper("Hello,", " world", "", "Hello, world",
+ Pseudolocalizer::Method::kNone));
}
TEST(PseudolocalizerTest, PlaintextAccent) {
- EXPECT_TRUE(simpleHelper("", "[]", Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("Hello, world",
- "[Ĥéļļö, ŵöŕļð one two]", Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(SimpleHelper("", "[]", Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(SimpleHelper("Hello, world", "[Ĥéļļö, ŵöŕļð one two]",
+ Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("Hello, %1d",
- "[Ĥéļļö, »%1d« one two]", Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(SimpleHelper("Hello, %1d", "[Ĥéļļö, »%1d« one two]",
+ Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("Battery %1d%%",
- "[βåţţéŕý »%1d«%% one two]", Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("^1 %", "[^1 % one]", Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(compoundHelper("", "", "", "[]", Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(compoundHelper("Hello,", " world", "",
- "[Ĥéļļö, ŵöŕļð one two]", Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(SimpleHelper("Battery %1d%%", "[βåţţéŕý »%1d«%% one two]",
+ Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(
+ SimpleHelper("^1 %", "[^1 % one]", Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(
+ CompoundHelper("", "", "", "[]", Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(CompoundHelper("Hello,", " world", "", "[Ĥéļļö, ŵöŕļð one two]",
+ Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, PlaintextBidi) {
- EXPECT_TRUE(simpleHelper("", "", Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(simpleHelper("word",
- "\xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f",
- Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(simpleHelper(" word ",
- " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
- Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(simpleHelper(" word ",
- " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
- Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(simpleHelper("hello\n world\n",
- "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n" \
- " \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
- Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(compoundHelper("hello", "\n ", " world\n",
- "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n" \
- " \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
- Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(SimpleHelper("", "", Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(SimpleHelper(
+ "word", "\xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f",
+ Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(SimpleHelper(
+ " word ", " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
+ Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(SimpleHelper(
+ " word ", " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
+ Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(
+ SimpleHelper("hello\n world\n",
+ "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n"
+ " \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
+ Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(CompoundHelper(
+ "hello", "\n ", " world\n",
+ "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n"
+ " \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
+ Pseudolocalizer::Method::kBidi));
}
TEST(PseudolocalizerTest, SimpleICU) {
- // Single-fragment messages
- EXPECT_TRUE(simpleHelper("{placeholder}", "[»{placeholder}«]",
+ // Single-fragment messages
+ EXPECT_TRUE(SimpleHelper("{placeholder}", "[»{placeholder}«]",
+ Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(SimpleHelper("{USER} is offline", "[»{USER}« îš öƒƒļîñé one two]",
+ Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(SimpleHelper("Copy from {path1} to {path2}",
+ "[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]",
+ Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(SimpleHelper("Today is {1,date} {1,time}",
+ "[Ţöðåý îš »{1,date}« »{1,time}« one two]",
+ Pseudolocalizer::Method::kAccent));
+
+ // Multi-fragment messages
+ EXPECT_TRUE(CompoundHelper("{USER}", " ", "is offline",
+ "[»{USER}« îš öƒƒļîñé one two]",
Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("{USER} is offline",
- "[»{USER}« îš öƒƒļîñé one two]", Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("Copy from {path1} to {path2}",
+ EXPECT_TRUE(CompoundHelper("Copy from ", "{path1}", " to {path2}",
"[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]",
Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("Today is {1,date} {1,time}",
- "[Ţöðåý îš »{1,date}« »{1,time}« one two]",
- Pseudolocalizer::Method::kAccent));
-
- // Multi-fragment messages
- EXPECT_TRUE(compoundHelper("{USER}", " ", "is offline",
- "[»{USER}« îš öƒƒļîñé one two]",
- Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(compoundHelper("Copy from ", "{path1}", " to {path2}",
- "[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]",
- Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, ICUBidi) {
- // Single-fragment messages
- EXPECT_TRUE(simpleHelper("{placeholder}",
- "\xe2\x80\x8f\xE2\x80\xae{placeholder}\xE2\x80\xac\xe2\x80\x8f",
- Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(simpleHelper(
- "{COUNT, plural, one {one} other {other}}",
- "{COUNT, plural, " \
- "one {\xe2\x80\x8f\xE2\x80\xaeone\xE2\x80\xac\xe2\x80\x8f} " \
- "other {\xe2\x80\x8f\xE2\x80\xaeother\xE2\x80\xac\xe2\x80\x8f}}",
- Pseudolocalizer::Method::kBidi));
+ // Single-fragment messages
+ EXPECT_TRUE(SimpleHelper(
+ "{placeholder}",
+ "\xe2\x80\x8f\xE2\x80\xae{placeholder}\xE2\x80\xac\xe2\x80\x8f",
+ Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(SimpleHelper(
+ "{COUNT, plural, one {one} other {other}}",
+ "{COUNT, plural, "
+ "one {\xe2\x80\x8f\xE2\x80\xaeone\xE2\x80\xac\xe2\x80\x8f} "
+ "other {\xe2\x80\x8f\xE2\x80\xaeother\xE2\x80\xac\xe2\x80\x8f}}",
+ Pseudolocalizer::Method::kBidi));
}
TEST(PseudolocalizerTest, Escaping) {
- // Single-fragment messages
- EXPECT_TRUE(simpleHelper("'{USER'} is offline",
- "['{ÛŠÉŔ'} îš öƒƒļîñé one two three]",
- Pseudolocalizer::Method::kAccent));
+ // Single-fragment messages
+ EXPECT_TRUE(SimpleHelper("'{USER'} is offline",
+ "['{ÛŠÉŔ'} îš öƒƒļîñé one two three]",
+ Pseudolocalizer::Method::kAccent));
- // Multi-fragment messages
- EXPECT_TRUE(compoundHelper("'{USER}", " ", "''is offline",
- "['{ÛŠÉŔ} ''îš öƒƒļîñé one two three]",
- Pseudolocalizer::Method::kAccent));
+ // Multi-fragment messages
+ EXPECT_TRUE(CompoundHelper("'{USER}", " ", "''is offline",
+ "['{ÛŠÉŔ} ''îš öƒƒļîñé one two three]",
+ Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, PluralsAndSelects) {
- EXPECT_TRUE(simpleHelper(
- "{COUNT, plural, one {Delete a file} other {Delete {COUNT} files}}",
- "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} " \
+ EXPECT_TRUE(SimpleHelper(
+ "{COUNT, plural, one {Delete a file} other {Delete {COUNT} files}}",
+ "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} "
+ "other {Ðéļéţé »{COUNT}« ƒîļéš one two}}]",
+ Pseudolocalizer::Method::kAccent));
+
+ EXPECT_TRUE(
+ SimpleHelper("Distance is {COUNT, plural, one {# mile} other {# miles}}",
+ "[Ðîšţåñçé îš {COUNT, plural, one {# ḿîļé one two} "
+ "other {# ḿîļéš one two}}]",
+ Pseudolocalizer::Method::kAccent));
+
+ EXPECT_TRUE(SimpleHelper(
+ "{1, select, female {{1} added you} "
+ "male {{1} added you} other {{1} added you}}",
+ "[{1, select, female {»{1}« åððéð ýöû one two} "
+ "male {»{1}« åððéð ýöû one two} other {»{1}« åððéð ýöû one two}}]",
+ Pseudolocalizer::Method::kAccent));
+
+ EXPECT_TRUE(
+ CompoundHelper("{COUNT, plural, one {Delete a file} "
+ "other {Delete ",
+ "{COUNT}", " files}}",
+ "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} "
"other {Ðéļéţé »{COUNT}« ƒîļéš one two}}]",
- Pseudolocalizer::Method::kAccent));
-
- EXPECT_TRUE(simpleHelper(
- "Distance is {COUNT, plural, one {# mile} other {# miles}}",
- "[Ðîšţåñçé îš {COUNT, plural, one {# ḿîļé one two} " \
- "other {# ḿîļéš one two}}]",
- Pseudolocalizer::Method::kAccent));
-
- EXPECT_TRUE(simpleHelper(
- "{1, select, female {{1} added you} " \
- "male {{1} added you} other {{1} added you}}",
- "[{1, select, female {»{1}« åððéð ýöû one two} " \
- "male {»{1}« åððéð ýöû one two} other {»{1}« åððéð ýöû one two}}]",
- Pseudolocalizer::Method::kAccent));
-
- EXPECT_TRUE(compoundHelper(
- "{COUNT, plural, one {Delete a file} " \
- "other {Delete ", "{COUNT}", " files}}",
- "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} " \
- "other {Ðéļéţé »{COUNT}« ƒîļéš one two}}]",
- Pseudolocalizer::Method::kAccent));
+ Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, NestedICU) {
- EXPECT_TRUE(simpleHelper(
- "{person, select, " \
- "female {" \
- "{num_circles, plural," \
- "=0{{person} didn't add you to any of her circles.}" \
- "=1{{person} added you to one of her circles.}" \
- "other{{person} added you to her # circles.}}}" \
- "male {" \
- "{num_circles, plural," \
- "=0{{person} didn't add you to any of his circles.}" \
- "=1{{person} added you to one of his circles.}" \
- "other{{person} added you to his # circles.}}}" \
- "other {" \
- "{num_circles, plural," \
- "=0{{person} didn't add you to any of their circles.}" \
- "=1{{person} added you to one of their circles.}" \
- "other{{person} added you to their # circles.}}}}",
- "[{person, select, " \
- "female {" \
- "{num_circles, plural," \
- "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥéŕ çîŕçļéš." \
- " one two three four five}" \
- "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥéŕ çîŕçļéš." \
- " one two three four}" \
- "other{»{person}« åððéð ýöû ţö ĥéŕ # çîŕçļéš." \
- " one two three four}}}" \
- "male {" \
- "{num_circles, plural," \
- "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥîš çîŕçļéš." \
- " one two three four five}" \
- "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥîš çîŕçļéš." \
- " one two three four}" \
- "other{»{person}« åððéð ýöû ţö ĥîš # çîŕçļéš." \
- " one two three four}}}" \
- "other {{num_circles, plural," \
- "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ţĥéîŕ çîŕçļéš." \
- " one two three four five}" \
- "=1{»{person}« åððéð ýöû ţö öñé öƒ ţĥéîŕ çîŕçļéš." \
- " one two three four}" \
- "other{»{person}« åððéð ýöû ţö ţĥéîŕ # çîŕçļéš." \
- " one two three four}}}}]",
- Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(
+ SimpleHelper("{person, select, "
+ "female {"
+ "{num_circles, plural,"
+ "=0{{person} didn't add you to any of her circles.}"
+ "=1{{person} added you to one of her circles.}"
+ "other{{person} added you to her # circles.}}}"
+ "male {"
+ "{num_circles, plural,"
+ "=0{{person} didn't add you to any of his circles.}"
+ "=1{{person} added you to one of his circles.}"
+ "other{{person} added you to his # circles.}}}"
+ "other {"
+ "{num_circles, plural,"
+ "=0{{person} didn't add you to any of their circles.}"
+ "=1{{person} added you to one of their circles.}"
+ "other{{person} added you to their # circles.}}}}",
+ "[{person, select, "
+ "female {"
+ "{num_circles, plural,"
+ "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥéŕ çîŕçļéš."
+ " one two three four five}"
+ "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥéŕ çîŕçļéš."
+ " one two three four}"
+ "other{»{person}« åððéð ýöû ţö ĥéŕ # çîŕçļéš."
+ " one two three four}}}"
+ "male {"
+ "{num_circles, plural,"
+ "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥîš çîŕçļéš."
+ " one two three four five}"
+ "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥîš çîŕçļéš."
+ " one two three four}"
+ "other{»{person}« åððéð ýöû ţö ĥîš # çîŕçļéš."
+ " one two three four}}}"
+ "other {{num_circles, plural,"
+ "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ţĥéîŕ çîŕçļéš."
+ " one two three four five}"
+ "=1{»{person}« åððéð ýöû ţö öñé öƒ ţĥéîŕ çîŕçļéš."
+ " one two three four}"
+ "other{»{person}« åððéð ýöû ţö ţĥéîŕ # çîŕçļéš."
+ " one two three four}}}}]",
+ Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, RedefineMethod) {
- Pseudolocalizer pseudo(Pseudolocalizer::Method::kAccent);
- std::string result = pseudo.text("Hello, ");
- pseudo.setMethod(Pseudolocalizer::Method::kNone);
- result += pseudo.text("world!");
- ASSERT_EQ(StringPiece("Ĥéļļö, world!"), result);
+ Pseudolocalizer pseudo(Pseudolocalizer::Method::kAccent);
+ std::string result = pseudo.Text("Hello, ");
+ pseudo.SetMethod(Pseudolocalizer::Method::kNone);
+ result += pseudo.Text("world!");
+ ASSERT_EQ(StringPiece("Ĥéļļö, world!"), result);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp
index 3901419..d61a15a 100644
--- a/tools/aapt2/compile/XmlIdCollector.cpp
+++ b/tools/aapt2/compile/XmlIdCollector.cpp
@@ -14,57 +14,61 @@
* limitations under the License.
*/
-#include "ResourceUtils.h"
-#include "ResourceValues.h"
#include "compile/XmlIdCollector.h"
-#include "xml/XmlDom.h"
#include <algorithm>
#include <vector>
+#include "ResourceUtils.h"
+#include "ResourceValues.h"
+#include "xml/XmlDom.h"
+
namespace aapt {
namespace {
-static bool cmpName(const SourcedResourceName& a, const ResourceNameRef& b) {
- return a.name < b;
+static bool cmp_name(const SourcedResourceName& a, const ResourceNameRef& b) {
+ return a.name < b;
}
struct IdCollector : public xml::Visitor {
- using xml::Visitor::visit;
+ public:
+ using xml::Visitor::Visit;
- std::vector<SourcedResourceName>* mOutSymbols;
+ explicit IdCollector(std::vector<SourcedResourceName>* out_symbols)
+ : out_symbols_(out_symbols) {}
- explicit IdCollector(std::vector<SourcedResourceName>* outSymbols) : mOutSymbols(outSymbols) {
- }
-
- void visit(xml::Element* element) override {
- for (xml::Attribute& attr : element->attributes) {
- ResourceNameRef name;
- bool create = false;
- 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);
- if (iter == mOutSymbols->end() || iter->name != name) {
- mOutSymbols->insert(iter, SourcedResourceName{ name.toResourceName(),
- element->lineNumber });
- }
- }
- }
+ void Visit(xml::Element* element) override {
+ for (xml::Attribute& attr : element->attributes) {
+ ResourceNameRef name;
+ bool create = false;
+ if (ResourceUtils::ParseReference(attr.value, &name, &create, nullptr)) {
+ if (create && name.type == ResourceType::kId) {
+ auto iter = std::lower_bound(out_symbols_->begin(),
+ out_symbols_->end(), name, cmp_name);
+ if (iter == out_symbols_->end() || iter->name != name) {
+ out_symbols_->insert(iter,
+ SourcedResourceName{name.ToResourceName(),
+ element->line_number});
+ }
}
-
- xml::Visitor::visit(element);
+ }
}
+
+ xml::Visitor::Visit(element);
+ }
+
+ private:
+ std::vector<SourcedResourceName>* out_symbols_;
};
-} // namespace
+} // namespace
-bool XmlIdCollector::consume(IAaptContext* context, xml::XmlResource* xmlRes) {
- xmlRes->file.exportedSymbols.clear();
- IdCollector collector(&xmlRes->file.exportedSymbols);
- xmlRes->root->accept(&collector);
- return true;
+bool XmlIdCollector::Consume(IAaptContext* context, xml::XmlResource* xmlRes) {
+ xmlRes->file.exported_symbols.clear();
+ IdCollector collector(&xmlRes->file.exported_symbols);
+ xmlRes->root->Accept(&collector);
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/XmlIdCollector.h b/tools/aapt2/compile/XmlIdCollector.h
index 1b14944..8febf0f 100644
--- a/tools/aapt2/compile/XmlIdCollector.h
+++ b/tools/aapt2/compile/XmlIdCollector.h
@@ -23,9 +23,9 @@
namespace aapt {
struct XmlIdCollector : public IXmlResourceConsumer {
- bool consume(IAaptContext* context, xml::XmlResource* xmlRes) override;
+ bool Consume(IAaptContext* context, xml::XmlResource* xml_res) override;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_XMLIDCOLLECTOR_H */
diff --git a/tools/aapt2/compile/XmlIdCollector_test.cpp b/tools/aapt2/compile/XmlIdCollector_test.cpp
index 2c9eab8..98da56d 100644
--- a/tools/aapt2/compile/XmlIdCollector_test.cpp
+++ b/tools/aapt2/compile/XmlIdCollector_test.cpp
@@ -15,18 +15,17 @@
*/
#include "compile/XmlIdCollector.h"
-#include "test/Builders.h"
-#include "test/Context.h"
#include <algorithm>
-#include <gtest/gtest.h>
+
+#include "test/Test.h"
namespace aapt {
TEST(XmlIdCollectorTest, CollectsIds) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/foo"
text="@+id/bar">
@@ -34,28 +33,35 @@
class="@+id/bar"/>
</View>)EOF");
- XmlIdCollector collector;
- ASSERT_TRUE(collector.consume(context.get(), doc.get()));
+ XmlIdCollector collector;
+ ASSERT_TRUE(collector.Consume(context.get(), doc.get()));
- EXPECT_EQ(1, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(),
- SourcedResourceName{ test::parseNameOrDie("id/foo"), 3u }));
+ EXPECT_EQ(
+ 1, std::count(doc->file.exported_symbols.begin(),
+ doc->file.exported_symbols.end(),
+ SourcedResourceName{test::ParseNameOrDie("id/foo"), 3u}));
- EXPECT_EQ(1, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(),
- SourcedResourceName{ test::parseNameOrDie("id/bar"), 3u }));
+ EXPECT_EQ(
+ 1, std::count(doc->file.exported_symbols.begin(),
+ doc->file.exported_symbols.end(),
+ SourcedResourceName{test::ParseNameOrDie("id/bar"), 3u}));
- EXPECT_EQ(1, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(),
- SourcedResourceName{ test::parseNameOrDie("id/car"), 6u }));
+ EXPECT_EQ(
+ 1, std::count(doc->file.exported_symbols.begin(),
+ doc->file.exported_symbols.end(),
+ SourcedResourceName{test::ParseNameOrDie("id/car"), 6u}));
}
TEST(XmlIdCollectorTest, DontCollectNonIds) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View foo=\"@+string/foo\"/>");
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDom("<View foo=\"@+string/foo\"/>");
- XmlIdCollector collector;
- ASSERT_TRUE(collector.consume(context.get(), doc.get()));
+ XmlIdCollector collector;
+ ASSERT_TRUE(collector.Consume(context.get(), doc.get()));
- EXPECT_TRUE(doc->file.exportedSymbols.empty());
+ EXPECT_TRUE(doc->file.exported_symbols.empty());
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/diff/Diff.cpp b/tools/aapt2/diff/Diff.cpp
index 9b1f057..593e7ab 100644
--- a/tools/aapt2/diff/Diff.cpp
+++ b/tools/aapt2/diff/Diff.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "android-base/macros.h"
+
#include "Flags.h"
#include "ResourceTable.h"
#include "ValueVisitor.h"
@@ -22,417 +24,409 @@
#include "process/SymbolTable.h"
#include "unflatten/BinaryResourceParser.h"
-#include <android-base/macros.h>
-
namespace aapt {
class DiffContext : public IAaptContext {
-public:
- const std::string& getCompilationPackage() override {
- return mEmpty;
- }
+ public:
+ const std::string& GetCompilationPackage() override { return empty_; }
- uint8_t getPackageId() override {
- return 0x0;
- }
+ uint8_t GetPackageId() override { return 0x0; }
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ IDiagnostics* GetDiagnostics() override { return &diagnostics_; }
- NameMangler* getNameMangler() override {
- return &mNameMangler;
- }
+ NameMangler* GetNameMangler() override { return &name_mangler_; }
- SymbolTable* getExternalSymbols() override {
- return &mSymbolTable;
- }
+ SymbolTable* GetExternalSymbols() override { return &symbol_table_; }
- bool verbose() override {
- return false;
- }
+ bool IsVerbose() override { return false; }
- int getMinSdkVersion() override {
- return 0;
- }
+ int GetMinSdkVersion() override { return 0; }
-private:
- std::string mEmpty;
- StdErrDiagnostics mDiagnostics;
- NameMangler mNameMangler = NameMangler(NameManglerPolicy{});
- SymbolTable mSymbolTable;
+ private:
+ std::string empty_;
+ StdErrDiagnostics diagnostics_;
+ NameMangler name_mangler_ = NameMangler(NameManglerPolicy{});
+ SymbolTable symbol_table_;
};
class LoadedApk {
-public:
- LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
- std::unique_ptr<ResourceTable> table) :
- mSource(source), mApk(std::move(apk)), mTable(std::move(table)) {
- }
+ public:
+ LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
+ std::unique_ptr<ResourceTable> table)
+ : source_(source), apk_(std::move(apk)), table_(std::move(table)) {}
- io::IFileCollection* getFileCollection() {
- return mApk.get();
- }
+ io::IFileCollection* GetFileCollection() { return apk_.get(); }
- ResourceTable* getResourceTable() {
- return mTable.get();
- }
+ ResourceTable* GetResourceTable() { return table_.get(); }
- const Source& getSource() {
- return mSource;
- }
+ const Source& GetSource() { return source_; }
-private:
- Source mSource;
- std::unique_ptr<io::IFileCollection> mApk;
- std::unique_ptr<ResourceTable> mTable;
+ private:
+ Source source_;
+ std::unique_ptr<io::IFileCollection> apk_;
+ std::unique_ptr<ResourceTable> table_;
- DISALLOW_COPY_AND_ASSIGN(LoadedApk);
+ DISALLOW_COPY_AND_ASSIGN(LoadedApk);
};
-static std::unique_ptr<LoadedApk> loadApkFromPath(IAaptContext* context, const StringPiece& path) {
- Source source(path);
- std::string error;
- std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::create(path, &error);
- if (!apk) {
- context->getDiagnostics()->error(DiagMessage(source) << error);
- return {};
- }
+static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context,
+ const StringPiece& path) {
+ Source source(path);
+ std::string error;
+ std::unique_ptr<io::ZipFileCollection> apk =
+ io::ZipFileCollection::Create(path, &error);
+ if (!apk) {
+ context->GetDiagnostics()->Error(DiagMessage(source) << error);
+ return {};
+ }
- io::IFile* file = apk->findFile("resources.arsc");
- if (!file) {
- context->getDiagnostics()->error(DiagMessage(source) << "no resources.arsc found");
- return {};
- }
+ io::IFile* file = apk->FindFile("resources.arsc");
+ if (!file) {
+ context->GetDiagnostics()->Error(DiagMessage(source)
+ << "no resources.arsc found");
+ return {};
+ }
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- context->getDiagnostics()->error(DiagMessage(source) << "could not open resources.arsc");
- return {};
- }
+ std::unique_ptr<io::IData> data = file->OpenAsData();
+ if (!data) {
+ context->GetDiagnostics()->Error(DiagMessage(source)
+ << "could not open resources.arsc");
+ return {};
+ }
- std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
- BinaryResourceParser parser(context, table.get(), source, data->data(), data->size());
- if (!parser.parse()) {
- return {};
- }
+ std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
+ BinaryResourceParser parser(context, table.get(), source, data->data(),
+ data->size());
+ if (!parser.Parse()) {
+ return {};
+ }
- return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
+ return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
}
-static void emitDiffLine(const Source& source, const StringPiece& message) {
- std::cerr << source << ": " << message << "\n";
+static void EmitDiffLine(const Source& source, const StringPiece& message) {
+ std::cerr << source << ": " << message << "\n";
}
-static bool isSymbolVisibilityDifferent(const Symbol& symbolA, const Symbol& symbolB) {
- return symbolA.state != symbolB.state;
+static bool IsSymbolVisibilityDifferent(const Symbol& symbol_a,
+ const Symbol& symbol_b) {
+ return symbol_a.state != symbol_b.state;
}
template <typename Id>
-static bool isIdDiff(const Symbol& symbolA, const Maybe<Id>& idA,
- const Symbol& symbolB, const Maybe<Id>& idB) {
- if (symbolA.state == SymbolState::kPublic || symbolB.state == SymbolState::kPublic) {
- return idA != idB;
- }
- return false;
+static bool IsIdDiff(const Symbol& symbol_a, const Maybe<Id>& id_a,
+ const Symbol& symbol_b, const Maybe<Id>& id_b) {
+ if (symbol_a.state == SymbolState::kPublic ||
+ symbol_b.state == SymbolState::kPublic) {
+ return id_a != id_b;
+ }
+ return false;
}
-static bool emitResourceConfigValueDiff(IAaptContext* context,
- LoadedApk* apkA,
- ResourceTablePackage* pkgA,
- ResourceTableType* typeA,
- ResourceEntry* entryA,
- ResourceConfigValue* configValueA,
- LoadedApk* apkB,
- ResourceTablePackage* pkgB,
- ResourceTableType* typeB,
- ResourceEntry* entryB,
- ResourceConfigValue* configValueB) {
- Value* valueA = configValueA->value.get();
- Value* valueB = configValueB->value.get();
- if (!valueA->equals(valueB)) {
- std::stringstream strStream;
- strStream << "value " << pkgA->name << ":" << typeA->type << "/" << entryA->name
- << " config=" << configValueA->config << " does not match:\n";
- valueA->print(&strStream);
- strStream << "\n vs \n";
- valueB->print(&strStream);
- emitDiffLine(apkB->getSource(), strStream.str());
- return true;
- }
- return false;
+static bool EmitResourceConfigValueDiff(
+ IAaptContext* context, LoadedApk* apk_a, ResourceTablePackage* pkg_a,
+ ResourceTableType* type_a, ResourceEntry* entry_a,
+ ResourceConfigValue* config_value_a, LoadedApk* apk_b,
+ ResourceTablePackage* pkg_b, ResourceTableType* type_b,
+ ResourceEntry* entry_b, ResourceConfigValue* config_value_b) {
+ Value* value_a = config_value_a->value.get();
+ Value* value_b = config_value_b->value.get();
+ if (!value_a->Equals(value_b)) {
+ std::stringstream str_stream;
+ str_stream << "value " << pkg_a->name << ":" << type_a->type << "/"
+ << entry_a->name << " config=" << config_value_a->config
+ << " does not match:\n";
+ value_a->Print(&str_stream);
+ str_stream << "\n vs \n";
+ value_b->Print(&str_stream);
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ return true;
+ }
+ return false;
}
-static bool emitResourceEntryDiff(IAaptContext* context,
- LoadedApk* apkA,
- ResourceTablePackage* pkgA,
- ResourceTableType* typeA,
- ResourceEntry* entryA,
- LoadedApk* apkB,
- ResourceTablePackage* pkgB,
- ResourceTableType* typeB,
- ResourceEntry* entryB) {
- bool diff = false;
- for (std::unique_ptr<ResourceConfigValue>& configValueA : entryA->values) {
- ResourceConfigValue* configValueB = entryB->findValue(configValueA->config);
- if (!configValueB) {
- std::stringstream strStream;
- strStream << "missing " << pkgA->name << ":" << typeA->type << "/" << entryA->name
- << " config=" << configValueA->config;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
+static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a,
+ ResourceTablePackage* pkg_a,
+ ResourceTableType* type_a,
+ ResourceEntry* entry_a, LoadedApk* apk_b,
+ ResourceTablePackage* pkg_b,
+ ResourceTableType* type_b,
+ ResourceEntry* entry_b) {
+ bool diff = false;
+ for (std::unique_ptr<ResourceConfigValue>& config_value_a : entry_a->values) {
+ ResourceConfigValue* config_value_b =
+ entry_b->FindValue(config_value_a->config);
+ if (!config_value_b) {
+ std::stringstream str_stream;
+ str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/"
+ << entry_a->name << " config=" << config_value_a->config;
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
+ } else {
+ diff |= EmitResourceConfigValueDiff(
+ context, apk_a, pkg_a, type_a, entry_a, config_value_a.get(), apk_b,
+ pkg_b, type_b, entry_b, config_value_b);
+ }
+ }
+
+ // Check for any newly added config values.
+ for (std::unique_ptr<ResourceConfigValue>& config_value_b : entry_b->values) {
+ ResourceConfigValue* config_value_a =
+ entry_a->FindValue(config_value_b->config);
+ if (!config_value_a) {
+ std::stringstream str_stream;
+ str_stream << "new config " << pkg_b->name << ":" << type_b->type << "/"
+ << entry_b->name << " config=" << config_value_b->config;
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
+ }
+ }
+ return false;
+}
+
+static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a,
+ ResourceTablePackage* pkg_a,
+ ResourceTableType* type_a, LoadedApk* apk_b,
+ ResourceTablePackage* pkg_b,
+ ResourceTableType* type_b) {
+ bool diff = false;
+ for (std::unique_ptr<ResourceEntry>& entry_a : type_a->entries) {
+ ResourceEntry* entry_b = type_b->FindEntry(entry_a->name);
+ if (!entry_b) {
+ std::stringstream str_stream;
+ str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/"
+ << entry_a->name;
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
+ } else {
+ if (IsSymbolVisibilityDifferent(entry_a->symbol_status,
+ entry_b->symbol_status)) {
+ std::stringstream str_stream;
+ str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
+ << " has different visibility (";
+ if (entry_b->symbol_status.state == SymbolState::kPublic) {
+ str_stream << "PUBLIC";
} else {
- diff |= emitResourceConfigValueDiff(context, apkA, pkgA, typeA, entryA,
- configValueA.get(), apkB, pkgB, typeB, entryB,
- configValueB);
+ str_stream << "PRIVATE";
}
+ str_stream << " vs ";
+ if (entry_a->symbol_status.state == SymbolState::kPublic) {
+ str_stream << "PUBLIC";
+ } else {
+ str_stream << "PRIVATE";
+ }
+ str_stream << ")";
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
+ } else if (IsIdDiff(entry_a->symbol_status, entry_a->id,
+ entry_b->symbol_status, entry_b->id)) {
+ std::stringstream str_stream;
+ str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
+ << " has different public ID (";
+ if (entry_b->id) {
+ str_stream << "0x" << std::hex << entry_b->id.value();
+ } else {
+ str_stream << "none";
+ }
+ str_stream << " vs ";
+ if (entry_a->id) {
+ str_stream << "0x " << std::hex << entry_a->id.value();
+ } else {
+ str_stream << "none";
+ }
+ str_stream << ")";
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
+ }
+ diff |=
+ EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a.get(),
+ apk_b, pkg_b, type_b, entry_b);
}
+ }
- // Check for any newly added config values.
- for (std::unique_ptr<ResourceConfigValue>& configValueB : entryB->values) {
- ResourceConfigValue* configValueA = entryA->findValue(configValueB->config);
- if (!configValueA) {
- std::stringstream strStream;
- strStream << "new config " << pkgB->name << ":" << typeB->type << "/" << entryB->name
- << " config=" << configValueB->config;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
+ // Check for any newly added entries.
+ for (std::unique_ptr<ResourceEntry>& entry_b : type_b->entries) {
+ ResourceEntry* entry_a = type_a->FindEntry(entry_b->name);
+ if (!entry_a) {
+ std::stringstream str_stream;
+ str_stream << "new entry " << pkg_b->name << ":" << type_b->type << "/"
+ << entry_b->name;
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
}
- return false;
+ }
+ return diff;
}
-static bool emitResourceTypeDiff(IAaptContext* context,
- LoadedApk* apkA,
- ResourceTablePackage* pkgA,
- ResourceTableType* typeA,
- LoadedApk* apkB,
- ResourceTablePackage* pkgB,
- ResourceTableType* typeB) {
- bool diff = false;
- for (std::unique_ptr<ResourceEntry>& entryA : typeA->entries) {
- ResourceEntry* entryB = typeB->findEntry(entryA->name);
- if (!entryB) {
- std::stringstream strStream;
- strStream << "missing " << pkgA->name << ":" << typeA->type << "/" << entryA->name;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
+static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a,
+ ResourceTablePackage* pkg_a,
+ LoadedApk* apk_b,
+ ResourceTablePackage* pkg_b) {
+ bool diff = false;
+ for (std::unique_ptr<ResourceTableType>& type_a : pkg_a->types) {
+ ResourceTableType* type_b = pkg_b->FindType(type_a->type);
+ if (!type_b) {
+ std::stringstream str_stream;
+ str_stream << "missing " << pkg_a->name << ":" << type_a->type;
+ EmitDiffLine(apk_a->GetSource(), str_stream.str());
+ diff = true;
+ } else {
+ if (IsSymbolVisibilityDifferent(type_a->symbol_status,
+ type_b->symbol_status)) {
+ std::stringstream str_stream;
+ str_stream << pkg_a->name << ":" << type_a->type
+ << " has different visibility (";
+ if (type_b->symbol_status.state == SymbolState::kPublic) {
+ str_stream << "PUBLIC";
} else {
- if (isSymbolVisibilityDifferent(entryA->symbolStatus, entryB->symbolStatus)) {
- std::stringstream strStream;
- strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
- << " has different visibility (";
- if (entryB->symbolStatus.state == SymbolState::kPublic) {
- strStream << "PUBLIC";
- } else {
- strStream << "PRIVATE";
- }
- strStream << " vs ";
- if (entryA->symbolStatus.state == SymbolState::kPublic) {
- strStream << "PUBLIC";
- } else {
- strStream << "PRIVATE";
- }
- strStream << ")";
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- } else if (isIdDiff(entryA->symbolStatus, entryA->id,
- entryB->symbolStatus, entryB->id)) {
- std::stringstream strStream;
- strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
- << " has different public ID (";
- if (entryB->id) {
- strStream << "0x" << std::hex << entryB->id.value();
- } else {
- strStream << "none";
- }
- strStream << " vs ";
- if (entryA->id) {
- strStream << "0x " << std::hex << entryA->id.value();
- } else {
- strStream << "none";
- }
- strStream << ")";
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
- diff |= emitResourceEntryDiff(context, apkA, pkgA, typeA, entryA.get(),
- apkB, pkgB, typeB, entryB);
+ str_stream << "PRIVATE";
}
+ str_stream << " vs ";
+ if (type_a->symbol_status.state == SymbolState::kPublic) {
+ str_stream << "PUBLIC";
+ } else {
+ str_stream << "PRIVATE";
+ }
+ str_stream << ")";
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
+ } else if (IsIdDiff(type_a->symbol_status, type_a->id,
+ type_b->symbol_status, type_b->id)) {
+ std::stringstream str_stream;
+ str_stream << pkg_a->name << ":" << type_a->type
+ << " has different public ID (";
+ if (type_b->id) {
+ str_stream << "0x" << std::hex << type_b->id.value();
+ } else {
+ str_stream << "none";
+ }
+ str_stream << " vs ";
+ if (type_a->id) {
+ str_stream << "0x " << std::hex << type_a->id.value();
+ } else {
+ str_stream << "none";
+ }
+ str_stream << ")";
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
+ }
+ diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a.get(), apk_b,
+ pkg_b, type_b);
}
+ }
- // Check for any newly added entries.
- for (std::unique_ptr<ResourceEntry>& entryB : typeB->entries) {
- ResourceEntry* entryA = typeA->findEntry(entryB->name);
- if (!entryA) {
- std::stringstream strStream;
- strStream << "new entry " << pkgB->name << ":" << typeB->type << "/" << entryB->name;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
+ // Check for any newly added types.
+ for (std::unique_ptr<ResourceTableType>& type_b : pkg_b->types) {
+ ResourceTableType* type_a = pkg_a->FindType(type_b->type);
+ if (!type_a) {
+ std::stringstream str_stream;
+ str_stream << "new type " << pkg_b->name << ":" << type_b->type;
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
}
- return diff;
+ }
+ return diff;
}
-static bool emitResourcePackageDiff(IAaptContext* context, LoadedApk* apkA,
- ResourceTablePackage* pkgA,
- LoadedApk* apkB, ResourceTablePackage* pkgB) {
- bool diff = false;
- for (std::unique_ptr<ResourceTableType>& typeA : pkgA->types) {
- ResourceTableType* typeB = pkgB->findType(typeA->type);
- if (!typeB) {
- std::stringstream strStream;
- strStream << "missing " << pkgA->name << ":" << typeA->type;
- emitDiffLine(apkA->getSource(), strStream.str());
- diff = true;
+static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a,
+ LoadedApk* apk_b) {
+ ResourceTable* table_a = apk_a->GetResourceTable();
+ ResourceTable* table_b = apk_b->GetResourceTable();
+
+ bool diff = false;
+ for (std::unique_ptr<ResourceTablePackage>& pkg_a : table_a->packages) {
+ ResourceTablePackage* pkg_b = table_b->FindPackage(pkg_a->name);
+ if (!pkg_b) {
+ std::stringstream str_stream;
+ str_stream << "missing package " << pkg_a->name;
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
+ } else {
+ if (pkg_a->id != pkg_b->id) {
+ std::stringstream str_stream;
+ str_stream << "package '" << pkg_a->name << "' has different id (";
+ if (pkg_b->id) {
+ str_stream << "0x" << std::hex << pkg_b->id.value();
} else {
- if (isSymbolVisibilityDifferent(typeA->symbolStatus, typeB->symbolStatus)) {
- std::stringstream strStream;
- strStream << pkgA->name << ":" << typeA->type << " has different visibility (";
- if (typeB->symbolStatus.state == SymbolState::kPublic) {
- strStream << "PUBLIC";
- } else {
- strStream << "PRIVATE";
- }
- strStream << " vs ";
- if (typeA->symbolStatus.state == SymbolState::kPublic) {
- strStream << "PUBLIC";
- } else {
- strStream << "PRIVATE";
- }
- strStream << ")";
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- } else if (isIdDiff(typeA->symbolStatus, typeA->id, typeB->symbolStatus, typeB->id)) {
- std::stringstream strStream;
- strStream << pkgA->name << ":" << typeA->type << " has different public ID (";
- if (typeB->id) {
- strStream << "0x" << std::hex << typeB->id.value();
- } else {
- strStream << "none";
- }
- strStream << " vs ";
- if (typeA->id) {
- strStream << "0x " << std::hex << typeA->id.value();
- } else {
- strStream << "none";
- }
- strStream << ")";
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
- diff |= emitResourceTypeDiff(context, apkA, pkgA, typeA.get(), apkB, pkgB, typeB);
+ str_stream << "none";
}
- }
-
- // Check for any newly added types.
- for (std::unique_ptr<ResourceTableType>& typeB : pkgB->types) {
- ResourceTableType* typeA = pkgA->findType(typeB->type);
- if (!typeA) {
- std::stringstream strStream;
- strStream << "new type " << pkgB->name << ":" << typeB->type;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
- }
- return diff;
-}
-
-static bool emitResourceTableDiff(IAaptContext* context, LoadedApk* apkA, LoadedApk* apkB) {
- ResourceTable* tableA = apkA->getResourceTable();
- ResourceTable* tableB = apkB->getResourceTable();
-
- bool diff = false;
- for (std::unique_ptr<ResourceTablePackage>& pkgA : tableA->packages) {
- ResourceTablePackage* pkgB = tableB->findPackage(pkgA->name);
- if (!pkgB) {
- std::stringstream strStream;
- strStream << "missing package " << pkgA->name;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
+ str_stream << " vs ";
+ if (pkg_a->id) {
+ str_stream << "0x" << std::hex << pkg_a->id.value();
} else {
- if (pkgA->id != pkgB->id) {
- std::stringstream strStream;
- strStream << "package '" << pkgA->name << "' has different id (";
- if (pkgB->id) {
- strStream << "0x" << std::hex << pkgB->id.value();
- } else {
- strStream << "none";
- }
- strStream << " vs ";
- if (pkgA->id) {
- strStream << "0x" << std::hex << pkgA->id.value();
- } else {
- strStream << "none";
- }
- strStream << ")";
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
- diff |= emitResourcePackageDiff(context, apkA, pkgA.get(), apkB, pkgB);
+ str_stream << "none";
}
+ str_stream << ")";
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
+ }
+ diff |=
+ EmitResourcePackageDiff(context, apk_a, pkg_a.get(), apk_b, pkg_b);
}
+ }
- // Check for any newly added packages.
- for (std::unique_ptr<ResourceTablePackage>& pkgB : tableB->packages) {
- ResourceTablePackage* pkgA = tableA->findPackage(pkgB->name);
- if (!pkgA) {
- std::stringstream strStream;
- strStream << "new package " << pkgB->name;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
+ // Check for any newly added packages.
+ for (std::unique_ptr<ResourceTablePackage>& pkg_b : table_b->packages) {
+ ResourceTablePackage* pkg_a = table_a->FindPackage(pkg_b->name);
+ if (!pkg_a) {
+ std::stringstream str_stream;
+ str_stream << "new package " << pkg_b->name;
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
}
- return diff;
+ }
+ return diff;
}
class ZeroingReferenceVisitor : public ValueVisitor {
-public:
- using ValueVisitor::visit;
+ public:
+ using ValueVisitor::Visit;
- void visit(Reference* ref) override {
- if (ref->name && ref->id) {
- if (ref->id.value().packageId() == 0x7f) {
- ref->id = {};
- }
- }
+ void Visit(Reference* ref) override {
+ if (ref->name && ref->id) {
+ if (ref->id.value().package_id() == 0x7f) {
+ ref->id = {};
+ }
}
+ }
};
-static void zeroOutAppReferences(ResourceTable* table) {
- ZeroingReferenceVisitor visitor;
- visitAllValuesInTable(table, &visitor);
+static void ZeroOutAppReferences(ResourceTable* table) {
+ ZeroingReferenceVisitor visitor;
+ VisitAllValuesInTable(table, &visitor);
}
-int diff(const std::vector<StringPiece>& args) {
- DiffContext context;
+int Diff(const std::vector<StringPiece>& args) {
+ DiffContext context;
- Flags flags;
- if (!flags.parse("aapt2 diff", args, &std::cerr)) {
- return 1;
- }
+ Flags flags;
+ if (!flags.Parse("aapt2 diff", args, &std::cerr)) {
+ return 1;
+ }
- if (flags.getArgs().size() != 2u) {
- std::cerr << "must have two apks as arguments.\n\n";
- flags.usage("aapt2 diff", &std::cerr);
- return 1;
- }
+ if (flags.GetArgs().size() != 2u) {
+ std::cerr << "must have two apks as arguments.\n\n";
+ flags.Usage("aapt2 diff", &std::cerr);
+ return 1;
+ }
- std::unique_ptr<LoadedApk> apkA = loadApkFromPath(&context, flags.getArgs()[0]);
- std::unique_ptr<LoadedApk> apkB = loadApkFromPath(&context, flags.getArgs()[1]);
- if (!apkA || !apkB) {
- return 1;
- }
+ std::unique_ptr<LoadedApk> apk_a =
+ LoadApkFromPath(&context, flags.GetArgs()[0]);
+ std::unique_ptr<LoadedApk> apk_b =
+ LoadApkFromPath(&context, flags.GetArgs()[1]);
+ if (!apk_a || !apk_b) {
+ return 1;
+ }
- // Zero out Application IDs in references.
- zeroOutAppReferences(apkA->getResourceTable());
- zeroOutAppReferences(apkB->getResourceTable());
+ // Zero out Application IDs in references.
+ ZeroOutAppReferences(apk_a->GetResourceTable());
+ ZeroOutAppReferences(apk_b->GetResourceTable());
- if (emitResourceTableDiff(&context, apkA.get(), apkB.get())) {
- // We emitted a diff, so return 1 (failure).
- return 1;
- }
- return 0;
+ if (EmitResourceTableDiff(&context, apk_a.get(), apk_b.get())) {
+ // We emitted a diff, so return 1 (failure).
+ return 1;
+ }
+ return 0;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/dump/Dump.cpp
index f61ec94..2920c2a 100644
--- a/tools/aapt2/dump/Dump.cpp
+++ b/tools/aapt2/dump/Dump.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <vector>
+
#include "Debug.h"
#include "Diagnostics.h"
#include "Flags.h"
@@ -24,187 +26,179 @@
#include "util/Files.h"
#include "util/StringPiece.h"
-#include <vector>
-
namespace aapt {
-//struct DumpOptions {
-//
-//};
+void DumpCompiledFile(const pb::CompiledFile& pb_file, const void* data,
+ size_t len, const Source& source, IAaptContext* context) {
+ std::unique_ptr<ResourceFile> file =
+ DeserializeCompiledFileFromPb(pb_file, source, context->GetDiagnostics());
+ if (!file) {
+ context->GetDiagnostics()->Warn(DiagMessage()
+ << "failed to read compiled file");
+ return;
+ }
-void dumpCompiledFile(const pb::CompiledFile& pbFile, const void* data, size_t len,
- const Source& source, IAaptContext* context) {
- std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(pbFile, source,
- context->getDiagnostics());
- if (!file) {
- context->getDiagnostics()->warn(DiagMessage() << "failed to read compiled file");
- return;
- }
-
- std::cout << "Resource: " << file->name << "\n"
- << "Config: " << file->config << "\n"
- << "Source: " << file->source << "\n";
+ std::cout << "Resource: " << file->name << "\n"
+ << "Config: " << file->config << "\n"
+ << "Source: " << file->source << "\n";
}
-void tryDumpFile(IAaptContext* context, const std::string& filePath) {
- std::unique_ptr<ResourceTable> table;
+void TryDumpFile(IAaptContext* context, const std::string& file_path) {
+ std::unique_ptr<ResourceTable> table;
- std::string err;
- std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::create(filePath, &err);
- if (zip) {
- io::IFile* file = zip->findFile("resources.arsc.flat");
- if (file) {
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- context->getDiagnostics()->error(DiagMessage(filePath)
- << "failed to open resources.arsc.flat");
- return;
- }
+ std::string err;
+ std::unique_ptr<io::ZipFileCollection> zip =
+ io::ZipFileCollection::Create(file_path, &err);
+ if (zip) {
+ io::IFile* file = zip->FindFile("resources.arsc.flat");
+ if (file) {
+ std::unique_ptr<io::IData> data = file->OpenAsData();
+ if (!data) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(file_path) << "failed to open resources.arsc.flat");
+ return;
+ }
- pb::ResourceTable pbTable;
- if (!pbTable.ParseFromArray(data->data(), data->size())) {
- context->getDiagnostics()->error(DiagMessage(filePath)
- << "invalid resources.arsc.flat");
- return;
- }
+ pb::ResourceTable pb_table;
+ if (!pb_table.ParseFromArray(data->data(), data->size())) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path)
+ << "invalid resources.arsc.flat");
+ return;
+ }
- table = deserializeTableFromPb(
- pbTable, Source(filePath), context->getDiagnostics());
- if (!table) {
- return;
- }
- }
-
- if (!table) {
- file = zip->findFile("resources.arsc");
- if (file) {
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- context->getDiagnostics()->error(DiagMessage(filePath)
- << "failed to open resources.arsc");
- return;
- }
-
- table = util::make_unique<ResourceTable>();
- BinaryResourceParser parser(context, table.get(), Source(filePath),
- data->data(), data->size());
- if (!parser.parse()) {
- return;
- }
- }
- }
+ table = DeserializeTableFromPb(pb_table, Source(file_path),
+ context->GetDiagnostics());
+ if (!table) {
+ return;
+ }
}
if (!table) {
- Maybe<android::FileMap> file = file::mmapPath(filePath, &err);
- if (!file) {
- context->getDiagnostics()->error(DiagMessage(filePath) << err);
- return;
+ file = zip->FindFile("resources.arsc");
+ if (file) {
+ std::unique_ptr<io::IData> data = file->OpenAsData();
+ if (!data) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path)
+ << "failed to open resources.arsc");
+ return;
}
- android::FileMap* fileMap = &file.value();
-
- // Try as a compiled table.
- pb::ResourceTable pbTable;
- if (pbTable.ParseFromArray(fileMap->getDataPtr(), fileMap->getDataLength())) {
- table = deserializeTableFromPb(pbTable, Source(filePath), context->getDiagnostics());
+ table = util::make_unique<ResourceTable>();
+ BinaryResourceParser parser(context, table.get(), Source(file_path),
+ data->data(), data->size());
+ if (!parser.Parse()) {
+ return;
}
+ }
+ }
+ }
- if (!table) {
- // Try as a compiled file.
- CompiledFileInputStream input(fileMap->getDataPtr(), fileMap->getDataLength());
-
- uint32_t numFiles = 0;
- if (!input.ReadLittleEndian32(&numFiles)) {
- return;
- }
-
- for (uint32_t i = 0; i < numFiles; i++) {
- pb::CompiledFile compiledFile;
- if (!input.ReadCompiledFile(&compiledFile)) {
- context->getDiagnostics()->warn(DiagMessage() << "failed to read compiled file");
- return;
- }
-
- uint64_t offset, len;
- if (!input.ReadDataMetaData(&offset, &len)) {
- context->getDiagnostics()->warn(DiagMessage() << "failed to read meta data");
- return;
- }
-
- const void* data = static_cast<const uint8_t*>(fileMap->getDataPtr()) + offset;
- dumpCompiledFile(compiledFile, data, len, Source(filePath), context);
- }
- }
+ if (!table) {
+ Maybe<android::FileMap> file = file::MmapPath(file_path, &err);
+ if (!file) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path) << err);
+ return;
}
- if (table) {
- DebugPrintTableOptions debugPrintTableOptions;
- debugPrintTableOptions.showSources = true;
- Debug::printTable(table.get(), debugPrintTableOptions);
+ android::FileMap* file_map = &file.value();
+
+ // Try as a compiled table.
+ pb::ResourceTable pb_table;
+ if (pb_table.ParseFromArray(file_map->getDataPtr(),
+ file_map->getDataLength())) {
+ table = DeserializeTableFromPb(pb_table, Source(file_path),
+ context->GetDiagnostics());
}
+
+ if (!table) {
+ // Try as a compiled file.
+ CompiledFileInputStream input(file_map->getDataPtr(),
+ file_map->getDataLength());
+
+ uint32_t num_files = 0;
+ if (!input.ReadLittleEndian32(&num_files)) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < num_files; i++) {
+ pb::CompiledFile compiled_file;
+ if (!input.ReadCompiledFile(&compiled_file)) {
+ context->GetDiagnostics()->Warn(DiagMessage()
+ << "failed to read compiled file");
+ return;
+ }
+
+ uint64_t offset, len;
+ if (!input.ReadDataMetaData(&offset, &len)) {
+ context->GetDiagnostics()->Warn(DiagMessage()
+ << "failed to read meta data");
+ return;
+ }
+
+ const void* data =
+ static_cast<const uint8_t*>(file_map->getDataPtr()) + offset;
+ DumpCompiledFile(compiled_file, data, len, Source(file_path), context);
+ }
+ }
+ }
+
+ if (table) {
+ DebugPrintTableOptions options;
+ options.show_sources = true;
+ Debug::PrintTable(table.get(), options);
+ }
}
class DumpContext : public IAaptContext {
-public:
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ public:
+ IDiagnostics* GetDiagnostics() override { return &diagnostics_; }
- NameMangler* getNameMangler() override {
- abort();
- return nullptr;
- }
+ NameMangler* GetNameMangler() override {
+ abort();
+ return nullptr;
+ }
- const std::string& getCompilationPackage() override {
- static std::string empty;
- return empty;
- }
+ const std::string& GetCompilationPackage() override {
+ static std::string empty;
+ return empty;
+ }
- uint8_t getPackageId() override {
- return 0;
- }
+ uint8_t GetPackageId() override { return 0; }
- SymbolTable* getExternalSymbols() override {
- abort();
- return nullptr;
- }
+ SymbolTable* GetExternalSymbols() override {
+ abort();
+ return nullptr;
+ }
- bool verbose() override {
- return mVerbose;
- }
+ bool IsVerbose() override { return verbose_; }
- void setVerbose(bool val) {
- mVerbose = val;
- }
+ void SetVerbose(bool val) { verbose_ = val; }
- int getMinSdkVersion() override {
- return 0;
- }
+ int GetMinSdkVersion() override { return 0; }
-private:
- StdErrDiagnostics mDiagnostics;
- bool mVerbose = false;
+ private:
+ StdErrDiagnostics diagnostics_;
+ bool verbose_ = false;
};
/**
* Entry point for dump command.
*/
-int dump(const std::vector<StringPiece>& args) {
- bool verbose = false;
- Flags flags = Flags()
- .optionalSwitch("-v", "increase verbosity of output", &verbose);
- if (!flags.parse("aapt2 dump", args, &std::cerr)) {
- return 1;
- }
+int Dump(const std::vector<StringPiece>& args) {
+ bool verbose = false;
+ Flags flags =
+ Flags().OptionalSwitch("-v", "increase verbosity of output", &verbose);
+ if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
+ return 1;
+ }
- DumpContext context;
- context.setVerbose(verbose);
+ DumpContext context;
+ context.SetVerbose(verbose);
- for (const std::string& arg : flags.getArgs()) {
- tryDumpFile(&context, arg);
- }
- return 0;
+ for (const std::string& arg : flags.GetArgs()) {
+ TryDumpFile(&context, arg);
+ }
+ return 0;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/filter/ConfigFilter.cpp b/tools/aapt2/filter/ConfigFilter.cpp
index 68a017d..66aff82 100644
--- a/tools/aapt2/filter/ConfigFilter.cpp
+++ b/tools/aapt2/filter/ConfigFilter.cpp
@@ -14,64 +14,69 @@
* limitations under the License.
*/
-#include "ConfigDescription.h"
#include "filter/ConfigFilter.h"
-#include <androidfw/ResourceTypes.h>
+#include "androidfw/ResourceTypes.h"
+
+#include "ConfigDescription.h"
namespace aapt {
-void AxisConfigFilter::addConfig(ConfigDescription config) {
- uint32_t diffMask = ConfigDescription::defaultConfig().diff(config);
+void AxisConfigFilter::AddConfig(ConfigDescription config) {
+ uint32_t diff_mask = ConfigDescription::DefaultConfig().diff(config);
- // Ignore the version
- diffMask &= ~android::ResTable_config::CONFIG_VERSION;
+ // Ignore the version
+ diff_mask &= ~android::ResTable_config::CONFIG_VERSION;
- // Ignore any densities. Those are best handled in --preferred-density
- if ((diffMask & android::ResTable_config::CONFIG_DENSITY) != 0) {
- config.density = 0;
- diffMask &= ~android::ResTable_config::CONFIG_DENSITY;
- }
+ // Ignore any densities. Those are best handled in --preferred-density
+ if ((diff_mask & android::ResTable_config::CONFIG_DENSITY) != 0) {
+ config.density = 0;
+ diff_mask &= ~android::ResTable_config::CONFIG_DENSITY;
+ }
- mConfigs.insert(std::make_pair(config, diffMask));
- mConfigMask |= diffMask;
+ configs_.insert(std::make_pair(config, diff_mask));
+ config_mask_ |= diff_mask;
}
-bool AxisConfigFilter::match(const ConfigDescription& config) const {
- const uint32_t mask = ConfigDescription::defaultConfig().diff(config);
- if ((mConfigMask & mask) == 0) {
- // The two configurations don't have any common axis.
- return true;
- }
+bool AxisConfigFilter::Match(const ConfigDescription& config) const {
+ const uint32_t mask = ConfigDescription::DefaultConfig().diff(config);
+ if ((config_mask_ & mask) == 0) {
+ // The two configurations don't have any common axis.
+ return true;
+ }
- uint32_t matchedAxis = 0;
- for (const auto& entry : mConfigs) {
- const ConfigDescription& target = entry.first;
- const uint32_t diffMask = entry.second;
- uint32_t diff = target.diff(config);
- if ((diff & diffMask) == 0) {
- // Mark the axis that was matched.
- matchedAxis |= diffMask;
- } else if ((diff & diffMask) == android::ResTable_config::CONFIG_LOCALE) {
- // If the locales differ, but the languages are the same and
- // the locale we are matching only has a language specified,
- // we match.
- if (config.language[0] &&
- memcmp(config.language, target.language, sizeof(config.language)) == 0) {
- if (config.country[0] == 0) {
- matchedAxis |= android::ResTable_config::CONFIG_LOCALE;
- }
- }
- } else if ((diff & diffMask) == android::ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE) {
- // Special case if the smallest screen width doesn't match. We check that the
- // config being matched has a smaller screen width than the filter specified.
- if (config.smallestScreenWidthDp != 0 &&
- config.smallestScreenWidthDp < target.smallestScreenWidthDp) {
- matchedAxis |= android::ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE;
- }
+ uint32_t matched_axis = 0;
+ for (const auto& entry : configs_) {
+ const ConfigDescription& target = entry.first;
+ const uint32_t diff_mask = entry.second;
+ uint32_t diff = target.diff(config);
+ if ((diff & diff_mask) == 0) {
+ // Mark the axis that was matched.
+ matched_axis |= diff_mask;
+ } else if ((diff & diff_mask) == android::ResTable_config::CONFIG_LOCALE) {
+ // If the locales differ, but the languages are the same and
+ // the locale we are matching only has a language specified,
+ // we match.
+ if (config.language[0] &&
+ memcmp(config.language, target.language, sizeof(config.language)) ==
+ 0) {
+ if (config.country[0] == 0) {
+ matched_axis |= android::ResTable_config::CONFIG_LOCALE;
}
+ }
+ } else if ((diff & diff_mask) ==
+ android::ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE) {
+ // Special case if the smallest screen width doesn't match. We check that
+ // the
+ // config being matched has a smaller screen width than the filter
+ // specified.
+ if (config.smallestScreenWidthDp != 0 &&
+ config.smallestScreenWidthDp < target.smallestScreenWidthDp) {
+ matched_axis |= android::ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE;
+ }
}
- return matchedAxis == (mConfigMask & mask);
+ }
+ return matched_axis == (config_mask_ & mask);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/filter/ConfigFilter.h b/tools/aapt2/filter/ConfigFilter.h
index 36e9c44..3f13416 100644
--- a/tools/aapt2/filter/ConfigFilter.h
+++ b/tools/aapt2/filter/ConfigFilter.h
@@ -17,45 +17,48 @@
#ifndef AAPT_FILTER_CONFIGFILTER_H
#define AAPT_FILTER_CONFIGFILTER_H
-#include "ConfigDescription.h"
-
#include <set>
#include <utility>
+#include "ConfigDescription.h"
+
namespace aapt {
/**
* Matches ConfigDescriptions based on some pattern.
*/
class IConfigFilter {
-public:
- virtual ~IConfigFilter() = default;
+ public:
+ virtual ~IConfigFilter() = default;
- /**
- * Returns true if the filter matches the configuration, false otherwise.
- */
- virtual bool match(const ConfigDescription& config) const = 0;
+ /**
+ * Returns true if the filter matches the configuration, false otherwise.
+ */
+ virtual bool Match(const ConfigDescription& config) const = 0;
};
/**
- * Implements config axis matching. An axis is one component of a configuration, like screen
- * density or locale. If an axis is specified in the filter, and the axis is specified in
- * the configuration to match, they must be compatible. Otherwise the configuration to match is
+ * Implements config axis matching. An axis is one component of a configuration,
+ * like screen
+ * density or locale. If an axis is specified in the filter, and the axis is
+ * specified in
+ * the configuration to match, they must be compatible. Otherwise the
+ * configuration to match is
* accepted.
*
* Used when handling "-c" options.
*/
class AxisConfigFilter : public IConfigFilter {
-public:
- void addConfig(ConfigDescription config);
+ public:
+ void AddConfig(ConfigDescription config);
- bool match(const ConfigDescription& config) const override;
+ bool Match(const ConfigDescription& config) const override;
-private:
- std::set<std::pair<ConfigDescription, uint32_t>> mConfigs;
- uint32_t mConfigMask = 0;
+ private:
+ std::set<std::pair<ConfigDescription, uint32_t>> configs_;
+ uint32_t config_mask_ = 0;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FILTER_CONFIGFILTER_H */
diff --git a/tools/aapt2/filter/ConfigFilter_test.cpp b/tools/aapt2/filter/ConfigFilter_test.cpp
index f6b4955..586dd5f 100644
--- a/tools/aapt2/filter/ConfigFilter_test.cpp
+++ b/tools/aapt2/filter/ConfigFilter_test.cpp
@@ -15,98 +15,98 @@
*/
#include "filter/ConfigFilter.h"
-#include "test/Common.h"
-#include <gtest/gtest.h>
+#include "test/Test.h"
namespace aapt {
TEST(ConfigFilterTest, EmptyFilterMatchesAnything) {
- AxisConfigFilter filter;
+ AxisConfigFilter filter;
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("320dpi")));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr")));
+ EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("320dpi")));
+ EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("fr")));
}
TEST(ConfigFilterTest, MatchesConfigWithUnrelatedAxis) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr"));
+ AxisConfigFilter filter;
+ filter.AddConfig(test::ParseConfigOrDie("fr"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("320dpi")));
+ EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("320dpi")));
}
TEST(ConfigFilterTest, MatchesConfigWithSameValueAxis) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr"));
+ AxisConfigFilter filter;
+ filter.AddConfig(test::ParseConfigOrDie("fr"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr")));
+ EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("fr")));
}
TEST(ConfigFilterTest, MatchesConfigWithSameValueAxisAndOtherUnrelatedAxis) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr"));
+ AxisConfigFilter filter;
+ filter.AddConfig(test::ParseConfigOrDie("fr"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr-320dpi")));
+ EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("fr-320dpi")));
}
TEST(ConfigFilterTest, MatchesConfigWithOneMatchingAxis) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr-rFR"));
- filter.addConfig(test::parseConfigOrDie("sw360dp"));
- filter.addConfig(test::parseConfigOrDie("normal"));
- filter.addConfig(test::parseConfigOrDie("en-rUS"));
+ AxisConfigFilter filter;
+ filter.AddConfig(test::ParseConfigOrDie("fr-rFR"));
+ filter.AddConfig(test::ParseConfigOrDie("sw360dp"));
+ filter.AddConfig(test::ParseConfigOrDie("normal"));
+ filter.AddConfig(test::ParseConfigOrDie("en-rUS"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("en")));
+ EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("en")));
}
TEST(ConfigFilterTest, DoesNotMatchConfigWithDifferentValueAxis) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr"));
+ AxisConfigFilter filter;
+ filter.AddConfig(test::ParseConfigOrDie("fr"));
- EXPECT_FALSE(filter.match(test::parseConfigOrDie("de")));
+ EXPECT_FALSE(filter.Match(test::ParseConfigOrDie("de")));
}
TEST(ConfigFilterTest, DoesNotMatchWhenOneQualifierIsExplicitlyNotMatched) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr-rFR"));
- filter.addConfig(test::parseConfigOrDie("en-rUS"));
- filter.addConfig(test::parseConfigOrDie("normal"));
- filter.addConfig(test::parseConfigOrDie("large"));
- filter.addConfig(test::parseConfigOrDie("xxhdpi"));
- filter.addConfig(test::parseConfigOrDie("sw320dp"));
+ AxisConfigFilter filter;
+ filter.AddConfig(test::ParseConfigOrDie("fr-rFR"));
+ filter.AddConfig(test::ParseConfigOrDie("en-rUS"));
+ filter.AddConfig(test::ParseConfigOrDie("normal"));
+ filter.AddConfig(test::ParseConfigOrDie("large"));
+ filter.AddConfig(test::ParseConfigOrDie("xxhdpi"));
+ filter.AddConfig(test::ParseConfigOrDie("sw320dp"));
- EXPECT_FALSE(filter.match(test::parseConfigOrDie("fr-sw600dp-v13")));
+ EXPECT_FALSE(filter.Match(test::ParseConfigOrDie("fr-sw600dp-v13")));
}
TEST(ConfigFilterTest, MatchesSmallestWidthWhenSmaller) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("sw600dp"));
+ AxisConfigFilter filter;
+ filter.AddConfig(test::ParseConfigOrDie("sw600dp"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr-sw320dp-v13")));
+ EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("fr-sw320dp-v13")));
}
TEST(ConfigFilterTest, MatchesConfigWithSameLanguageButNoRegionSpecified) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("de-rDE"));
+ AxisConfigFilter filter;
+ filter.AddConfig(test::ParseConfigOrDie("de-rDE"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("de")));
+ EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("de")));
}
TEST(ConfigFilterTest, IgnoresVersion) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("normal-v4"));
+ AxisConfigFilter filter;
+ filter.AddConfig(test::ParseConfigOrDie("normal-v4"));
- // The configs don't match on any axis besides version, which should be ignored.
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("sw600dp-v13")));
+ // The configs don't match on any axis besides version, which should be
+ // ignored.
+ EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("sw600dp-v13")));
}
TEST(ConfigFilterTest, MatchesConfigWithRegion) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("kok"));
- filter.addConfig(test::parseConfigOrDie("kok-rIN"));
- filter.addConfig(test::parseConfigOrDie("kok-v419"));
+ AxisConfigFilter filter;
+ filter.AddConfig(test::ParseConfigOrDie("kok"));
+ filter.AddConfig(test::ParseConfigOrDie("kok-rIN"));
+ filter.AddConfig(test::ParseConfigOrDie("kok-v419"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("kok-rIN")));
+ EXPECT_TRUE(filter.Match(test::ParseConfigOrDie("kok-rIN")));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/Archive.cpp b/tools/aapt2/flatten/Archive.cpp
index 3a244c0..47de0a3 100644
--- a/tools/aapt2/flatten/Archive.cpp
+++ b/tools/aapt2/flatten/Archive.cpp
@@ -15,170 +15,186 @@
*/
#include "flatten/Archive.h"
-#include "util/Files.h"
-#include "util/StringPiece.h"
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
-#include <ziparchive/zip_writer.h>
+
+#include "android-base/macros.h"
+#include "ziparchive/zip_writer.h"
+
+#include "util/Files.h"
+#include "util/StringPiece.h"
namespace aapt {
namespace {
-struct DirectoryWriter : public IArchiveWriter {
- std::string mOutDir;
- std::unique_ptr<FILE, decltype(fclose)*> mFile = { nullptr, fclose };
+class DirectoryWriter : public IArchiveWriter {
+ public:
+ DirectoryWriter() = default;
- bool open(IDiagnostics* diag, const StringPiece& outDir) {
- mOutDir = outDir.toString();
- file::FileType type = file::getFileType(mOutDir);
- if (type == file::FileType::kNonexistant) {
- diag->error(DiagMessage() << "directory " << mOutDir << " does not exist");
- return false;
- } else if (type != file::FileType::kDirectory) {
- diag->error(DiagMessage() << mOutDir << " is not a directory");
- return false;
- }
- return true;
+ bool Open(IDiagnostics* diag, const StringPiece& out_dir) {
+ dir_ = out_dir.ToString();
+ file::FileType type = file::GetFileType(dir_);
+ if (type == file::FileType::kNonexistant) {
+ diag->Error(DiagMessage() << "directory " << dir_ << " does not exist");
+ return false;
+ } else if (type != file::FileType::kDirectory) {
+ diag->Error(DiagMessage() << dir_ << " is not a directory");
+ return false;
+ }
+ return true;
+ }
+
+ bool StartEntry(const StringPiece& path, uint32_t flags) override {
+ if (file_) {
+ return false;
}
- bool startEntry(const StringPiece& path, uint32_t flags) override {
- if (mFile) {
- return false;
- }
+ std::string full_path = dir_;
+ file::AppendPath(&full_path, path);
+ file::mkdirs(file::GetStem(full_path));
- std::string fullPath = mOutDir;
- file::appendPath(&fullPath, path);
- file::mkdirs(file::getStem(fullPath));
+ file_ = {fopen(full_path.data(), "wb"), fclose};
+ if (!file_) {
+ return false;
+ }
+ return true;
+ }
- mFile = { fopen(fullPath.data(), "wb"), fclose };
- if (!mFile) {
- return false;
- }
- return true;
+ bool WriteEntry(const BigBuffer& buffer) override {
+ if (!file_) {
+ return false;
}
- bool writeEntry(const BigBuffer& buffer) override {
- if (!mFile) {
- return false;
- }
-
- for (const BigBuffer::Block& b : buffer) {
- if (fwrite(b.buffer.get(), 1, b.size, mFile.get()) != b.size) {
- mFile.reset(nullptr);
- return false;
- }
- }
- return true;
+ for (const BigBuffer::Block& b : buffer) {
+ if (fwrite(b.buffer.get(), 1, b.size, file_.get()) != b.size) {
+ file_.reset(nullptr);
+ return false;
+ }
}
+ return true;
+ }
- bool writeEntry(const void* data, size_t len) override {
- if (fwrite(data, 1, len, mFile.get()) != len) {
- mFile.reset(nullptr);
- return false;
- }
- return true;
+ bool WriteEntry(const void* data, size_t len) override {
+ if (fwrite(data, 1, len, file_.get()) != len) {
+ file_.reset(nullptr);
+ return false;
}
+ return true;
+ }
- bool finishEntry() override {
- if (!mFile) {
- return false;
- }
- mFile.reset(nullptr);
- return true;
+ bool FinishEntry() override {
+ if (!file_) {
+ return false;
}
+ file_.reset(nullptr);
+ return true;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
+
+ std::string dir_;
+ std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
};
-struct ZipFileWriter : public IArchiveWriter {
- std::unique_ptr<FILE, decltype(fclose)*> mFile = { nullptr, fclose };
- std::unique_ptr<ZipWriter> mWriter;
+class ZipFileWriter : public IArchiveWriter {
+ public:
+ ZipFileWriter() = default;
- bool open(IDiagnostics* diag, const StringPiece& path) {
- mFile = { fopen(path.data(), "w+b"), fclose };
- if (!mFile) {
- diag->error(DiagMessage() << "failed to open " << path << ": " << strerror(errno));
- return false;
- }
- mWriter = util::make_unique<ZipWriter>(mFile.get());
- return true;
+ bool Open(IDiagnostics* diag, const StringPiece& path) {
+ file_ = {fopen(path.data(), "w+b"), fclose};
+ if (!file_) {
+ diag->Error(DiagMessage() << "failed to Open " << path << ": "
+ << strerror(errno));
+ return false;
+ }
+ writer_ = util::make_unique<ZipWriter>(file_.get());
+ return true;
+ }
+
+ bool StartEntry(const StringPiece& path, uint32_t flags) override {
+ if (!writer_) {
+ return false;
}
- bool startEntry(const StringPiece& path, uint32_t flags) override {
- if (!mWriter) {
- return false;
- }
-
- size_t zipFlags = 0;
- if (flags & ArchiveEntry::kCompress) {
- zipFlags |= ZipWriter::kCompress;
- }
-
- if (flags & ArchiveEntry::kAlign) {
- zipFlags |= ZipWriter::kAlign32;
- }
-
- int32_t result = mWriter->StartEntry(path.data(), zipFlags);
- if (result != 0) {
- return false;
- }
- return true;
+ size_t zip_flags = 0;
+ if (flags & ArchiveEntry::kCompress) {
+ zip_flags |= ZipWriter::kCompress;
}
- bool writeEntry(const void* data, size_t len) override {
- int32_t result = mWriter->WriteBytes(data, len);
- if (result != 0) {
- return false;
- }
- return true;
+ if (flags & ArchiveEntry::kAlign) {
+ zip_flags |= ZipWriter::kAlign32;
}
- bool writeEntry(const BigBuffer& buffer) override {
- for (const BigBuffer::Block& b : buffer) {
- int32_t result = mWriter->WriteBytes(b.buffer.get(), b.size);
- if (result != 0) {
- return false;
- }
- }
- return true;
+ int32_t result = writer_->StartEntry(path.data(), zip_flags);
+ if (result != 0) {
+ return false;
}
+ return true;
+ }
- bool finishEntry() override {
- int32_t result = mWriter->FinishEntry();
- if (result != 0) {
- return false;
- }
- return true;
+ bool WriteEntry(const void* data, size_t len) override {
+ int32_t result = writer_->WriteBytes(data, len);
+ if (result != 0) {
+ return false;
}
+ return true;
+ }
- virtual ~ZipFileWriter() {
- if (mWriter) {
- mWriter->Finish();
- }
+ bool WriteEntry(const BigBuffer& buffer) override {
+ for (const BigBuffer::Block& b : buffer) {
+ int32_t result = writer_->WriteBytes(b.buffer.get(), b.size);
+ if (result != 0) {
+ return false;
+ }
}
+ return true;
+ }
+
+ bool FinishEntry() override {
+ int32_t result = writer_->FinishEntry();
+ if (result != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ virtual ~ZipFileWriter() {
+ if (writer_) {
+ writer_->Finish();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ZipFileWriter);
+
+ std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
+ std::unique_ptr<ZipWriter> writer_;
};
-} // namespace
+} // namespace
-std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(IDiagnostics* diag,
- const StringPiece& path) {
-
- std::unique_ptr<DirectoryWriter> writer = util::make_unique<DirectoryWriter>();
- if (!writer->open(diag, path)) {
- return {};
- }
- return std::move(writer);
+std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(
+ IDiagnostics* diag, const StringPiece& path) {
+ std::unique_ptr<DirectoryWriter> writer =
+ util::make_unique<DirectoryWriter>();
+ if (!writer->Open(diag, path)) {
+ return {};
+ }
+ return std::move(writer);
}
-std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(IDiagnostics* diag,
- const StringPiece& path) {
- std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
- if (!writer->open(diag, path)) {
- return {};
- }
- return std::move(writer);
+std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(
+ IDiagnostics* diag, const StringPiece& path) {
+ std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
+ if (!writer->Open(diag, path)) {
+ return {};
+ }
+ return std::move(writer);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/Archive.h b/tools/aapt2/flatten/Archive.h
index 96d8512..4fcb3ff 100644
--- a/tools/aapt2/flatten/Archive.h
+++ b/tools/aapt2/flatten/Archive.h
@@ -17,51 +17,52 @@
#ifndef AAPT_FLATTEN_ARCHIVE_H
#define AAPT_FLATTEN_ARCHIVE_H
-#include "Diagnostics.h"
-#include "util/BigBuffer.h"
-#include "util/Files.h"
-#include "util/StringPiece.h"
-
-#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <fstream>
#include <memory>
#include <string>
#include <vector>
+#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+
+#include "Diagnostics.h"
+#include "util/BigBuffer.h"
+#include "util/Files.h"
+#include "util/StringPiece.h"
+
namespace aapt {
struct ArchiveEntry {
- enum : uint32_t {
- kCompress = 0x01,
- kAlign = 0x02,
- };
+ enum : uint32_t {
+ kCompress = 0x01,
+ kAlign = 0x02,
+ };
- std::string path;
- uint32_t flags;
- size_t uncompressedSize;
+ std::string path;
+ uint32_t flags;
+ size_t uncompressed_size;
};
class IArchiveWriter : public google::protobuf::io::CopyingOutputStream {
-public:
- virtual ~IArchiveWriter() = default;
+ public:
+ virtual ~IArchiveWriter() = default;
- virtual bool startEntry(const StringPiece& path, uint32_t flags) = 0;
- virtual bool writeEntry(const BigBuffer& buffer) = 0;
- virtual bool writeEntry(const void* data, size_t len) = 0;
- virtual bool finishEntry() = 0;
+ virtual bool StartEntry(const StringPiece& path, uint32_t flags) = 0;
+ virtual bool WriteEntry(const BigBuffer& buffer) = 0;
+ virtual bool WriteEntry(const void* data, size_t len) = 0;
+ virtual bool FinishEntry() = 0;
- // CopyingOutputStream implementations.
- bool Write(const void* buffer, int size) override {
- return writeEntry(buffer, size);
- }
+ // CopyingOutputStream implementations.
+ bool Write(const void* buffer, int size) override {
+ return WriteEntry(buffer, size);
+ }
};
-std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(IDiagnostics* diag,
- const StringPiece& path);
+std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(
+ IDiagnostics* diag, const StringPiece& path);
-std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(IDiagnostics* diag,
- const StringPiece& path);
+std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(
+ IDiagnostics* diag, const StringPiece& path);
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FLATTEN_ARCHIVE_H */
diff --git a/tools/aapt2/flatten/ChunkWriter.h b/tools/aapt2/flatten/ChunkWriter.h
index de1d87a..968d3ee 100644
--- a/tools/aapt2/flatten/ChunkWriter.h
+++ b/tools/aapt2/flatten/ChunkWriter.h
@@ -17,71 +17,64 @@
#ifndef AAPT_FLATTEN_CHUNKWRITER_H
#define AAPT_FLATTEN_CHUNKWRITER_H
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+
#include "util/BigBuffer.h"
#include "util/Util.h"
-#include <androidfw/ResourceTypes.h>
-
namespace aapt {
class ChunkWriter {
-private:
- BigBuffer* mBuffer;
- size_t mStartSize = 0;
- android::ResChunk_header* mHeader = nullptr;
+ public:
+ explicit inline ChunkWriter(BigBuffer* buffer) : buffer_(buffer) {}
+ ChunkWriter(ChunkWriter&&) = default;
+ ChunkWriter& operator=(ChunkWriter&&) = default;
-public:
- explicit inline ChunkWriter(BigBuffer* buffer) : mBuffer(buffer) {
- }
+ template <typename T>
+ inline T* StartChunk(uint16_t type) {
+ start_size_ = buffer_->size();
+ T* chunk = buffer_->NextBlock<T>();
+ header_ = &chunk->header;
+ header_->type = util::HostToDevice16(type);
+ header_->headerSize = util::HostToDevice16(sizeof(T));
+ return chunk;
+ }
- ChunkWriter(const ChunkWriter&) = delete;
- ChunkWriter& operator=(const ChunkWriter&) = delete;
- ChunkWriter(ChunkWriter&&) = default;
- ChunkWriter& operator=(ChunkWriter&&) = default;
+ template <typename T>
+ inline T* NextBlock(size_t count = 1) {
+ return buffer_->NextBlock<T>(count);
+ }
- template <typename T>
- inline T* startChunk(uint16_t type) {
- mStartSize = mBuffer->size();
- T* chunk = mBuffer->nextBlock<T>();
- mHeader = &chunk->header;
- mHeader->type = util::hostToDevice16(type);
- mHeader->headerSize = util::hostToDevice16(sizeof(T));
- return chunk;
- }
+ inline BigBuffer* buffer() { return buffer_; }
- template <typename T>
- inline T* nextBlock(size_t count = 1) {
- return mBuffer->nextBlock<T>(count);
- }
+ inline android::ResChunk_header* chunk_header() { return header_; }
- inline BigBuffer* getBuffer() {
- return mBuffer;
- }
+ inline size_t size() { return buffer_->size() - start_size_; }
- inline android::ResChunk_header* getChunkHeader() {
- return mHeader;
- }
+ inline android::ResChunk_header* Finish() {
+ buffer_->Align4();
+ header_->size = util::HostToDevice32(buffer_->size() - start_size_);
+ return header_;
+ }
- inline size_t size() {
- return mBuffer->size() - mStartSize;
- }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChunkWriter);
- inline android::ResChunk_header* finish() {
- mBuffer->align4();
- mHeader->size = util::hostToDevice32(mBuffer->size() - mStartSize);
- return mHeader;
- }
+ BigBuffer* buffer_;
+ size_t start_size_ = 0;
+ android::ResChunk_header* header_ = nullptr;
};
template <>
-inline android::ResChunk_header* ChunkWriter::startChunk(uint16_t type) {
- mStartSize = mBuffer->size();
- mHeader = mBuffer->nextBlock<android::ResChunk_header>();
- mHeader->type = util::hostToDevice16(type);
- mHeader->headerSize = util::hostToDevice16(sizeof(android::ResChunk_header));
- return mHeader;
+inline android::ResChunk_header* ChunkWriter::StartChunk(uint16_t type) {
+ start_size_ = buffer_->size();
+ header_ = buffer_->NextBlock<android::ResChunk_header>();
+ header_->type = util::HostToDevice16(type);
+ header_->headerSize = util::HostToDevice16(sizeof(android::ResChunk_header));
+ return header_;
}
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FLATTEN_CHUNKWRITER_H */
diff --git a/tools/aapt2/flatten/ResourceTypeExtensions.h b/tools/aapt2/flatten/ResourceTypeExtensions.h
index 3e20ad6..6359b41 100644
--- a/tools/aapt2/flatten/ResourceTypeExtensions.h
+++ b/tools/aapt2/flatten/ResourceTypeExtensions.h
@@ -17,20 +17,21 @@
#ifndef AAPT_RESOURCE_TYPE_EXTENSIONS_H
#define AAPT_RESOURCE_TYPE_EXTENSIONS_H
-#include <androidfw/ResourceTypes.h>
+#include "androidfw/ResourceTypes.h"
namespace aapt {
/**
- * An alternative struct to use instead of ResTable_map_entry. This one is a standard_layout
+ * An alternative struct to use instead of ResTable_map_entry. This one is a
+ * standard_layout
* struct.
*/
struct ResTable_entry_ext {
- android::ResTable_entry entry;
- android::ResTable_ref parent;
- uint32_t count;
+ android::ResTable_entry entry;
+ android::ResTable_ref parent;
+ uint32_t count;
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_RESOURCE_TYPE_EXTENSIONS_H
+#endif // AAPT_RESOURCE_TYPE_EXTENSIONS_H
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index d5067b1..19d030e 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -14,21 +14,23 @@
* limitations under the License.
*/
+#include "flatten/TableFlattener.h"
+
+#include <algorithm>
+#include <numeric>
+#include <sstream>
+#include <type_traits>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-
#include "flatten/ChunkWriter.h"
#include "flatten/ResourceTypeExtensions.h"
-#include "flatten/TableFlattener.h"
#include "util/BigBuffer.h"
-#include <android-base/macros.h>
-#include <algorithm>
-#include <sstream>
-#include <type_traits>
-#include <numeric>
-
using namespace android;
namespace aapt {
@@ -36,439 +38,458 @@
namespace {
template <typename T>
-static bool cmpIds(const T* a, const T* b) {
- return a->id.value() < b->id.value();
+static bool cmp_ids(const T* a, const T* b) {
+ return a->id.value() < b->id.value();
}
static void strcpy16_htod(uint16_t* dst, size_t len, const StringPiece16& src) {
- if (len == 0) {
- return;
- }
+ if (len == 0) {
+ return;
+ }
- size_t i;
- const char16_t* srcData = src.data();
- for (i = 0; i < len - 1 && i < src.size(); i++) {
- dst[i] = util::hostToDevice16((uint16_t) srcData[i]);
- }
- dst[i] = 0;
+ size_t i;
+ const char16_t* src_data = src.data();
+ for (i = 0; i < len - 1 && i < src.size(); i++) {
+ dst[i] = util::HostToDevice16((uint16_t)src_data[i]);
+ }
+ dst[i] = 0;
}
-static bool cmpStyleEntries(const Style::Entry& a, const Style::Entry& b) {
- if (a.key.id) {
- if (b.key.id) {
- return a.key.id.value() < b.key.id.value();
- }
- return true;
- } else if (!b.key.id) {
- return a.key.name.value() < b.key.name.value();
- }
- return false;
+static bool cmp_style_entries(const Style::Entry& a, const Style::Entry& b) {
+ if (a.key.id) {
+ if (b.key.id) {
+ return a.key.id.value() < b.key.id.value();
+ }
+ return true;
+ } else if (!b.key.id) {
+ return a.key.name.value() < b.key.name.value();
+ }
+ return false;
}
struct FlatEntry {
- ResourceEntry* entry;
- Value* value;
+ ResourceEntry* entry;
+ Value* value;
- // The entry string pool index to the entry's name.
- uint32_t entryKey;
+ // The entry string pool index to the entry's name.
+ uint32_t entry_key;
};
class MapFlattenVisitor : public RawValueVisitor {
-public:
- using RawValueVisitor::visit;
+ public:
+ using RawValueVisitor::Visit;
- MapFlattenVisitor(ResTable_entry_ext* outEntry, BigBuffer* buffer) :
- mOutEntry(outEntry), mBuffer(buffer) {
+ MapFlattenVisitor(ResTable_entry_ext* out_entry, BigBuffer* buffer)
+ : out_entry_(out_entry), buffer_(buffer) {}
+
+ void Visit(Attribute* attr) override {
+ {
+ Reference key = Reference(ResourceId(ResTable_map::ATTR_TYPE));
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->type_mask);
+ FlattenEntry(&key, &val);
}
- void visit(Attribute* attr) override {
- {
- Reference key = Reference(ResourceId(ResTable_map::ATTR_TYPE));
- BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->typeMask);
- flattenEntry(&key, &val);
- }
-
- if (attr->minInt != std::numeric_limits<int32_t>::min()) {
- Reference key = Reference(ResourceId(ResTable_map::ATTR_MIN));
- BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->minInt));
- flattenEntry(&key, &val);
- }
-
- if (attr->maxInt != std::numeric_limits<int32_t>::max()) {
- Reference key = Reference(ResourceId(ResTable_map::ATTR_MAX));
- BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->maxInt));
- flattenEntry(&key, &val);
- }
-
- for (Attribute::Symbol& s : attr->symbols) {
- BinaryPrimitive val(Res_value::TYPE_INT_DEC, s.value);
- flattenEntry(&s.symbol, &val);
- }
+ if (attr->min_int != std::numeric_limits<int32_t>::min()) {
+ Reference key = Reference(ResourceId(ResTable_map::ATTR_MIN));
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC,
+ static_cast<uint32_t>(attr->min_int));
+ FlattenEntry(&key, &val);
}
- void visit(Style* style) override {
- if (style->parent) {
- const Reference& parentRef = style->parent.value();
- assert(parentRef.id && "parent has no ID");
- mOutEntry->parent.ident = util::hostToDevice32(parentRef.id.value().id);
- }
-
- // Sort the style.
- std::sort(style->entries.begin(), style->entries.end(), cmpStyleEntries);
-
- for (Style::Entry& entry : style->entries) {
- flattenEntry(&entry.key, entry.value.get());
- }
+ if (attr->max_int != std::numeric_limits<int32_t>::max()) {
+ Reference key = Reference(ResourceId(ResTable_map::ATTR_MAX));
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC,
+ static_cast<uint32_t>(attr->max_int));
+ FlattenEntry(&key, &val);
}
- void visit(Styleable* styleable) override {
- for (auto& attrRef : styleable->entries) {
- BinaryPrimitive val(Res_value{});
- flattenEntry(&attrRef, &val);
- }
+ for (Attribute::Symbol& s : attr->symbols) {
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, s.value);
+ FlattenEntry(&s.symbol, &val);
+ }
+ }
+ void Visit(Style* style) override {
+ if (style->parent) {
+ const Reference& parent_ref = style->parent.value();
+ CHECK(bool(parent_ref.id)) << "parent has no ID";
+ out_entry_->parent.ident = util::HostToDevice32(parent_ref.id.value().id);
}
- void visit(Array* array) override {
- for (auto& item : array->items) {
- ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
- flattenValue(item.get(), outEntry);
- outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
- mEntryCount++;
- }
+ // Sort the style.
+ std::sort(style->entries.begin(), style->entries.end(), cmp_style_entries);
+
+ for (Style::Entry& entry : style->entries) {
+ FlattenEntry(&entry.key, entry.value.get());
}
+ }
- void visit(Plural* plural) override {
- const size_t count = plural->values.size();
- for (size_t i = 0; i < count; i++) {
- if (!plural->values[i]) {
- continue;
- }
-
- ResourceId q;
- switch (i) {
- case Plural::Zero:
- q.id = android::ResTable_map::ATTR_ZERO;
- break;
-
- case Plural::One:
- q.id = android::ResTable_map::ATTR_ONE;
- break;
-
- case Plural::Two:
- q.id = android::ResTable_map::ATTR_TWO;
- break;
-
- case Plural::Few:
- q.id = android::ResTable_map::ATTR_FEW;
- break;
-
- case Plural::Many:
- q.id = android::ResTable_map::ATTR_MANY;
- break;
-
- case Plural::Other:
- q.id = android::ResTable_map::ATTR_OTHER;
- break;
-
- default:
- assert(false);
- break;
- }
-
- Reference key(q);
- flattenEntry(&key, plural->values[i].get());
- }
+ void Visit(Styleable* styleable) override {
+ for (auto& attr_ref : styleable->entries) {
+ BinaryPrimitive val(Res_value{});
+ FlattenEntry(&attr_ref, &val);
}
+ }
- /**
- * Call this after visiting a Value. This will finish any work that
- * needs to be done to prepare the entry.
- */
- void finish() {
- mOutEntry->count = util::hostToDevice32(mEntryCount);
+ void Visit(Array* array) override {
+ for (auto& item : array->items) {
+ ResTable_map* out_entry = buffer_->NextBlock<ResTable_map>();
+ FlattenValue(item.get(), out_entry);
+ out_entry->value.size = util::HostToDevice16(sizeof(out_entry->value));
+ entry_count_++;
}
+ }
-private:
- void flattenKey(Reference* key, ResTable_map* outEntry) {
- assert(key->id && "key has no ID");
- outEntry->name.ident = util::hostToDevice32(key->id.value().id);
+ void Visit(Plural* plural) override {
+ const size_t count = plural->values.size();
+ for (size_t i = 0; i < count; i++) {
+ if (!plural->values[i]) {
+ continue;
+ }
+
+ ResourceId q;
+ switch (i) {
+ case Plural::Zero:
+ q.id = android::ResTable_map::ATTR_ZERO;
+ break;
+
+ case Plural::One:
+ q.id = android::ResTable_map::ATTR_ONE;
+ break;
+
+ case Plural::Two:
+ q.id = android::ResTable_map::ATTR_TWO;
+ break;
+
+ case Plural::Few:
+ q.id = android::ResTable_map::ATTR_FEW;
+ break;
+
+ case Plural::Many:
+ q.id = android::ResTable_map::ATTR_MANY;
+ break;
+
+ case Plural::Other:
+ q.id = android::ResTable_map::ATTR_OTHER;
+ break;
+
+ default:
+ LOG(FATAL) << "unhandled plural type";
+ break;
+ }
+
+ Reference key(q);
+ FlattenEntry(&key, plural->values[i].get());
}
+ }
- void flattenValue(Item* value, ResTable_map* outEntry) {
- bool result = value->flatten(&outEntry->value);
- assert(result && "flatten failed");
- }
+ /**
+ * Call this after visiting a Value. This will finish any work that
+ * needs to be done to prepare the entry.
+ */
+ void Finish() { out_entry_->count = util::HostToDevice32(entry_count_); }
- void flattenEntry(Reference* key, Item* value) {
- ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
- flattenKey(key, outEntry);
- flattenValue(value, outEntry);
- outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
- mEntryCount++;
- }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MapFlattenVisitor);
- ResTable_entry_ext* mOutEntry;
- BigBuffer* mBuffer;
- size_t mEntryCount = 0;
+ void FlattenKey(Reference* key, ResTable_map* out_entry) {
+ CHECK(bool(key->id)) << "key has no ID";
+ out_entry->name.ident = util::HostToDevice32(key->id.value().id);
+ }
+
+ void FlattenValue(Item* value, ResTable_map* out_entry) {
+ CHECK(value->Flatten(&out_entry->value)) << "flatten failed";
+ }
+
+ void FlattenEntry(Reference* key, Item* value) {
+ ResTable_map* out_entry = buffer_->NextBlock<ResTable_map>();
+ FlattenKey(key, out_entry);
+ FlattenValue(value, out_entry);
+ out_entry->value.size = util::HostToDevice16(sizeof(out_entry->value));
+ entry_count_++;
+ }
+
+ ResTable_entry_ext* out_entry_;
+ BigBuffer* buffer_;
+ size_t entry_count_ = 0;
};
class PackageFlattener {
-public:
- PackageFlattener(IDiagnostics* diag, ResourceTablePackage* package) :
- mDiag(diag), mPackage(package) {
+ public:
+ PackageFlattener(IDiagnostics* diag, ResourceTablePackage* package)
+ : diag_(diag), package_(package) {}
+
+ bool FlattenPackage(BigBuffer* buffer) {
+ ChunkWriter pkg_writer(buffer);
+ ResTable_package* pkg_header =
+ pkg_writer.StartChunk<ResTable_package>(RES_TABLE_PACKAGE_TYPE);
+ pkg_header->id = util::HostToDevice32(package_->id.value());
+
+ if (package_->name.size() >= arraysize(pkg_header->name)) {
+ diag_->Error(DiagMessage() << "package name '" << package_->name
+ << "' is too long");
+ return false;
}
- bool flattenPackage(BigBuffer* buffer) {
- ChunkWriter pkgWriter(buffer);
- ResTable_package* pkgHeader = pkgWriter.startChunk<ResTable_package>(
- RES_TABLE_PACKAGE_TYPE);
- pkgHeader->id = util::hostToDevice32(mPackage->id.value());
+ // Copy the package name in device endianness.
+ strcpy16_htod(pkg_header->name, arraysize(pkg_header->name),
+ util::Utf8ToUtf16(package_->name));
- if (mPackage->name.size() >= arraysize(pkgHeader->name)) {
- mDiag->error(DiagMessage() <<
- "package name '" << mPackage->name << "' is too long");
- return false;
- }
+ // Serialize the types. We do this now so that our type and key strings
+ // are populated. We write those first.
+ BigBuffer type_buffer(1024);
+ FlattenTypes(&type_buffer);
- // Copy the package name in device endianness.
- strcpy16_htod(pkgHeader->name, arraysize(pkgHeader->name),
- util::utf8ToUtf16(mPackage->name));
+ pkg_header->typeStrings = util::HostToDevice32(pkg_writer.size());
+ StringPool::FlattenUtf16(pkg_writer.buffer(), type_pool_);
- // Serialize the types. We do this now so that our type and key strings
- // are populated. We write those first.
- BigBuffer typeBuffer(1024);
- flattenTypes(&typeBuffer);
+ pkg_header->keyStrings = util::HostToDevice32(pkg_writer.size());
+ StringPool::FlattenUtf8(pkg_writer.buffer(), key_pool_);
- pkgHeader->typeStrings = util::hostToDevice32(pkgWriter.size());
- StringPool::flattenUtf16(pkgWriter.getBuffer(), mTypePool);
+ // Append the types.
+ buffer->AppendBuffer(std::move(type_buffer));
- pkgHeader->keyStrings = util::hostToDevice32(pkgWriter.size());
- StringPool::flattenUtf8(pkgWriter.getBuffer(), mKeyPool);
+ pkg_writer.Finish();
+ return true;
+ }
- // Append the types.
- buffer->appendBuffer(std::move(typeBuffer));
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PackageFlattener);
- pkgWriter.finish();
- return true;
- }
-
-private:
- IDiagnostics* mDiag;
- ResourceTablePackage* mPackage;
- StringPool mTypePool;
- StringPool mKeyPool;
-
- template <typename T, bool IsItem>
- T* writeEntry(FlatEntry* entry, BigBuffer* buffer) {
- static_assert(std::is_same<ResTable_entry, T>::value ||
+ template <typename T, bool IsItem>
+ T* WriteEntry(FlatEntry* entry, BigBuffer* buffer) {
+ static_assert(std::is_same<ResTable_entry, T>::value ||
std::is_same<ResTable_entry_ext, T>::value,
- "T must be ResTable_entry or ResTable_entry_ext");
+ "T must be ResTable_entry or ResTable_entry_ext");
- T* result = buffer->nextBlock<T>();
- ResTable_entry* outEntry = (ResTable_entry*)(result);
- if (entry->entry->symbolStatus.state == SymbolState::kPublic) {
- outEntry->flags |= ResTable_entry::FLAG_PUBLIC;
- }
-
- if (entry->value->isWeak()) {
- outEntry->flags |= ResTable_entry::FLAG_WEAK;
- }
-
- if (!IsItem) {
- outEntry->flags |= ResTable_entry::FLAG_COMPLEX;
- }
-
- outEntry->flags = util::hostToDevice16(outEntry->flags);
- outEntry->key.index = util::hostToDevice32(entry->entryKey);
- outEntry->size = util::hostToDevice16(sizeof(T));
- return result;
+ T* result = buffer->NextBlock<T>();
+ ResTable_entry* out_entry = (ResTable_entry*)result;
+ if (entry->entry->symbol_status.state == SymbolState::kPublic) {
+ out_entry->flags |= ResTable_entry::FLAG_PUBLIC;
}
- bool flattenValue(FlatEntry* entry, BigBuffer* buffer) {
- if (Item* item = valueCast<Item>(entry->value)) {
- writeEntry<ResTable_entry, true>(entry, buffer);
- Res_value* outValue = buffer->nextBlock<Res_value>();
- bool result = item->flatten(outValue);
- assert(result && "flatten failed");
- outValue->size = util::hostToDevice16(sizeof(*outValue));
- } else {
- ResTable_entry_ext* outEntry = writeEntry<ResTable_entry_ext, false>(entry, buffer);
- MapFlattenVisitor visitor(outEntry, buffer);
- entry->value->accept(&visitor);
- visitor.finish();
- }
- return true;
+ if (entry->value->IsWeak()) {
+ out_entry->flags |= ResTable_entry::FLAG_WEAK;
}
- bool flattenConfig(const ResourceTableType* type, const ConfigDescription& config,
- std::vector<FlatEntry>* entries, BigBuffer* buffer) {
- ChunkWriter typeWriter(buffer);
- ResTable_type* typeHeader = typeWriter.startChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
- typeHeader->id = type->id.value();
- typeHeader->config = config;
- typeHeader->config.swapHtoD();
-
- auto maxAccum = [](uint32_t max, const std::unique_ptr<ResourceEntry>& a) -> uint32_t {
- return std::max(max, (uint32_t) a->id.value());
- };
-
- // Find the largest entry ID. That is how many entries we will have.
- const uint32_t entryCount =
- std::accumulate(type->entries.begin(), type->entries.end(), 0, maxAccum) + 1;
-
- typeHeader->entryCount = util::hostToDevice32(entryCount);
- uint32_t* indices = typeWriter.nextBlock<uint32_t>(entryCount);
-
- assert((size_t) entryCount <= std::numeric_limits<uint16_t>::max() + 1);
- memset(indices, 0xff, entryCount * sizeof(uint32_t));
-
- typeHeader->entriesStart = util::hostToDevice32(typeWriter.size());
-
- const size_t entryStart = typeWriter.getBuffer()->size();
- for (FlatEntry& flatEntry : *entries) {
- assert(flatEntry.entry->id.value() < entryCount);
- indices[flatEntry.entry->id.value()] = util::hostToDevice32(
- typeWriter.getBuffer()->size() - entryStart);
- if (!flattenValue(&flatEntry, typeWriter.getBuffer())) {
- mDiag->error(DiagMessage()
- << "failed to flatten resource '"
- << ResourceNameRef(mPackage->name, type->type, flatEntry.entry->name)
- << "' for configuration '" << config << "'");
- return false;
- }
- }
- typeWriter.finish();
- return true;
+ if (!IsItem) {
+ out_entry->flags |= ResTable_entry::FLAG_COMPLEX;
}
- std::vector<ResourceTableType*> collectAndSortTypes() {
- std::vector<ResourceTableType*> sortedTypes;
- for (auto& type : mPackage->types) {
- if (type->type == ResourceType::kStyleable) {
- // Styleables aren't real Resource Types, they are represented in the R.java
- // file.
- continue;
- }
+ out_entry->flags = util::HostToDevice16(out_entry->flags);
+ out_entry->key.index = util::HostToDevice32(entry->entry_key);
+ out_entry->size = util::HostToDevice16(sizeof(T));
+ return result;
+ }
- assert(type->id && "type must have an ID set");
+ bool FlattenValue(FlatEntry* entry, BigBuffer* buffer) {
+ if (Item* item = ValueCast<Item>(entry->value)) {
+ WriteEntry<ResTable_entry, true>(entry, buffer);
+ Res_value* outValue = buffer->NextBlock<Res_value>();
+ CHECK(item->Flatten(outValue)) << "flatten failed";
+ outValue->size = util::HostToDevice16(sizeof(*outValue));
+ } else {
+ ResTable_entry_ext* out_entry =
+ WriteEntry<ResTable_entry_ext, false>(entry, buffer);
+ MapFlattenVisitor visitor(out_entry, buffer);
+ entry->value->Accept(&visitor);
+ visitor.Finish();
+ }
+ return true;
+ }
- sortedTypes.push_back(type.get());
- }
- std::sort(sortedTypes.begin(), sortedTypes.end(), cmpIds<ResourceTableType>);
- return sortedTypes;
+ bool FlattenConfig(const ResourceTableType* type,
+ const ConfigDescription& config,
+ std::vector<FlatEntry>* entries, BigBuffer* buffer) {
+ ChunkWriter type_writer(buffer);
+ ResTable_type* type_header =
+ type_writer.StartChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
+ type_header->id = type->id.value();
+ type_header->config = config;
+ type_header->config.swapHtoD();
+
+ auto max_accum = [](uint32_t max,
+ const std::unique_ptr<ResourceEntry>& a) -> uint32_t {
+ return std::max(max, (uint32_t)a->id.value());
+ };
+
+ // Find the largest entry ID. That is how many entries we will have.
+ const uint32_t entry_count =
+ std::accumulate(type->entries.begin(), type->entries.end(), 0,
+ max_accum) +
+ 1;
+
+ type_header->entryCount = util::HostToDevice32(entry_count);
+ uint32_t* indices = type_writer.NextBlock<uint32_t>(entry_count);
+
+ CHECK((size_t)entry_count <= std::numeric_limits<uint16_t>::max());
+ memset(indices, 0xff, entry_count * sizeof(uint32_t));
+
+ type_header->entriesStart = util::HostToDevice32(type_writer.size());
+
+ const size_t entry_start = type_writer.buffer()->size();
+ for (FlatEntry& flat_entry : *entries) {
+ CHECK(flat_entry.entry->id.value() < entry_count);
+ indices[flat_entry.entry->id.value()] =
+ util::HostToDevice32(type_writer.buffer()->size() - entry_start);
+ if (!FlattenValue(&flat_entry, type_writer.buffer())) {
+ diag_->Error(DiagMessage()
+ << "failed to flatten resource '"
+ << ResourceNameRef(package_->name, type->type,
+ flat_entry.entry->name)
+ << "' for configuration '" << config << "'");
+ return false;
+ }
+ }
+ type_writer.Finish();
+ return true;
+ }
+
+ std::vector<ResourceTableType*> CollectAndSortTypes() {
+ std::vector<ResourceTableType*> sorted_types;
+ for (auto& type : package_->types) {
+ if (type->type == ResourceType::kStyleable) {
+ // Styleables aren't real Resource Types, they are represented in the
+ // R.java file.
+ continue;
+ }
+
+ CHECK(bool(type->id)) << "type must have an ID set";
+
+ sorted_types.push_back(type.get());
+ }
+ std::sort(sorted_types.begin(), sorted_types.end(),
+ cmp_ids<ResourceTableType>);
+ return sorted_types;
+ }
+
+ std::vector<ResourceEntry*> CollectAndSortEntries(ResourceTableType* type) {
+ // Sort the entries by entry ID.
+ std::vector<ResourceEntry*> sorted_entries;
+ for (auto& entry : type->entries) {
+ CHECK(bool(entry->id)) << "entry must have an ID set";
+ sorted_entries.push_back(entry.get());
+ }
+ std::sort(sorted_entries.begin(), sorted_entries.end(),
+ cmp_ids<ResourceEntry>);
+ return sorted_entries;
+ }
+
+ bool FlattenTypeSpec(ResourceTableType* type,
+ std::vector<ResourceEntry*>* sorted_entries,
+ BigBuffer* buffer) {
+ ChunkWriter type_spec_writer(buffer);
+ ResTable_typeSpec* spec_header =
+ type_spec_writer.StartChunk<ResTable_typeSpec>(
+ RES_TABLE_TYPE_SPEC_TYPE);
+ spec_header->id = type->id.value();
+
+ if (sorted_entries->empty()) {
+ type_spec_writer.Finish();
+ return true;
}
- std::vector<ResourceEntry*> collectAndSortEntries(ResourceTableType* type) {
- // Sort the entries by entry ID.
- std::vector<ResourceEntry*> sortedEntries;
- for (auto& entry : type->entries) {
- assert(entry->id && "entry must have an ID set");
- sortedEntries.push_back(entry.get());
+ // We can't just take the size of the vector. There may be holes in the
+ // entry ID space.
+ // Since the entries are sorted by ID, the last one will be the biggest.
+ const size_t num_entries = sorted_entries->back()->id.value() + 1;
+
+ spec_header->entryCount = util::HostToDevice32(num_entries);
+
+ // Reserve space for the masks of each resource in this type. These
+ // show for which configuration axis the resource changes.
+ uint32_t* config_masks = type_spec_writer.NextBlock<uint32_t>(num_entries);
+
+ const size_t actual_num_entries = sorted_entries->size();
+ for (size_t entryIndex = 0; entryIndex < actual_num_entries; entryIndex++) {
+ ResourceEntry* entry = sorted_entries->at(entryIndex);
+
+ // Populate the config masks for this entry.
+
+ if (entry->symbol_status.state == SymbolState::kPublic) {
+ config_masks[entry->id.value()] |=
+ util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+ }
+
+ const size_t config_count = entry->values.size();
+ for (size_t i = 0; i < config_count; i++) {
+ const ConfigDescription& config = entry->values[i]->config;
+ for (size_t j = i + 1; j < config_count; j++) {
+ config_masks[entry->id.value()] |=
+ util::HostToDevice32(config.diff(entry->values[j]->config));
}
- std::sort(sortedEntries.begin(), sortedEntries.end(), cmpIds<ResourceEntry>);
- return sortedEntries;
+ }
}
+ type_spec_writer.Finish();
+ return true;
+ }
- bool flattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sortedEntries,
- BigBuffer* buffer) {
- ChunkWriter typeSpecWriter(buffer);
- ResTable_typeSpec* specHeader = typeSpecWriter.startChunk<ResTable_typeSpec>(
- RES_TABLE_TYPE_SPEC_TYPE);
- specHeader->id = type->id.value();
+ bool FlattenTypes(BigBuffer* buffer) {
+ // Sort the types by their IDs. They will be inserted into the StringPool in
+ // this order.
+ std::vector<ResourceTableType*> sorted_types = CollectAndSortTypes();
- if (sortedEntries->empty()) {
- typeSpecWriter.finish();
- return true;
+ size_t expected_type_id = 1;
+ for (ResourceTableType* type : sorted_types) {
+ // If there is a gap in the type IDs, fill in the StringPool
+ // with empty values until we reach the ID we expect.
+ while (type->id.value() > expected_type_id) {
+ std::stringstream type_name;
+ type_name << "?" << expected_type_id;
+ type_pool_.MakeRef(type_name.str());
+ expected_type_id++;
+ }
+ expected_type_id++;
+ type_pool_.MakeRef(ToString(type->type));
+
+ std::vector<ResourceEntry*> sorted_entries = CollectAndSortEntries(type);
+
+ if (!FlattenTypeSpec(type, &sorted_entries, buffer)) {
+ return false;
+ }
+
+ // The binary resource table lists resource entries for each
+ // configuration.
+ // We store them inverted, where a resource entry lists the values for
+ // each
+ // configuration available. Here we reverse this to match the binary
+ // table.
+ std::map<ConfigDescription, std::vector<FlatEntry>>
+ config_to_entry_list_map;
+ for (ResourceEntry* entry : sorted_entries) {
+ const uint32_t key_index =
+ (uint32_t)key_pool_.MakeRef(entry->name).index();
+
+ // Group values by configuration.
+ for (auto& config_value : entry->values) {
+ config_to_entry_list_map[config_value->config].push_back(
+ FlatEntry{entry, config_value->value.get(), key_index});
}
+ }
- // We can't just take the size of the vector. There may be holes in the entry ID space.
- // Since the entries are sorted by ID, the last one will be the biggest.
- const size_t numEntries = sortedEntries->back()->id.value() + 1;
-
- specHeader->entryCount = util::hostToDevice32(numEntries);
-
- // Reserve space for the masks of each resource in this type. These
- // show for which configuration axis the resource changes.
- uint32_t* configMasks = typeSpecWriter.nextBlock<uint32_t>(numEntries);
-
- const size_t actualNumEntries = sortedEntries->size();
- for (size_t entryIndex = 0; entryIndex < actualNumEntries; entryIndex++) {
- ResourceEntry* entry = sortedEntries->at(entryIndex);
-
- // Populate the config masks for this entry.
-
- if (entry->symbolStatus.state == SymbolState::kPublic) {
- configMasks[entry->id.value()] |=
- util::hostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
- }
-
- const size_t configCount = entry->values.size();
- for (size_t i = 0; i < configCount; i++) {
- const ConfigDescription& config = entry->values[i]->config;
- for (size_t j = i + 1; j < configCount; j++) {
- configMasks[entry->id.value()] |= util::hostToDevice32(
- config.diff(entry->values[j]->config));
- }
- }
+ // Flatten a configuration value.
+ for (auto& entry : config_to_entry_list_map) {
+ if (!FlattenConfig(type, entry.first, &entry.second, buffer)) {
+ return false;
}
- typeSpecWriter.finish();
- return true;
+ }
}
+ return true;
+ }
- bool flattenTypes(BigBuffer* buffer) {
- // Sort the types by their IDs. They will be inserted into the StringPool in this order.
- std::vector<ResourceTableType*> sortedTypes = collectAndSortTypes();
-
- size_t expectedTypeId = 1;
- for (ResourceTableType* type : sortedTypes) {
- // If there is a gap in the type IDs, fill in the StringPool
- // with empty values until we reach the ID we expect.
- while (type->id.value() > expectedTypeId) {
- std::stringstream typeName;
- typeName << "?" << expectedTypeId;
- mTypePool.makeRef(typeName.str());
- expectedTypeId++;
- }
- expectedTypeId++;
- mTypePool.makeRef(toString(type->type));
-
- std::vector<ResourceEntry*> sortedEntries = collectAndSortEntries(type);
-
- if (!flattenTypeSpec(type, &sortedEntries, buffer)) {
- return false;
- }
-
- // The binary resource table lists resource entries for each configuration.
- // We store them inverted, where a resource entry lists the values for each
- // configuration available. Here we reverse this to match the binary table.
- std::map<ConfigDescription, std::vector<FlatEntry>> configToEntryListMap;
- for (ResourceEntry* entry : sortedEntries) {
- const uint32_t keyIndex = (uint32_t) mKeyPool.makeRef(entry->name).getIndex();
-
- // Group values by configuration.
- for (auto& configValue : entry->values) {
- configToEntryListMap[configValue->config].push_back(FlatEntry{
- entry, configValue->value.get(), keyIndex });
- }
- }
-
- // Flatten a configuration value.
- for (auto& entry : configToEntryListMap) {
- if (!flattenConfig(type, entry.first, &entry.second, buffer)) {
- return false;
- }
- }
- }
- return true;
- }
+ IDiagnostics* diag_;
+ ResourceTablePackage* package_;
+ StringPool type_pool_;
+ StringPool key_pool_;
};
-} // namespace
+} // namespace
-bool TableFlattener::consume(IAaptContext* context, ResourceTable* table) {
- // We must do this before writing the resources, since the string pool IDs may change.
- table->stringPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
+ // We must do this before writing the resources, since the string pool IDs may
+ // change.
+ table->string_pool.Sort(
+ [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
int diff = a.context.priority - b.context.priority;
if (diff < 0) return true;
if (diff > 0) return false;
@@ -476,31 +497,32 @@
if (diff < 0) return true;
if (diff > 0) return false;
return a.value < b.value;
- });
- table->stringPool.prune();
+ });
+ table->string_pool.Prune();
- // Write the ResTable header.
- ChunkWriter tableWriter(mBuffer);
- ResTable_header* tableHeader = tableWriter.startChunk<ResTable_header>(RES_TABLE_TYPE);
- tableHeader->packageCount = util::hostToDevice32(table->packages.size());
+ // Write the ResTable header.
+ ChunkWriter table_writer(buffer_);
+ ResTable_header* table_header =
+ table_writer.StartChunk<ResTable_header>(RES_TABLE_TYPE);
+ table_header->packageCount = util::HostToDevice32(table->packages.size());
- // Flatten the values string pool.
- StringPool::flattenUtf8(tableWriter.getBuffer(), table->stringPool);
+ // Flatten the values string pool.
+ StringPool::FlattenUtf8(table_writer.buffer(), table->string_pool);
- BigBuffer packageBuffer(1024);
+ BigBuffer package_buffer(1024);
- // Flatten each package.
- for (auto& package : table->packages) {
- PackageFlattener flattener(context->getDiagnostics(), package.get());
- if (!flattener.flattenPackage(&packageBuffer)) {
- return false;
- }
+ // Flatten each package.
+ for (auto& package : table->packages) {
+ PackageFlattener flattener(context->GetDiagnostics(), package.get());
+ if (!flattener.FlattenPackage(&package_buffer)) {
+ return false;
}
+ }
- // Finally merge all the packages into the main buffer.
- tableWriter.getBuffer()->appendBuffer(std::move(packageBuffer));
- tableWriter.finish();
- return true;
+ // Finally merge all the packages into the main buffer.
+ table_writer.buffer()->AppendBuffer(std::move(package_buffer));
+ table_writer.Finish();
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/flatten/TableFlattener.h
index b416f20..53f52c2 100644
--- a/tools/aapt2/flatten/TableFlattener.h
+++ b/tools/aapt2/flatten/TableFlattener.h
@@ -17,24 +17,26 @@
#ifndef AAPT_FLATTEN_TABLEFLATTENER_H
#define AAPT_FLATTEN_TABLEFLATTENER_H
+#include "android-base/macros.h"
+
+#include "ResourceTable.h"
#include "process/IResourceTableConsumer.h"
+#include "util/BigBuffer.h"
namespace aapt {
-class BigBuffer;
-class ResourceTable;
-
class TableFlattener : public IResourceTableConsumer {
-public:
- explicit TableFlattener(BigBuffer* buffer) : mBuffer(buffer) {
- }
+ public:
+ explicit TableFlattener(BigBuffer* buffer) : buffer_(buffer) {}
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
-private:
- BigBuffer* mBuffer;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TableFlattener);
+
+ BigBuffer* buffer_;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FLATTEN_TABLEFLATTENER_H */
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp
index b25bfa7..c726240 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/flatten/TableFlattener_test.cpp
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include "ResourceUtils.h"
#include "flatten/TableFlattener.h"
+
+#include "ResourceUtils.h"
#include "test/Test.h"
#include "unflatten/BinaryResourceParser.h"
#include "util/Util.h"
@@ -25,195 +26,211 @@
namespace aapt {
class TableFlattenerTest : public ::testing::Test {
-public:
- void SetUp() override {
- mContext = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setPackageId(0x7f)
- .build();
+ public:
+ void SetUp() override {
+ context_ = test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .Build();
+ }
+
+ ::testing::AssertionResult Flatten(ResourceTable* table,
+ ResTable* out_table) {
+ BigBuffer buffer(1024);
+ TableFlattener flattener(&buffer);
+ if (!flattener.Consume(context_.get(), table)) {
+ return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
}
- ::testing::AssertionResult flatten(ResourceTable* table, ResTable* outTable) {
- BigBuffer buffer(1024);
- TableFlattener flattener(&buffer);
- if (!flattener.consume(mContext.get(), table)) {
- return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
- }
+ std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+ if (out_table->add(data.get(), buffer.size(), -1, true) != NO_ERROR) {
+ return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
+ }
+ return ::testing::AssertionSuccess();
+ }
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- if (outTable->add(data.get(), buffer.size(), -1, true) != NO_ERROR) {
- return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
- }
- return ::testing::AssertionSuccess();
+ ::testing::AssertionResult Flatten(ResourceTable* table,
+ ResourceTable* out_table) {
+ BigBuffer buffer(1024);
+ TableFlattener flattener(&buffer);
+ if (!flattener.Consume(context_.get(), table)) {
+ return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
}
- ::testing::AssertionResult flatten(ResourceTable* table, ResourceTable* outTable) {
- BigBuffer buffer(1024);
- TableFlattener flattener(&buffer);
- if (!flattener.consume(mContext.get(), table)) {
- return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
- }
+ std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+ BinaryResourceParser parser(context_.get(), out_table, {}, data.get(),
+ buffer.size());
+ if (!parser.Parse()) {
+ return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
+ }
+ return ::testing::AssertionSuccess();
+ }
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- BinaryResourceParser parser(mContext.get(), outTable, {}, data.get(), buffer.size());
- if (!parser.parse()) {
- return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
- }
- return ::testing::AssertionSuccess();
+ ::testing::AssertionResult Exists(ResTable* table,
+ const StringPiece& expected_name,
+ const ResourceId& expected_id,
+ const ConfigDescription& expected_config,
+ const uint8_t expected_data_type,
+ const uint32_t expected_data,
+ const uint32_t expected_spec_flags) {
+ const ResourceName expected_res_name = test::ParseNameOrDie(expected_name);
+
+ table->setParameters(&expected_config);
+
+ ResTable_config config;
+ Res_value val;
+ uint32_t spec_flags;
+ if (table->getResource(expected_id.id, &val, false, 0, &spec_flags,
+ &config) < 0) {
+ return ::testing::AssertionFailure() << "could not find resource with";
}
- ::testing::AssertionResult exists(ResTable* table,
- const StringPiece& expectedName,
- const ResourceId& expectedId,
- const ConfigDescription& expectedConfig,
- const uint8_t expectedDataType, const uint32_t expectedData,
- const uint32_t expectedSpecFlags) {
- const ResourceName expectedResName = test::parseNameOrDie(expectedName);
-
- table->setParameters(&expectedConfig);
-
- ResTable_config config;
- Res_value val;
- uint32_t specFlags;
- if (table->getResource(expectedId.id, &val, false, 0, &specFlags, &config) < 0) {
- return ::testing::AssertionFailure() << "could not find resource with";
- }
-
- if (expectedDataType != val.dataType) {
- return ::testing::AssertionFailure()
- << "expected data type "
- << std::hex << (int) expectedDataType << " but got data type "
- << (int) val.dataType << std::dec << " instead";
- }
-
- if (expectedData != val.data) {
- return ::testing::AssertionFailure()
- << "expected data "
- << std::hex << expectedData << " but got data "
- << val.data << std::dec << " instead";
- }
-
- if (expectedSpecFlags != specFlags) {
- return ::testing::AssertionFailure()
- << "expected specFlags "
- << std::hex << expectedSpecFlags << " but got specFlags "
- << specFlags << std::dec << " instead";
- }
-
- ResTable::resource_name actualName;
- if (!table->getResourceName(expectedId.id, false, &actualName)) {
- return ::testing::AssertionFailure() << "failed to find resource name";
- }
-
- Maybe<ResourceName> resName = ResourceUtils::toResourceName(actualName);
- if (!resName) {
- return ::testing::AssertionFailure()
- << "expected name '" << expectedResName << "' but got '"
- << StringPiece16(actualName.package, actualName.packageLen)
- << ":"
- << StringPiece16(actualName.type, actualName.typeLen)
- << "/"
- << StringPiece16(actualName.name, actualName.nameLen)
- << "'";
- }
-
- if (expectedConfig != config) {
- return ::testing::AssertionFailure()
- << "expected config '" << expectedConfig << "' but got '"
- << ConfigDescription(config) << "'";
- }
- return ::testing::AssertionSuccess();
+ if (expected_data_type != val.dataType) {
+ return ::testing::AssertionFailure()
+ << "expected data type " << std::hex << (int)expected_data_type
+ << " but got data type " << (int)val.dataType << std::dec
+ << " instead";
}
-private:
- std::unique_ptr<IAaptContext> mContext;
+ if (expected_data != val.data) {
+ return ::testing::AssertionFailure()
+ << "expected data " << std::hex << expected_data
+ << " but got data " << val.data << std::dec << " instead";
+ }
+
+ if (expected_spec_flags != spec_flags) {
+ return ::testing::AssertionFailure()
+ << "expected specFlags " << std::hex << expected_spec_flags
+ << " but got specFlags " << spec_flags << std::dec << " instead";
+ }
+
+ ResTable::resource_name actual_name;
+ if (!table->getResourceName(expected_id.id, false, &actual_name)) {
+ return ::testing::AssertionFailure() << "failed to find resource name";
+ }
+
+ Maybe<ResourceName> resName = ResourceUtils::ToResourceName(actual_name);
+ if (!resName) {
+ return ::testing::AssertionFailure()
+ << "expected name '" << expected_res_name << "' but got '"
+ << StringPiece16(actual_name.package, actual_name.packageLen)
+ << ":" << StringPiece16(actual_name.type, actual_name.typeLen)
+ << "/" << StringPiece16(actual_name.name, actual_name.nameLen)
+ << "'";
+ }
+
+ if (expected_config != config) {
+ return ::testing::AssertionFailure() << "expected config '"
+ << expected_config << "' but got '"
+ << ConfigDescription(config) << "'";
+ }
+ return ::testing::AssertionSuccess();
+ }
+
+ private:
+ std::unique_ptr<IAaptContext> context_;
};
TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.test", 0x7f)
- .addSimple("com.app.test:id/one", ResourceId(0x7f020000))
- .addSimple("com.app.test:id/two", ResourceId(0x7f020001))
- .addValue("com.app.test:id/three", ResourceId(0x7f020002),
- test::buildReference("com.app.test:id/one", ResourceId(0x7f020000)))
- .addValue("com.app.test:integer/one", ResourceId(0x7f030000),
- util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
- .addValue("com.app.test:integer/one", test::parseConfigOrDie("v1"),
- ResourceId(0x7f030000),
- util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
- .addString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
- .addString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
+ .AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
+ .AddValue("com.app.test:id/three", ResourceId(0x7f020002),
+ test::BuildReference("com.app.test:id/one",
+ ResourceId(0x7f020000)))
+ .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
+ util::make_unique<BinaryPrimitive>(
+ uint8_t(Res_value::TYPE_INT_DEC), 1u))
+ .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
+ ResourceId(0x7f030000),
+ util::make_unique<BinaryPrimitive>(
+ uint8_t(Res_value::TYPE_INT_DEC), 2u))
+ .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
+ .AddString("com.app.test:layout/bar", ResourceId(0x7f050000),
+ "res/layout/bar.xml")
+ .Build();
- ResTable resTable;
- ASSERT_TRUE(flatten(table.get(), &resTable));
+ ResTable res_table;
+ ASSERT_TRUE(Flatten(table.get(), &res_table));
- EXPECT_TRUE(exists(&resTable, "com.app.test:id/one", ResourceId(0x7f020000), {},
- Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000),
+ {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(exists(&resTable, "com.app.test:id/two", ResourceId(0x7f020001), {},
- Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/two", ResourceId(0x7f020001),
+ {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(exists(&resTable, "com.app.test:id/three", ResourceId(0x7f020002), {},
- Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three",
+ ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE,
+ 0x7f020000u, 0u));
- EXPECT_TRUE(exists(&resTable, "com.app.test:integer/one", ResourceId(0x7f030000),
- {}, Res_value::TYPE_INT_DEC, 1u,
- ResTable_config::CONFIG_VERSION));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one",
+ ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
+ ResTable_config::CONFIG_VERSION));
- EXPECT_TRUE(exists(&resTable, "com.app.test:integer/one", ResourceId(0x7f030000),
- test::parseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u,
- ResTable_config::CONFIG_VERSION));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one",
+ ResourceId(0x7f030000), test::ParseConfigOrDie("v1"),
+ Res_value::TYPE_INT_DEC, 2u,
+ ResTable_config::CONFIG_VERSION));
- std::u16string fooStr = u"foo";
- ssize_t idx = resTable.getTableStringBlock(0)->indexOfString(fooStr.data(), fooStr.size());
- ASSERT_GE(idx, 0);
- EXPECT_TRUE(exists(&resTable, "com.app.test:string/test", ResourceId(0x7f040000),
- {}, Res_value::TYPE_STRING, (uint32_t) idx, 0u));
+ std::u16string foo_str = u"foo";
+ ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(),
+ foo_str.size());
+ ASSERT_GE(idx, 0);
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test",
+ ResourceId(0x7f040000), {}, Res_value::TYPE_STRING,
+ (uint32_t)idx, 0u));
- std::u16string barPath = u"res/layout/bar.xml";
- idx = resTable.getTableStringBlock(0)->indexOfString(barPath.data(), barPath.size());
- ASSERT_GE(idx, 0);
- EXPECT_TRUE(exists(&resTable, "com.app.test:layout/bar", ResourceId(0x7f050000), {},
- Res_value::TYPE_STRING, (uint32_t) idx, 0u));
+ std::u16string bar_path = u"res/layout/bar.xml";
+ idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(),
+ bar_path.size());
+ ASSERT_GE(idx, 0);
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar",
+ ResourceId(0x7f050000), {}, Res_value::TYPE_STRING,
+ (uint32_t)idx, 0u));
}
TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.test", 0x7f)
- .addSimple("com.app.test:id/one", ResourceId(0x7f020001))
- .addSimple("com.app.test:id/three", ResourceId(0x7f020003))
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddSimple("com.app.test:id/one", ResourceId(0x7f020001))
+ .AddSimple("com.app.test:id/three", ResourceId(0x7f020003))
+ .Build();
- ResTable resTable;
- ASSERT_TRUE(flatten(table.get(), &resTable));
+ ResTable res_table;
+ ASSERT_TRUE(Flatten(table.get(), &res_table));
- EXPECT_TRUE(exists(&resTable, "com.app.test:id/one", ResourceId(0x7f020001), {},
- Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(exists(&resTable, "com.app.test:id/three", ResourceId(0x7f020003), {},
- Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020001),
+ {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three",
+ ResourceId(0x7f020003), {}, Res_value::TYPE_INT_BOOLEAN,
+ 0u, 0u));
}
TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
- Attribute attr(false);
- attr.typeMask = android::ResTable_map::TYPE_INTEGER;
- attr.minInt = 10;
- attr.maxInt = 23;
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("android", 0x01)
- .addValue("android:attr/foo", ResourceId(0x01010000),
- util::make_unique<Attribute>(attr))
- .build();
+ Attribute attr(false);
+ attr.type_mask = android::ResTable_map::TYPE_INTEGER;
+ attr.min_int = 10;
+ attr.max_int = 23;
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddValue("android:attr/foo", ResourceId(0x01010000),
+ util::make_unique<Attribute>(attr))
+ .Build();
- ResourceTable result;
- ASSERT_TRUE(flatten(table.get(), &result));
+ ResourceTable result;
+ ASSERT_TRUE(Flatten(table.get(), &result));
- Attribute* actualAttr = test::getValue<Attribute>(&result, "android:attr/foo");
- ASSERT_NE(nullptr, actualAttr);
- EXPECT_EQ(attr.isWeak(), actualAttr->isWeak());
- EXPECT_EQ(attr.typeMask, actualAttr->typeMask);
- EXPECT_EQ(attr.minInt, actualAttr->minInt);
- EXPECT_EQ(attr.maxInt, actualAttr->maxInt);
+ Attribute* actualAttr =
+ test::GetValue<Attribute>(&result, "android:attr/foo");
+ ASSERT_NE(nullptr, actualAttr);
+ EXPECT_EQ(attr.IsWeak(), actualAttr->IsWeak());
+ EXPECT_EQ(attr.type_mask, actualAttr->type_mask);
+ EXPECT_EQ(attr.min_int, actualAttr->min_int);
+ EXPECT_EQ(attr.max_int, actualAttr->max_int);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index ed5b60f..366c373 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -14,18 +14,22 @@
* limitations under the License.
*/
+#include "flatten/XmlFlattener.h"
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+#include "utils/misc.h"
+
#include "SdkConstants.h"
#include "flatten/ChunkWriter.h"
#include "flatten/ResourceTypeExtensions.h"
-#include "flatten/XmlFlattener.h"
#include "xml/XmlDom.h"
-#include <androidfw/ResourceTypes.h>
-#include <algorithm>
-#include <map>
-#include <utils/misc.h>
-#include <vector>
-
using namespace android;
namespace aapt {
@@ -34,301 +38,323 @@
constexpr uint32_t kLowPriority = 0xffffffffu;
-struct XmlFlattenerVisitor : public xml::Visitor {
- using xml::Visitor::visit;
+static bool cmp_xml_attribute_by_id(const xml::Attribute* a,
+ const xml::Attribute* b) {
+ if (a->compiled_attribute && a->compiled_attribute.value().id) {
+ if (b->compiled_attribute && b->compiled_attribute.value().id) {
+ return a->compiled_attribute.value().id.value() <
+ b->compiled_attribute.value().id.value();
+ }
+ return true;
+ } else if (!b->compiled_attribute) {
+ int diff = a->namespace_uri.compare(b->namespace_uri);
+ if (diff < 0) {
+ return true;
+ } else if (diff > 0) {
+ return false;
+ }
+ return a->name < b->name;
+ }
+ return false;
+}
- BigBuffer* mBuffer;
- XmlFlattenerOptions mOptions;
- StringPool mPool;
- std::map<uint8_t, StringPool> mPackagePools;
+class XmlFlattenerVisitor : public xml::Visitor {
+ public:
+ using xml::Visitor::Visit;
- struct StringFlattenDest {
- StringPool::Ref ref;
- ResStringPool_ref* dest;
- };
- std::vector<StringFlattenDest> mStringRefs;
+ StringPool pool;
+ std::map<uint8_t, StringPool> package_pools;
- // Scratch vector to filter attributes. We avoid allocations
- // making this a member.
- std::vector<xml::Attribute*> mFilteredAttrs;
+ struct StringFlattenDest {
+ StringPool::Ref ref;
+ ResStringPool_ref* dest;
+ };
+ std::vector<StringFlattenDest> string_refs;
- XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options) :
- mBuffer(buffer), mOptions(options) {
+ XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options)
+ : buffer_(buffer), options_(options) {}
+
+ void Visit(xml::Namespace* node) override {
+ if (node->namespace_uri == xml::kSchemaTools) {
+ // Skip dedicated tools namespace.
+ xml::Visitor::Visit(node);
+ } else {
+ WriteNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
+ xml::Visitor::Visit(node);
+ WriteNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
+ }
+ }
+
+ void Visit(xml::Text* node) override {
+ if (util::TrimWhitespace(node->text).empty()) {
+ // Skip whitespace only text nodes.
+ return;
}
- void addString(const StringPiece& str, uint32_t priority, android::ResStringPool_ref* dest,
- bool treatEmptyStringAsNull = false) {
- if (str.empty() && treatEmptyStringAsNull) {
- // Some parts of the runtime treat null differently than empty string.
- dest->index = util::deviceToHost32(-1);
- } else {
- mStringRefs.push_back(StringFlattenDest{
- mPool.makeRef(str, StringPool::Context{ priority }),
- dest });
- }
+ ChunkWriter writer(buffer_);
+ ResXMLTree_node* flat_node =
+ writer.StartChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
+ flat_node->lineNumber = util::HostToDevice32(node->line_number);
+ flat_node->comment.index = util::HostToDevice32(-1);
+
+ ResXMLTree_cdataExt* flat_text = writer.NextBlock<ResXMLTree_cdataExt>();
+ AddString(node->text, kLowPriority, &flat_text->data);
+
+ writer.Finish();
+ }
+
+ void Visit(xml::Element* node) override {
+ {
+ ChunkWriter start_writer(buffer_);
+ ResXMLTree_node* flat_node =
+ start_writer.StartChunk<ResXMLTree_node>(RES_XML_START_ELEMENT_TYPE);
+ flat_node->lineNumber = util::HostToDevice32(node->line_number);
+ flat_node->comment.index = util::HostToDevice32(-1);
+
+ ResXMLTree_attrExt* flat_elem =
+ start_writer.NextBlock<ResXMLTree_attrExt>();
+
+ // A missing namespace must be null, not an empty string. Otherwise the
+ // runtime complains.
+ AddString(node->namespace_uri, kLowPriority, &flat_elem->ns,
+ true /* treat_empty_string_as_null */);
+ AddString(node->name, kLowPriority, &flat_elem->name,
+ true /* treat_empty_string_as_null */);
+
+ flat_elem->attributeStart = util::HostToDevice16(sizeof(*flat_elem));
+ flat_elem->attributeSize =
+ util::HostToDevice16(sizeof(ResXMLTree_attribute));
+
+ WriteAttributes(node, flat_elem, &start_writer);
+
+ start_writer.Finish();
}
- void addString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
- mStringRefs.push_back(StringFlattenDest{ ref, dest });
- }
-
- void writeNamespace(xml::Namespace* node, uint16_t type) {
- ChunkWriter writer(mBuffer);
-
- ResXMLTree_node* flatNode = writer.startChunk<ResXMLTree_node>(type);
- flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
- flatNode->comment.index = util::hostToDevice32(-1);
-
- ResXMLTree_namespaceExt* flatNs = writer.nextBlock<ResXMLTree_namespaceExt>();
- addString(node->namespacePrefix, kLowPriority, &flatNs->prefix);
- addString(node->namespaceUri, kLowPriority, &flatNs->uri);
-
- writer.finish();
- }
-
- void visit(xml::Namespace* node) override {
- if (node->namespaceUri == xml::kSchemaTools) {
- // Skip dedicated tools namespace.
- xml::Visitor::visit(node);
- } else {
- writeNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
- xml::Visitor::visit(node);
- writeNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
- }
- }
-
- void visit(xml::Text* node) override {
- if (util::trimWhitespace(node->text).empty()) {
- // Skip whitespace only text nodes.
- return;
- }
-
- ChunkWriter writer(mBuffer);
- ResXMLTree_node* flatNode = writer.startChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
- flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
- flatNode->comment.index = util::hostToDevice32(-1);
-
- ResXMLTree_cdataExt* flatText = writer.nextBlock<ResXMLTree_cdataExt>();
- addString(node->text, kLowPriority, &flatText->data);
-
- writer.finish();
- }
-
- void visit(xml::Element* node) override {
- {
- ChunkWriter startWriter(mBuffer);
- ResXMLTree_node* flatNode = startWriter.startChunk<ResXMLTree_node>(
- RES_XML_START_ELEMENT_TYPE);
- flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
- flatNode->comment.index = util::hostToDevice32(-1);
-
- ResXMLTree_attrExt* flatElem = startWriter.nextBlock<ResXMLTree_attrExt>();
-
- // A missing namespace must be null, not an empty string. Otherwise the runtime
- // complains.
- addString(node->namespaceUri, kLowPriority, &flatElem->ns,
- true /* treatEmptyStringAsNull */);
- addString(node->name, kLowPriority, &flatElem->name,
- true /* treatEmptyStringAsNull */);
-
- flatElem->attributeStart = util::hostToDevice16(sizeof(*flatElem));
- flatElem->attributeSize = util::hostToDevice16(sizeof(ResXMLTree_attribute));
-
- writeAttributes(node, flatElem, &startWriter);
-
- startWriter.finish();
- }
-
- xml::Visitor::visit(node);
-
- {
- ChunkWriter endWriter(mBuffer);
- ResXMLTree_node* flatEndNode = endWriter.startChunk<ResXMLTree_node>(
- RES_XML_END_ELEMENT_TYPE);
- flatEndNode->lineNumber = util::hostToDevice32(node->lineNumber);
- flatEndNode->comment.index = util::hostToDevice32(-1);
-
- ResXMLTree_endElementExt* flatEndElem = endWriter.nextBlock<ResXMLTree_endElementExt>();
- addString(node->namespaceUri, kLowPriority, &flatEndElem->ns,
- true /* treatEmptyStringAsNull */);
- addString(node->name, kLowPriority, &flatEndElem->name);
-
- endWriter.finish();
- }
- }
-
- static bool cmpXmlAttributeById(const xml::Attribute* a, const xml::Attribute* b) {
- if (a->compiledAttribute && a->compiledAttribute.value().id) {
- if (b->compiledAttribute && b->compiledAttribute.value().id) {
- return a->compiledAttribute.value().id.value() < b->compiledAttribute.value().id.value();
- }
- return true;
- } else if (!b->compiledAttribute) {
- int diff = a->namespaceUri.compare(b->namespaceUri);
- if (diff < 0) {
- return true;
- } else if (diff > 0) {
- return false;
- }
- return a->name < b->name;
- }
- return false;
- }
-
- void writeAttributes(xml::Element* node, ResXMLTree_attrExt* flatElem, ChunkWriter* writer) {
- mFilteredAttrs.clear();
- mFilteredAttrs.reserve(node->attributes.size());
-
- // Filter the attributes.
- for (xml::Attribute& attr : node->attributes) {
- if (mOptions.maxSdkLevel && attr.compiledAttribute && attr.compiledAttribute.value().id) {
- size_t sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id.value());
- if (sdkLevel > mOptions.maxSdkLevel.value()) {
- continue;
- }
- }
- if (attr.namespaceUri == xml::kSchemaTools) {
- continue;
- }
- mFilteredAttrs.push_back(&attr);
- }
-
- if (mFilteredAttrs.empty()) {
- return;
- }
-
- const ResourceId kIdAttr(0x010100d0);
-
- std::sort(mFilteredAttrs.begin(), mFilteredAttrs.end(), cmpXmlAttributeById);
-
- flatElem->attributeCount = util::hostToDevice16(mFilteredAttrs.size());
-
- ResXMLTree_attribute* flatAttr = writer->nextBlock<ResXMLTree_attribute>(
- mFilteredAttrs.size());
- uint16_t attributeIndex = 1;
- for (const xml::Attribute* xmlAttr : mFilteredAttrs) {
- // Assign the indices for specific attributes.
- if (xmlAttr->compiledAttribute && xmlAttr->compiledAttribute.value().id &&
- xmlAttr->compiledAttribute.value().id.value() == kIdAttr) {
- flatElem->idIndex = util::hostToDevice16(attributeIndex);
- } else if (xmlAttr->namespaceUri.empty()) {
- if (xmlAttr->name == "class") {
- flatElem->classIndex = util::hostToDevice16(attributeIndex);
- } else if (xmlAttr->name == "style") {
- flatElem->styleIndex = util::hostToDevice16(attributeIndex);
- }
- }
- attributeIndex++;
-
- // Add the namespaceUri to the list of StringRefs to encode. Use null if the namespace
- // is empty (doesn't exist).
- addString(xmlAttr->namespaceUri, kLowPriority, &flatAttr->ns,
- true /* treatEmptyStringAsNull */);
-
- flatAttr->rawValue.index = util::hostToDevice32(-1);
-
- if (!xmlAttr->compiledAttribute || !xmlAttr->compiledAttribute.value().id) {
- // The attribute has no associated ResourceID, so the string order doesn't matter.
- addString(xmlAttr->name, kLowPriority, &flatAttr->name);
- } else {
- // Attribute names are stored without packages, but we use
- // their StringPool index to lookup their resource IDs.
- // This will cause collisions, so we can't dedupe
- // attribute names from different packages. We use separate
- // pools that we later combine.
- //
- // Lookup the StringPool for this package and make the reference there.
- const xml::AaptAttribute& aaptAttr = xmlAttr->compiledAttribute.value();
-
- StringPool::Ref nameRef = mPackagePools[aaptAttr.id.value().packageId()].makeRef(
- xmlAttr->name, StringPool::Context{ aaptAttr.id.value().id });
-
- // Add it to the list of strings to flatten.
- addString(nameRef, &flatAttr->name);
- }
-
- if (mOptions.keepRawValues || !xmlAttr->compiledValue) {
- // Keep raw values if the value is not compiled or
- // if we're building a static library (need symbols).
- addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
- }
-
- if (xmlAttr->compiledValue) {
- bool result = xmlAttr->compiledValue->flatten(&flatAttr->typedValue);
- assert(result);
- } else {
- // Flatten as a regular string type.
- flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
- addString(xmlAttr->value, kLowPriority,
- (ResStringPool_ref*) &flatAttr->typedValue.data);
- }
-
- flatAttr->typedValue.size = util::hostToDevice16(sizeof(flatAttr->typedValue));
- flatAttr++;
- }
- }
-};
-
-} // namespace
-
-bool XmlFlattener::flatten(IAaptContext* context, xml::Node* node) {
- BigBuffer nodeBuffer(1024);
- XmlFlattenerVisitor visitor(&nodeBuffer, mOptions);
- node->accept(&visitor);
-
- // Merge the package pools into the main pool.
- for (auto& packagePoolEntry : visitor.mPackagePools) {
- visitor.mPool.merge(std::move(packagePoolEntry.second));
- }
-
- // Sort the string pool so that attribute resource IDs show up first.
- visitor.mPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- return a.context.priority < b.context.priority;
- });
-
- // Now we flatten the string pool references into the correct places.
- for (const auto& refEntry : visitor.mStringRefs) {
- refEntry.dest->index = util::hostToDevice32(refEntry.ref.getIndex());
- }
-
- // Write the XML header.
- ChunkWriter xmlHeaderWriter(mBuffer);
- xmlHeaderWriter.startChunk<ResXMLTree_header>(RES_XML_TYPE);
-
- // Flatten the StringPool.
- StringPool::flattenUtf8(mBuffer, visitor.mPool);
+ xml::Visitor::Visit(node);
{
- // Write the array of resource IDs, indexed by StringPool order.
- ChunkWriter resIdMapWriter(mBuffer);
- resIdMapWriter.startChunk<ResChunk_header>(RES_XML_RESOURCE_MAP_TYPE);
- for (const auto& str : visitor.mPool) {
- ResourceId id = { str->context.priority };
- if (id.id == kLowPriority || !id.isValid()) {
- // When we see the first non-resource ID,
- // we're done.
- break;
- }
+ ChunkWriter end_writer(buffer_);
+ ResXMLTree_node* flat_end_node =
+ end_writer.StartChunk<ResXMLTree_node>(RES_XML_END_ELEMENT_TYPE);
+ flat_end_node->lineNumber = util::HostToDevice32(node->line_number);
+ flat_end_node->comment.index = util::HostToDevice32(-1);
- *resIdMapWriter.nextBlock<uint32_t>() = id.id;
+ ResXMLTree_endElementExt* flat_end_elem =
+ end_writer.NextBlock<ResXMLTree_endElementExt>();
+ AddString(node->namespace_uri, kLowPriority, &flat_end_elem->ns,
+ true /* treat_empty_string_as_null */);
+ AddString(node->name, kLowPriority, &flat_end_elem->name);
+
+ end_writer.Finish();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XmlFlattenerVisitor);
+
+ void AddString(const StringPiece& str, uint32_t priority,
+ android::ResStringPool_ref* dest,
+ bool treat_empty_string_as_null = false) {
+ if (str.empty() && treat_empty_string_as_null) {
+ // Some parts of the runtime treat null differently than empty string.
+ dest->index = util::DeviceToHost32(-1);
+ } else {
+ string_refs.push_back(StringFlattenDest{
+ pool.MakeRef(str, StringPool::Context(priority)), dest});
+ }
+ }
+
+ void AddString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
+ string_refs.push_back(StringFlattenDest{ref, dest});
+ }
+
+ void WriteNamespace(xml::Namespace* node, uint16_t type) {
+ ChunkWriter writer(buffer_);
+
+ ResXMLTree_node* flatNode = writer.StartChunk<ResXMLTree_node>(type);
+ flatNode->lineNumber = util::HostToDevice32(node->line_number);
+ flatNode->comment.index = util::HostToDevice32(-1);
+
+ ResXMLTree_namespaceExt* flat_ns =
+ writer.NextBlock<ResXMLTree_namespaceExt>();
+ AddString(node->namespace_prefix, kLowPriority, &flat_ns->prefix);
+ AddString(node->namespace_uri, kLowPriority, &flat_ns->uri);
+
+ writer.Finish();
+ }
+
+ void WriteAttributes(xml::Element* node, ResXMLTree_attrExt* flat_elem,
+ ChunkWriter* writer) {
+ filtered_attrs_.clear();
+ filtered_attrs_.reserve(node->attributes.size());
+
+ // Filter the attributes.
+ for (xml::Attribute& attr : node->attributes) {
+ if (options_.max_sdk_level && attr.compiled_attribute &&
+ attr.compiled_attribute.value().id) {
+ size_t sdk_level =
+ FindAttributeSdkLevel(attr.compiled_attribute.value().id.value());
+ if (sdk_level > options_.max_sdk_level.value()) {
+ continue;
}
- resIdMapWriter.finish();
+ }
+ if (attr.namespace_uri == xml::kSchemaTools) {
+ continue;
+ }
+ filtered_attrs_.push_back(&attr);
}
- // Move the nodeBuffer and append it to the out buffer.
- mBuffer->appendBuffer(std::move(nodeBuffer));
-
- // Finish the xml header.
- xmlHeaderWriter.finish();
- return true;
-}
-
-bool XmlFlattener::consume(IAaptContext* context, xml::XmlResource* resource) {
- if (!resource->root) {
- return false;
+ if (filtered_attrs_.empty()) {
+ return;
}
- return flatten(context, resource->root.get());
+
+ const ResourceId kIdAttr(0x010100d0);
+
+ std::sort(filtered_attrs_.begin(), filtered_attrs_.end(),
+ cmp_xml_attribute_by_id);
+
+ flat_elem->attributeCount = util::HostToDevice16(filtered_attrs_.size());
+
+ ResXMLTree_attribute* flat_attr =
+ writer->NextBlock<ResXMLTree_attribute>(filtered_attrs_.size());
+ uint16_t attribute_index = 1;
+ for (const xml::Attribute* xml_attr : filtered_attrs_) {
+ // Assign the indices for specific attributes.
+ if (xml_attr->compiled_attribute &&
+ xml_attr->compiled_attribute.value().id &&
+ xml_attr->compiled_attribute.value().id.value() == kIdAttr) {
+ flat_elem->idIndex = util::HostToDevice16(attribute_index);
+ } else if (xml_attr->namespace_uri.empty()) {
+ if (xml_attr->name == "class") {
+ flat_elem->classIndex = util::HostToDevice16(attribute_index);
+ } else if (xml_attr->name == "style") {
+ flat_elem->styleIndex = util::HostToDevice16(attribute_index);
+ }
+ }
+ attribute_index++;
+
+ // Add the namespaceUri to the list of StringRefs to encode. Use null if
+ // the namespace
+ // is empty (doesn't exist).
+ AddString(xml_attr->namespace_uri, kLowPriority, &flat_attr->ns,
+ true /* treat_empty_string_as_null */);
+
+ flat_attr->rawValue.index = util::HostToDevice32(-1);
+
+ if (!xml_attr->compiled_attribute ||
+ !xml_attr->compiled_attribute.value().id) {
+ // The attribute has no associated ResourceID, so the string order
+ // doesn't matter.
+ AddString(xml_attr->name, kLowPriority, &flat_attr->name);
+ } else {
+ // Attribute names are stored without packages, but we use
+ // their StringPool index to lookup their resource IDs.
+ // This will cause collisions, so we can't dedupe
+ // attribute names from different packages. We use separate
+ // pools that we later combine.
+ //
+ // Lookup the StringPool for this package and make the reference there.
+ const xml::AaptAttribute& aapt_attr =
+ xml_attr->compiled_attribute.value();
+
+ StringPool::Ref name_ref =
+ package_pools[aapt_attr.id.value().package_id()].MakeRef(
+ xml_attr->name, StringPool::Context(aapt_attr.id.value().id));
+
+ // Add it to the list of strings to flatten.
+ AddString(name_ref, &flat_attr->name);
+ }
+
+ if (options_.keep_raw_values || !xml_attr->compiled_value) {
+ // Keep raw values if the value is not compiled or
+ // if we're building a static library (need symbols).
+ AddString(xml_attr->value, kLowPriority, &flat_attr->rawValue);
+ }
+
+ if (xml_attr->compiled_value) {
+ CHECK(xml_attr->compiled_value->Flatten(&flat_attr->typedValue));
+ } else {
+ // Flatten as a regular string type.
+ flat_attr->typedValue.dataType = android::Res_value::TYPE_STRING;
+ AddString(xml_attr->value, kLowPriority,
+ (ResStringPool_ref*)&flat_attr->typedValue.data);
+ }
+
+ flat_attr->typedValue.size =
+ util::HostToDevice16(sizeof(flat_attr->typedValue));
+ flat_attr++;
+ }
+ }
+
+ BigBuffer* buffer_;
+ XmlFlattenerOptions options_;
+
+ // Scratch vector to filter attributes. We avoid allocations
+ // making this a member.
+ std::vector<xml::Attribute*> filtered_attrs_;
+};
+
+} // namespace
+
+bool XmlFlattener::Flatten(IAaptContext* context, xml::Node* node) {
+ BigBuffer node_buffer(1024);
+ XmlFlattenerVisitor visitor(&node_buffer, options_);
+ node->Accept(&visitor);
+
+ // Merge the package pools into the main pool.
+ for (auto& package_pool_entry : visitor.package_pools) {
+ visitor.pool.Merge(std::move(package_pool_entry.second));
+ }
+
+ // Sort the string pool so that attribute resource IDs show up first.
+ visitor.pool.Sort(
+ [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+ return a.context.priority < b.context.priority;
+ });
+
+ // Now we flatten the string pool references into the correct places.
+ for (const auto& ref_entry : visitor.string_refs) {
+ ref_entry.dest->index = util::HostToDevice32(ref_entry.ref.index());
+ }
+
+ // Write the XML header.
+ ChunkWriter xml_header_writer(buffer_);
+ xml_header_writer.StartChunk<ResXMLTree_header>(RES_XML_TYPE);
+
+ // Flatten the StringPool.
+ StringPool::FlattenUtf8(buffer_, visitor.pool);
+
+ {
+ // Write the array of resource IDs, indexed by StringPool order.
+ ChunkWriter res_id_map_writer(buffer_);
+ res_id_map_writer.StartChunk<ResChunk_header>(RES_XML_RESOURCE_MAP_TYPE);
+ for (const auto& str : visitor.pool) {
+ ResourceId id = {str->context.priority};
+ if (id.id == kLowPriority || !id.is_valid()) {
+ // When we see the first non-resource ID,
+ // we're done.
+ break;
+ }
+
+ *res_id_map_writer.NextBlock<uint32_t>() = id.id;
+ }
+ res_id_map_writer.Finish();
+ }
+
+ // Move the nodeBuffer and append it to the out buffer.
+ buffer_->AppendBuffer(std::move(node_buffer));
+
+ // Finish the xml header.
+ xml_header_writer.Finish();
+ return true;
}
-} // namespace aapt
+bool XmlFlattener::Consume(IAaptContext* context, xml::XmlResource* resource) {
+ if (!resource->root) {
+ return false;
+ }
+ return Flatten(context, resource->root.get());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/flatten/XmlFlattener.h b/tools/aapt2/flatten/XmlFlattener.h
index a688ac9..f5129fd 100644
--- a/tools/aapt2/flatten/XmlFlattener.h
+++ b/tools/aapt2/flatten/XmlFlattener.h
@@ -17,6 +17,8 @@
#ifndef AAPT_FLATTEN_XMLFLATTENER_H
#define AAPT_FLATTEN_XMLFLATTENER_H
+#include "android-base/macros.h"
+
#include "process/IResourceTableConsumer.h"
#include "util/BigBuffer.h"
#include "xml/XmlDom.h"
@@ -24,32 +26,33 @@
namespace aapt {
struct XmlFlattenerOptions {
- /**
- * Keep attribute raw string values along with typed values.
- */
- bool keepRawValues = false;
+ /**
+ * Keep attribute raw string values along with typed values.
+ */
+ bool keep_raw_values = false;
- /**
- * If set, the max SDK level of attribute to flatten. All others are ignored.
- */
- Maybe<size_t> maxSdkLevel;
+ /**
+ * If set, the max SDK level of attribute to flatten. All others are ignored.
+ */
+ Maybe<size_t> max_sdk_level;
};
class XmlFlattener : public IXmlResourceConsumer {
-public:
- XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options) :
- mBuffer(buffer), mOptions(options) {
- }
+ public:
+ XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options)
+ : buffer_(buffer), options_(options) {}
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
-private:
- BigBuffer* mBuffer;
- XmlFlattenerOptions mOptions;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XmlFlattener);
- bool flatten(IAaptContext* context, xml::Node* node);
+ bool Flatten(IAaptContext* context, xml::Node* node);
+
+ BigBuffer* buffer_;
+ XmlFlattenerOptions options_;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FLATTEN_XMLFLATTENER_H */
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index 4d1e178..2c83bb3 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -15,240 +15,251 @@
*/
#include "flatten/XmlFlattener.h"
+
+#include "androidfw/ResourceTypes.h"
+
#include "link/Linkers.h"
#include "test/Test.h"
#include "util/BigBuffer.h"
#include "util/Util.h"
-#include <androidfw/ResourceTypes.h>
-
namespace aapt {
class XmlFlattenerTest : public ::testing::Test {
-public:
- void SetUp() override {
- mContext = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" })
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addSymbol("android:attr/id", ResourceId(0x010100d0),
- test::AttributeBuilder().build())
- .addSymbol("com.app.test:id/id", ResourceId(0x7f020000))
- .addSymbol("android:attr/paddingStart", ResourceId(0x010103b3),
- test::AttributeBuilder().build())
- .addSymbol("android:attr/colorAccent", ResourceId(0x01010435),
- test::AttributeBuilder().build())
- .build())
- .build();
+ public:
+ void SetUp() override {
+ context_ =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .AddSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .AddSymbol("android:attr/id", ResourceId(0x010100d0),
+ test::AttributeBuilder().Build())
+ .AddSymbol("com.app.test:id/id", ResourceId(0x7f020000))
+ .AddSymbol("android:attr/paddingStart",
+ ResourceId(0x010103b3),
+ test::AttributeBuilder().Build())
+ .AddSymbol("android:attr/colorAccent",
+ ResourceId(0x01010435),
+ test::AttributeBuilder().Build())
+ .Build())
+ .Build();
+ }
+
+ ::testing::AssertionResult Flatten(xml::XmlResource* doc,
+ android::ResXMLTree* out_tree,
+ const XmlFlattenerOptions& options = {}) {
+ using namespace android; // For NO_ERROR on windows because it is a macro.
+
+ BigBuffer buffer(1024);
+ XmlFlattener flattener(&buffer, options);
+ if (!flattener.Consume(context_.get(), doc)) {
+ return ::testing::AssertionFailure() << "failed to flatten XML Tree";
}
- ::testing::AssertionResult flatten(xml::XmlResource* doc, android::ResXMLTree* outTree,
- const XmlFlattenerOptions& options = {}) {
- using namespace android; // For NO_ERROR on windows because it is a macro.
-
- BigBuffer buffer(1024);
- XmlFlattener flattener(&buffer, options);
- if (!flattener.consume(mContext.get(), doc)) {
- return ::testing::AssertionFailure() << "failed to flatten XML Tree";
- }
-
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- if (outTree->setTo(data.get(), buffer.size(), true) != NO_ERROR) {
- return ::testing::AssertionFailure() << "flattened XML is corrupt";
- }
- return ::testing::AssertionSuccess();
+ std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+ if (out_tree->setTo(data.get(), buffer.size(), true) != NO_ERROR) {
+ return ::testing::AssertionFailure() << "flattened XML is corrupt";
}
+ return ::testing::AssertionSuccess();
+ }
-protected:
- std::unique_ptr<IAaptContext> mContext;
+ protected:
+ std::unique_ptr<IAaptContext> context_;
};
TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:test="http://com.test"
attr="hey">
<Layout test:hello="hi" />
<Layout>Some text</Layout>
</View>)EOF");
+ android::ResXMLTree tree;
+ ASSERT_TRUE(Flatten(doc.get(), &tree));
- android::ResXMLTree tree;
- ASSERT_TRUE(flatten(doc.get(), &tree));
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
+ size_t len;
+ const char16_t* namespace_prefix = tree.getNamespacePrefix(&len);
+ EXPECT_EQ(StringPiece16(namespace_prefix, len), u"test");
- size_t len;
- const char16_t* namespacePrefix = tree.getNamespacePrefix(&len);
- EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
+ const char16_t* namespace_uri = tree.getNamespaceUri(&len);
+ ASSERT_EQ(StringPiece16(namespace_uri, len), u"http://com.test");
- const char16_t* namespaceUri = tree.getNamespaceUri(&len);
- ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test");
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ const char16_t* tag_name = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tag_name, len), u"View");
- ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
- const char16_t* tagName = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(tagName, len), u"View");
+ ASSERT_EQ(1u, tree.getAttributeCount());
+ ASSERT_EQ(tree.getAttributeNamespace(0, &len), nullptr);
+ const char16_t* attr_name = tree.getAttributeName(0, &len);
+ EXPECT_EQ(StringPiece16(attr_name, len), u"attr");
- ASSERT_EQ(1u, tree.getAttributeCount());
- ASSERT_EQ(tree.getAttributeNamespace(0, &len), nullptr);
- const char16_t* attrName = tree.getAttributeName(0, &len);
- EXPECT_EQ(StringPiece16(attrName, len), u"attr");
+ EXPECT_EQ(0, tree.indexOfAttribute(nullptr, 0, u"attr",
+ StringPiece16(u"attr").size()));
- EXPECT_EQ(0, tree.indexOfAttribute(nullptr, 0, u"attr", StringPiece16(u"attr").size()));
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tag_name = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tag_name, len), u"Layout");
- ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
- tagName = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
+ ASSERT_EQ(1u, tree.getAttributeCount());
+ const char16_t* attr_namespace = tree.getAttributeNamespace(0, &len);
+ EXPECT_EQ(StringPiece16(attr_namespace, len), u"http://com.test");
- ASSERT_EQ(1u, tree.getAttributeCount());
- const char16_t* attrNamespace = tree.getAttributeNamespace(0, &len);
- EXPECT_EQ(StringPiece16(attrNamespace, len), u"http://com.test");
+ attr_name = tree.getAttributeName(0, &len);
+ EXPECT_EQ(StringPiece16(attr_name, len), u"hello");
- attrName = tree.getAttributeName(0, &len);
- EXPECT_EQ(StringPiece16(attrName, len), u"hello");
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
- ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tag_name = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tag_name, len), u"Layout");
+ ASSERT_EQ(0u, tree.getAttributeCount());
- ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
- tagName = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
- ASSERT_EQ(0u, tree.getAttributeCount());
+ ASSERT_EQ(tree.next(), android::ResXMLTree::TEXT);
+ const char16_t* text = tree.getText(&len);
+ EXPECT_EQ(StringPiece16(text, len), u"Some text");
- ASSERT_EQ(tree.next(), android::ResXMLTree::TEXT);
- const char16_t* text = tree.getText(&len);
- EXPECT_EQ(StringPiece16(text, len), u"Some text");
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tag_name = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tag_name, len), u"Layout");
- ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
- ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
- tagName = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tag_name = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tag_name, len), u"View");
- ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
- ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
- tagName = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(tagName, len), u"View");
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_NAMESPACE);
+ namespace_prefix = tree.getNamespacePrefix(&len);
+ EXPECT_EQ(StringPiece16(namespace_prefix, len), u"test");
- ASSERT_EQ(tree.next(), android::ResXMLTree::END_NAMESPACE);
- namespacePrefix = tree.getNamespacePrefix(&len);
- EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
+ namespace_uri = tree.getNamespaceUri(&len);
+ ASSERT_EQ(StringPiece16(namespace_uri, len), u"http://com.test");
- namespaceUri = tree.getNamespaceUri(&len);
- ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test");
-
- ASSERT_EQ(tree.next(), android::ResXMLTree::END_DOCUMENT);
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_DOCUMENT);
}
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingStart="1dp"
android:colorAccent="#ffffff"/>)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- ASSERT_TRUE(linker.getSdkLevels().count(17) == 1);
- ASSERT_TRUE(linker.getSdkLevels().count(21) == 1);
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+ ASSERT_TRUE(linker.sdk_levels().count(17) == 1);
+ ASSERT_TRUE(linker.sdk_levels().count(21) == 1);
- android::ResXMLTree tree;
- XmlFlattenerOptions options;
- options.maxSdkLevel = 17;
- ASSERT_TRUE(flatten(doc.get(), &tree, options));
+ android::ResXMLTree tree;
+ XmlFlattenerOptions options;
+ options.max_sdk_level = 17;
+ ASSERT_TRUE(Flatten(doc.get(), &tree, options));
- while (tree.next() != android::ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
- }
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ }
- ASSERT_EQ(1u, tree.getAttributeCount());
- EXPECT_EQ(uint32_t(0x010103b3), tree.getAttributeNameResID(0));
+ ASSERT_EQ(1u, tree.getAttributeCount());
+ EXPECT_EQ(uint32_t(0x010103b3), tree.getAttributeNameResID(0));
}
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripOnlyTools) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:tools="http://schemas.android.com/tools"
xmlns:foo="http://schemas.android.com/foo"
foo:bar="Foo"
tools:ignore="MissingTranslation"/>)EOF");
- android::ResXMLTree tree;
- ASSERT_TRUE(flatten(doc.get(), &tree));
+ android::ResXMLTree tree;
+ ASSERT_TRUE(Flatten(doc.get(), &tree));
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
- size_t len;
- const char16_t* namespacePrefix = tree.getNamespacePrefix(&len);
- EXPECT_EQ(StringPiece16(namespacePrefix, len), u"foo");
+ size_t len;
+ const char16_t* namespace_prefix = tree.getNamespacePrefix(&len);
+ EXPECT_EQ(StringPiece16(namespace_prefix, len), u"foo");
- const char16_t* namespaceUri = tree.getNamespaceUri(&len);
- ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://schemas.android.com/foo");
+ const char16_t* namespace_uri = tree.getNamespaceUri(&len);
+ ASSERT_EQ(StringPiece16(namespace_uri, len),
+ u"http://schemas.android.com/foo");
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
- EXPECT_EQ(
- tree.indexOfAttribute("http://schemas.android.com/tools", "ignore"),
+ EXPECT_EQ(tree.indexOfAttribute("http://schemas.android.com/tools", "ignore"),
android::NAME_NOT_FOUND);
- EXPECT_GE(tree.indexOfAttribute("http://schemas.android.com/foo", "bar"), 0);
+ EXPECT_GE(tree.indexOfAttribute("http://schemas.android.com/foo", "bar"), 0);
}
TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/id"
class="str"
style="@id/id"/>)EOF");
- android::ResXMLTree tree;
- ASSERT_TRUE(flatten(doc.get(), &tree));
+ android::ResXMLTree tree;
+ ASSERT_TRUE(Flatten(doc.get(), &tree));
- while (tree.next() != android::ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
- }
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ }
- EXPECT_EQ(tree.indexOfClass(), 0);
- EXPECT_EQ(tree.indexOfStyle(), 1);
+ EXPECT_EQ(tree.indexOfClass(), 0);
+ EXPECT_EQ(tree.indexOfStyle(), 1);
}
/*
- * The device ResXMLParser in libandroidfw differentiates between empty namespace and null
+ * The device ResXMLParser in libandroidfw differentiates between empty
+ * namespace and null
* namespace.
*/
TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View package=\"android\"/>");
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDom("<View package=\"android\"/>");
- android::ResXMLTree tree;
- ASSERT_TRUE(flatten(doc.get(), &tree));
+ android::ResXMLTree tree;
+ ASSERT_TRUE(Flatten(doc.get(), &tree));
- while (tree.next() != android::ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
- }
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ }
- const StringPiece16 kPackage = u"package";
- EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
+ const StringPiece16 kPackage = u"package";
+ EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()),
+ 0);
}
TEST_F(XmlFlattenerTest, EmptyStringValueInAttributeIsNotNull) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View package=\"\"/>");
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDom("<View package=\"\"/>");
- android::ResXMLTree tree;
- ASSERT_TRUE(flatten(doc.get(), &tree));
+ android::ResXMLTree tree;
+ ASSERT_TRUE(Flatten(doc.get(), &tree));
- while (tree.next() != android::ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
- }
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ }
- const StringPiece16 kPackage = u"package";
- ssize_t idx = tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
- ASSERT_GE(idx, 0);
+ const StringPiece16 kPackage = u"package";
+ ssize_t idx =
+ tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
+ ASSERT_GE(idx, 0);
- size_t len;
- EXPECT_NE(nullptr, tree.getAttributeStringValue(idx, &len));
+ size_t len;
+ EXPECT_NE(nullptr, tree.getAttributeStringValue(idx, &len));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/io/Data.h b/tools/aapt2/io/Data.h
index 34eed63..fdc044d 100644
--- a/tools/aapt2/io/Data.h
+++ b/tools/aapt2/io/Data.h
@@ -17,106 +17,97 @@
#ifndef AAPT_IO_DATA_H
#define AAPT_IO_DATA_H
-#include <android-base/macros.h>
#include <memory>
-#include <utils/FileMap.h>
+
+#include "android-base/macros.h"
+#include "utils/FileMap.h"
namespace aapt {
namespace io {
/**
- * Interface for a block of contiguous memory. An instance of this interface owns the data.
+ * Interface for a block of contiguous memory. An instance of this interface
+ * owns the data.
*/
class IData {
-public:
- virtual ~IData() = default;
+ public:
+ virtual ~IData() = default;
- virtual const void* data() const = 0;
- virtual size_t size() const = 0;
+ virtual const void* data() const = 0;
+ virtual size_t size() const = 0;
};
class DataSegment : public IData {
-public:
- explicit DataSegment(std::unique_ptr<IData> data, size_t offset, size_t len) :
- mData(std::move(data)), mOffset(offset), mLen(len) {
- }
+ public:
+ explicit DataSegment(std::unique_ptr<IData> data, size_t offset, size_t len)
+ : data_(std::move(data)), offset_(offset), len_(len) {}
- const void* data() const override {
- return static_cast<const uint8_t*>(mData->data()) + mOffset;
- }
+ const void* data() const override {
+ return static_cast<const uint8_t*>(data_->data()) + offset_;
+ }
- size_t size() const override {
- return mLen;
- }
+ size_t size() const override { return len_; }
-private:
- DISALLOW_COPY_AND_ASSIGN(DataSegment);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DataSegment);
- std::unique_ptr<IData> mData;
- size_t mOffset;
- size_t mLen;
+ std::unique_ptr<IData> data_;
+ size_t offset_;
+ size_t len_;
};
/**
- * Implementation of IData that exposes a memory mapped file. The mmapped file is owned by this
+ * Implementation of IData that exposes a memory mapped file. The mmapped file
+ * is owned by this
* object.
*/
class MmappedData : public IData {
-public:
- explicit MmappedData(android::FileMap&& map) : mMap(std::forward<android::FileMap>(map)) {
- }
+ public:
+ explicit MmappedData(android::FileMap&& map)
+ : map_(std::forward<android::FileMap>(map)) {}
- const void* data() const override {
- return mMap.getDataPtr();
- }
+ const void* data() const override { return map_.getDataPtr(); }
- size_t size() const override {
- return mMap.getDataLength();
- }
+ size_t size() const override { return map_.getDataLength(); }
-private:
- android::FileMap mMap;
+ private:
+ android::FileMap map_;
};
/**
- * Implementation of IData that exposes a block of memory that was malloc'ed (new'ed). The
+ * Implementation of IData that exposes a block of memory that was malloc'ed
+ * (new'ed). The
* memory is owned by this object.
*/
class MallocData : public IData {
-public:
- MallocData(std::unique_ptr<const uint8_t[]> data, size_t size) :
- mData(std::move(data)), mSize(size) {
- }
+ public:
+ MallocData(std::unique_ptr<const uint8_t[]> data, size_t size)
+ : data_(std::move(data)), size_(size) {}
- const void* data() const override {
- return mData.get();
- }
+ const void* data() const override { return data_.get(); }
- size_t size() const override {
- return mSize;
- }
+ size_t size() const override { return size_; }
-private:
- std::unique_ptr<const uint8_t[]> mData;
- size_t mSize;
+ private:
+ std::unique_ptr<const uint8_t[]> data_;
+ size_t size_;
};
/**
- * When mmap fails because the file has length 0, we use the EmptyData to simulate data of length 0.
+ * When mmap fails because the file has length 0, we use the EmptyData to
+ * simulate data of length 0.
*/
class EmptyData : public IData {
-public:
- const void* data() const override {
- static const uint8_t d = 0;
- return &d;
- }
+ public:
+ const void* data() const override {
+ static const uint8_t d = 0;
+ return &d;
+ }
- size_t size() const override {
- return 0u;
- }
+ size_t size() const override { return 0u; }
};
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
#endif /* AAPT_IO_DATA_H */
diff --git a/tools/aapt2/io/File.cpp b/tools/aapt2/io/File.cpp
index 739c0d2..ee73728 100644
--- a/tools/aapt2/io/File.cpp
+++ b/tools/aapt2/io/File.cpp
@@ -21,23 +21,23 @@
namespace aapt {
namespace io {
-IFile* IFile::createFileSegment(size_t offset, size_t len) {
- FileSegment* fileSegment = new FileSegment(this, offset, len);
- mSegments.push_back(std::unique_ptr<IFile>(fileSegment));
- return fileSegment;
+IFile* IFile::CreateFileSegment(size_t offset, size_t len) {
+ FileSegment* file_segment = new FileSegment(this, offset, len);
+ segments_.push_back(std::unique_ptr<IFile>(file_segment));
+ return file_segment;
}
-std::unique_ptr<IData> FileSegment::openAsData() {
- std::unique_ptr<IData> data = mFile->openAsData();
- if (!data) {
- return {};
- }
-
- if (mOffset <= data->size() - mLen) {
- return util::make_unique<DataSegment>(std::move(data), mOffset, mLen);
- }
+std::unique_ptr<IData> FileSegment::OpenAsData() {
+ std::unique_ptr<IData> data = file_->OpenAsData();
+ if (!data) {
return {};
+ }
+
+ if (offset_ <= data->size() - len_) {
+ return util::make_unique<DataSegment>(std::move(data), offset_, len_);
+ }
+ return {};
}
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/File.h b/tools/aapt2/io/File.h
index 807981e..644f59f 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -17,96 +17,104 @@
#ifndef AAPT_IO_FILE_H
#define AAPT_IO_FILE_H
-#include "Source.h"
-#include "io/Data.h"
-#include "util/Util.h"
-
-#include <android-base/macros.h>
#include <list>
#include <memory>
#include <vector>
+#include "android-base/macros.h"
+
+#include "Source.h"
+#include "io/Data.h"
+#include "util/Util.h"
+
namespace aapt {
namespace io {
/**
- * Interface for a file, which could be a real file on the file system, or a file inside
+ * Interface for a file, which could be a real file on the file system, or a
+ * file inside
* a ZIP archive.
*/
class IFile {
-public:
- virtual ~IFile() = default;
+ public:
+ virtual ~IFile() = default;
- /**
- * Open the file and return it as a block of contiguous memory. How this occurs is
- * implementation dependent. For example, if this is a file on the file system, it may
- * simply mmap the contents. If this file represents a compressed file in a ZIP archive,
- * it may need to inflate it to memory, incurring a copy.
- *
- * Returns nullptr on failure.
- */
- virtual std::unique_ptr<IData> openAsData() = 0;
+ /**
+ * Open the file and return it as a block of contiguous memory. How this
+ * occurs is
+ * implementation dependent. For example, if this is a file on the file
+ * system, it may
+ * simply mmap the contents. If this file represents a compressed file in a
+ * ZIP archive,
+ * it may need to inflate it to memory, incurring a copy.
+ *
+ * Returns nullptr on failure.
+ */
+ virtual std::unique_ptr<IData> OpenAsData() = 0;
- /**
- * Returns the source of this file. This is for presentation to the user and may not be a
- * valid file system path (for example, it may contain a '@' sign to separate the files within
- * a ZIP archive from the path to the containing ZIP archive.
- */
- virtual const Source& getSource() const = 0;
+ /**
+ * Returns the source of this file. This is for presentation to the user and
+ * may not be a
+ * valid file system path (for example, it may contain a '@' sign to separate
+ * the files within
+ * a ZIP archive from the path to the containing ZIP archive.
+ */
+ virtual const Source& GetSource() const = 0;
- IFile* createFileSegment(size_t offset, size_t len);
+ IFile* CreateFileSegment(size_t offset, size_t len);
-private:
- // Any segments created from this IFile need to be owned by this IFile, so keep them
- // in a list. This will never be read, so we prefer better insertion performance
- // than cache locality, hence the list.
- std::list<std::unique_ptr<IFile>> mSegments;
+ private:
+ // Any segments created from this IFile need to be owned by this IFile, so
+ // keep them
+ // in a list. This will never be read, so we prefer better insertion
+ // performance
+ // than cache locality, hence the list.
+ std::list<std::unique_ptr<IFile>> segments_;
};
/**
- * An IFile that wraps an underlying IFile but limits it to a subsection of that file.
+ * An IFile that wraps an underlying IFile but limits it to a subsection of that
+ * file.
*/
class FileSegment : public IFile {
-public:
- explicit FileSegment(IFile* file, size_t offset, size_t len) :
- mFile(file), mOffset(offset), mLen(len) {
- }
+ public:
+ explicit FileSegment(IFile* file, size_t offset, size_t len)
+ : file_(file), offset_(offset), len_(len) {}
- std::unique_ptr<IData> openAsData() override;
+ std::unique_ptr<IData> OpenAsData() override;
- const Source& getSource() const override {
- return mFile->getSource();
- }
+ const Source& GetSource() const override { return file_->GetSource(); }
-private:
- DISALLOW_COPY_AND_ASSIGN(FileSegment);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FileSegment);
- IFile* mFile;
- size_t mOffset;
- size_t mLen;
+ IFile* file_;
+ size_t offset_;
+ size_t len_;
};
class IFileCollectionIterator {
-public:
- virtual ~IFileCollectionIterator() = default;
+ public:
+ virtual ~IFileCollectionIterator() = default;
- virtual bool hasNext() = 0;
- virtual IFile* next() = 0;
+ virtual bool HasNext() = 0;
+ virtual IFile* Next() = 0;
};
/**
- * Interface for a collection of files, all of which share a common source. That source may
+ * Interface for a collection of files, all of which share a common source. That
+ * source may
* simply be the filesystem, or a ZIP archive.
*/
class IFileCollection {
-public:
- virtual ~IFileCollection() = default;
+ public:
+ virtual ~IFileCollection() = default;
- virtual IFile* findFile(const StringPiece& path) = 0;
- virtual std::unique_ptr<IFileCollectionIterator> iterator() = 0;
+ virtual IFile* FindFile(const StringPiece& path) = 0;
+ virtual std::unique_ptr<IFileCollectionIterator> Iterator() = 0;
};
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
#endif /* AAPT_IO_FILE_H */
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index e758d8a4..828f34e 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -14,65 +14,62 @@
* limitations under the License.
*/
-#include "Source.h"
#include "io/FileSystem.h"
+
+#include "utils/FileMap.h"
+
+#include "Source.h"
#include "util/Files.h"
#include "util/Maybe.h"
#include "util/StringPiece.h"
#include "util/Util.h"
-#include <utils/FileMap.h>
-
namespace aapt {
namespace io {
-RegularFile::RegularFile(const Source& source) : mSource(source) {
-}
+RegularFile::RegularFile(const Source& source) : source_(source) {}
-std::unique_ptr<IData> RegularFile::openAsData() {
- android::FileMap map;
- if (Maybe<android::FileMap> map = file::mmapPath(mSource.path, nullptr)) {
- if (map.value().getDataPtr() && map.value().getDataLength() > 0) {
- return util::make_unique<MmappedData>(std::move(map.value()));
- }
- return util::make_unique<EmptyData>();
+std::unique_ptr<IData> RegularFile::OpenAsData() {
+ android::FileMap map;
+ if (Maybe<android::FileMap> map = file::MmapPath(source_.path, nullptr)) {
+ if (map.value().getDataPtr() && map.value().getDataLength() > 0) {
+ return util::make_unique<MmappedData>(std::move(map.value()));
}
- return {};
+ return util::make_unique<EmptyData>();
+ }
+ return {};
}
-const Source& RegularFile::getSource() const {
- return mSource;
+const Source& RegularFile::GetSource() const { return source_; }
+
+FileCollectionIterator::FileCollectionIterator(FileCollection* collection)
+ : current_(collection->files_.begin()), end_(collection->files_.end()) {}
+
+bool FileCollectionIterator::HasNext() { return current_ != end_; }
+
+IFile* FileCollectionIterator::Next() {
+ IFile* result = current_->second.get();
+ ++current_;
+ return result;
}
-FileCollectionIterator::FileCollectionIterator(FileCollection* collection) :
- mCurrent(collection->mFiles.begin()), mEnd(collection->mFiles.end()) {
+IFile* FileCollection::InsertFile(const StringPiece& path) {
+ return (files_[path.ToString()] =
+ util::make_unique<RegularFile>(Source(path)))
+ .get();
}
-bool FileCollectionIterator::hasNext() {
- return mCurrent != mEnd;
+IFile* FileCollection::FindFile(const StringPiece& path) {
+ auto iter = files_.find(path.ToString());
+ if (iter != files_.end()) {
+ return iter->second.get();
+ }
+ return nullptr;
}
-IFile* FileCollectionIterator::next() {
- IFile* result = mCurrent->second.get();
- ++mCurrent;
- return result;
+std::unique_ptr<IFileCollectionIterator> FileCollection::Iterator() {
+ return util::make_unique<FileCollectionIterator>(this);
}
-IFile* FileCollection::insertFile(const StringPiece& path) {
- return (mFiles[path.toString()] = util::make_unique<RegularFile>(Source(path))).get();
-}
-
-IFile* FileCollection::findFile(const StringPiece& path) {
- auto iter = mFiles.find(path.toString());
- if (iter != mFiles.end()) {
- return iter->second.get();
- }
- return nullptr;
-}
-
-std::unique_ptr<IFileCollectionIterator> FileCollection::iterator() {
- return util::make_unique<FileCollectionIterator>(this);
-}
-
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h
index 72a932a..84f851f 100644
--- a/tools/aapt2/io/FileSystem.h
+++ b/tools/aapt2/io/FileSystem.h
@@ -17,10 +17,10 @@
#ifndef AAPT_IO_FILESYSTEM_H
#define AAPT_IO_FILESYSTEM_H
-#include "io/File.h"
-
#include <map>
+#include "io/File.h"
+
namespace aapt {
namespace io {
@@ -28,47 +28,47 @@
* A regular file from the file system. Uses mmap to open the data.
*/
class RegularFile : public IFile {
-public:
- explicit RegularFile(const Source& source);
+ public:
+ explicit RegularFile(const Source& source);
- std::unique_ptr<IData> openAsData() override;
- const Source& getSource() const override;
+ std::unique_ptr<IData> OpenAsData() override;
+ const Source& GetSource() const override;
-private:
- Source mSource;
+ private:
+ Source source_;
};
class FileCollection;
class FileCollectionIterator : public IFileCollectionIterator {
-public:
- explicit FileCollectionIterator(FileCollection* collection);
+ public:
+ explicit FileCollectionIterator(FileCollection* collection);
- bool hasNext() override;
- io::IFile* next() override;
+ bool HasNext() override;
+ io::IFile* Next() override;
-private:
- std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
+ private:
+ std::map<std::string, std::unique_ptr<IFile>>::const_iterator current_, end_;
};
/**
* An IFileCollection representing the file system.
*/
class FileCollection : public IFileCollection {
-public:
- /**
- * Adds a file located at path. Returns the IFile representation of that file.
- */
- IFile* insertFile(const StringPiece& path);
- IFile* findFile(const StringPiece& path) override;
- std::unique_ptr<IFileCollectionIterator> iterator() override;
+ public:
+ /**
+ * Adds a file located at path. Returns the IFile representation of that file.
+ */
+ IFile* InsertFile(const StringPiece& path);
+ IFile* FindFile(const StringPiece& path) override;
+ std::unique_ptr<IFileCollectionIterator> Iterator() override;
-private:
- friend class FileCollectionIterator;
- std::map<std::string, std::unique_ptr<IFile>> mFiles;
+ private:
+ friend class FileCollectionIterator;
+ std::map<std::string, std::unique_ptr<IFile>> files_;
};
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
-#endif // AAPT_IO_FILESYSTEM_H
+#endif // AAPT_IO_FILESYSTEM_H
diff --git a/tools/aapt2/io/Io.cpp b/tools/aapt2/io/Io.cpp
index 963c21c..cab4b65 100644
--- a/tools/aapt2/io/Io.cpp
+++ b/tools/aapt2/io/Io.cpp
@@ -22,23 +22,23 @@
namespace aapt {
namespace io {
-bool copy(OutputStream* out, InputStream* in) {
- const void* inBuffer;
- int inLen;
- while (in->Next(&inBuffer, &inLen)) {
- void* outBuffer;
- int outLen;
- if (!out->Next(&outBuffer, &outLen)) {
- return !out->HadError();
- }
-
- const int bytesToCopy = std::min(inLen, outLen);
- memcpy(outBuffer, inBuffer, bytesToCopy);
- out->BackUp(outLen - bytesToCopy);
- in->BackUp(inLen - bytesToCopy);
+bool Copy(OutputStream* out, InputStream* in) {
+ const void* in_buffer;
+ int in_len;
+ while (in->Next(&in_buffer, &in_len)) {
+ void* out_buffer;
+ int out_len;
+ if (!out->Next(&out_buffer, &out_len)) {
+ return !out->HadError();
}
- return !in->HadError();
+
+ const int bytes_to_copy = std::min(in_len, out_len);
+ memcpy(out_buffer, in_buffer, bytes_to_copy);
+ out->BackUp(out_len - bytes_to_copy);
+ in->BackUp(in_len - bytes_to_copy);
+ }
+ return !in->HadError();
}
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/Io.h b/tools/aapt2/io/Io.h
index e1e9107..33cdc7b 100644
--- a/tools/aapt2/io/Io.h
+++ b/tools/aapt2/io/Io.h
@@ -17,9 +17,10 @@
#ifndef AAPT_IO_IO_H
#define AAPT_IO_IO_H
-#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <string>
+#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+
namespace aapt {
namespace io {
@@ -29,13 +30,11 @@
*
* The code style here matches the protobuf style.
*/
-class InputStream : public google::protobuf::io::ZeroCopyInputStream {
-public:
- virtual std::string GetError() const {
- return {};
- }
+class InputStream : public ::google::protobuf::io::ZeroCopyInputStream {
+ public:
+ virtual std::string GetError() const { return {}; }
- virtual bool HadError() const = 0;
+ virtual bool HadError() const = 0;
};
/**
@@ -44,13 +43,11 @@
*
* The code style here matches the protobuf style.
*/
-class OutputStream : public google::protobuf::io::ZeroCopyOutputStream {
-public:
- virtual std::string GetError() const {
- return {};
- }
+class OutputStream : public ::google::protobuf::io::ZeroCopyOutputStream {
+ public:
+ virtual std::string GetError() const { return {}; }
- virtual bool HadError() const = 0;
+ virtual bool HadError() const = 0;
};
/**
@@ -58,9 +55,9 @@
* If there was an error, check the individual streams' HadError/GetError
* methods.
*/
-bool copy(OutputStream* out, InputStream* in);
+bool Copy(OutputStream* out, InputStream* in);
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
#endif /* AAPT_IO_IO_H */
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
index b3e7a02..f4a128e 100644
--- a/tools/aapt2/io/ZipArchive.cpp
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -14,129 +14,128 @@
* limitations under the License.
*/
-#include "Source.h"
#include "io/ZipArchive.h"
-#include "util/Util.h"
-#include <utils/FileMap.h>
-#include <ziparchive/zip_archive.h>
+#include "utils/FileMap.h"
+#include "ziparchive/zip_archive.h"
+
+#include "Source.h"
+#include "util/Util.h"
namespace aapt {
namespace io {
-ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source) :
- mZipHandle(handle), mZipEntry(entry), mSource(source) {
-}
+ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry,
+ const Source& source)
+ : zip_handle_(handle), zip_entry_(entry), source_(source) {}
-std::unique_ptr<IData> ZipFile::openAsData() {
- if (mZipEntry.method == kCompressStored) {
- int fd = GetFileDescriptor(mZipHandle);
+std::unique_ptr<IData> ZipFile::OpenAsData() {
+ if (zip_entry_.method == kCompressStored) {
+ int fd = GetFileDescriptor(zip_handle_);
- android::FileMap fileMap;
- bool result = fileMap.create(nullptr, fd, mZipEntry.offset,
- mZipEntry.uncompressed_length, true);
- if (!result) {
- return {};
- }
- return util::make_unique<MmappedData>(std::move(fileMap));
-
- } else {
- std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(
- new uint8_t[mZipEntry.uncompressed_length]);
- int32_t result = ExtractToMemory(mZipHandle, &mZipEntry, data.get(),
- static_cast<uint32_t>(mZipEntry.uncompressed_length));
- if (result != 0) {
- return {};
- }
- return util::make_unique<MallocData>(std::move(data), mZipEntry.uncompressed_length);
+ android::FileMap file_map;
+ bool result = file_map.create(nullptr, fd, zip_entry_.offset,
+ zip_entry_.uncompressed_length, true);
+ if (!result) {
+ return {};
}
-}
+ return util::make_unique<MmappedData>(std::move(file_map));
-const Source& ZipFile::getSource() const {
- return mSource;
-}
-
-ZipFileCollectionIterator::ZipFileCollectionIterator(ZipFileCollection* collection) :
- mCurrent(collection->mFiles.begin()), mEnd(collection->mFiles.end()) {
-}
-
-bool ZipFileCollectionIterator::hasNext() {
- return mCurrent != mEnd;
-}
-
-IFile* ZipFileCollectionIterator::next() {
- IFile* result = mCurrent->second.get();
- ++mCurrent;
- return result;
-}
-
-ZipFileCollection::ZipFileCollection() : mHandle(nullptr) {
-}
-
-std::unique_ptr<ZipFileCollection> ZipFileCollection::create(const StringPiece& path,
- std::string* outError) {
- constexpr static const int32_t kEmptyArchive = -6;
-
- std::unique_ptr<ZipFileCollection> collection = std::unique_ptr<ZipFileCollection>(
- new ZipFileCollection());
-
- int32_t result = OpenArchive(path.data(), &collection->mHandle);
+ } else {
+ std::unique_ptr<uint8_t[]> data =
+ std::unique_ptr<uint8_t[]>(new uint8_t[zip_entry_.uncompressed_length]);
+ int32_t result =
+ ExtractToMemory(zip_handle_, &zip_entry_, data.get(),
+ static_cast<uint32_t>(zip_entry_.uncompressed_length));
if (result != 0) {
- // If a zip is empty, result will be an error code. This is fine and we should
- // return an empty ZipFileCollection.
- if (result == kEmptyArchive) {
- return collection;
- }
-
- if (outError) *outError = ErrorCodeString(result);
- return {};
+ return {};
}
-
- void* cookie = nullptr;
- result = StartIteration(collection->mHandle, &cookie, nullptr, nullptr);
- if (result != 0) {
- if (outError) *outError = ErrorCodeString(result);
- return {};
- }
-
- using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
- IterationEnder iterationEnder(cookie, EndIteration);
-
- ZipString zipEntryName;
- ZipEntry zipData;
- while ((result = Next(cookie, &zipData, &zipEntryName)) == 0) {
- std::string zipEntryPath = std::string(reinterpret_cast<const char*>(zipEntryName.name),
- zipEntryName.name_length);
- std::string nestedPath = path.toString() + "@" + zipEntryPath;
- collection->mFiles[zipEntryPath] = util::make_unique<ZipFile>(collection->mHandle,
- zipData,
- Source(nestedPath));
- }
-
- if (result != -1) {
- if (outError) *outError = ErrorCodeString(result);
- return {};
- }
- return collection;
+ return util::make_unique<MallocData>(std::move(data),
+ zip_entry_.uncompressed_length);
+ }
}
-IFile* ZipFileCollection::findFile(const StringPiece& path) {
- auto iter = mFiles.find(path.toString());
- if (iter != mFiles.end()) {
- return iter->second.get();
- }
- return nullptr;
+const Source& ZipFile::GetSource() const { return source_; }
+
+ZipFileCollectionIterator::ZipFileCollectionIterator(
+ ZipFileCollection* collection)
+ : current_(collection->files_.begin()), end_(collection->files_.end()) {}
+
+bool ZipFileCollectionIterator::HasNext() { return current_ != end_; }
+
+IFile* ZipFileCollectionIterator::Next() {
+ IFile* result = current_->second.get();
+ ++current_;
+ return result;
}
-std::unique_ptr<IFileCollectionIterator> ZipFileCollection::iterator() {
- return util::make_unique<ZipFileCollectionIterator>(this);
+ZipFileCollection::ZipFileCollection() : handle_(nullptr) {}
+
+std::unique_ptr<ZipFileCollection> ZipFileCollection::Create(
+ const StringPiece& path, std::string* out_error) {
+ constexpr static const int32_t kEmptyArchive = -6;
+
+ std::unique_ptr<ZipFileCollection> collection =
+ std::unique_ptr<ZipFileCollection>(new ZipFileCollection());
+
+ int32_t result = OpenArchive(path.data(), &collection->handle_);
+ if (result != 0) {
+ // If a zip is empty, result will be an error code. This is fine and we
+ // should
+ // return an empty ZipFileCollection.
+ if (result == kEmptyArchive) {
+ return collection;
+ }
+
+ if (out_error) *out_error = ErrorCodeString(result);
+ return {};
+ }
+
+ void* cookie = nullptr;
+ result = StartIteration(collection->handle_, &cookie, nullptr, nullptr);
+ if (result != 0) {
+ if (out_error) *out_error = ErrorCodeString(result);
+ return {};
+ }
+
+ using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
+ IterationEnder iteration_ender(cookie, EndIteration);
+
+ ZipString zip_entry_name;
+ ZipEntry zip_data;
+ while ((result = Next(cookie, &zip_data, &zip_entry_name)) == 0) {
+ std::string zip_entry_path =
+ std::string(reinterpret_cast<const char*>(zip_entry_name.name),
+ zip_entry_name.name_length);
+ std::string nested_path = path.ToString() + "@" + zip_entry_path;
+ collection->files_[zip_entry_path] = util::make_unique<ZipFile>(
+ collection->handle_, zip_data, Source(nested_path));
+ }
+
+ if (result != -1) {
+ if (out_error) *out_error = ErrorCodeString(result);
+ return {};
+ }
+ return collection;
+}
+
+IFile* ZipFileCollection::FindFile(const StringPiece& path) {
+ auto iter = files_.find(path.ToString());
+ if (iter != files_.end()) {
+ return iter->second.get();
+ }
+ return nullptr;
+}
+
+std::unique_ptr<IFileCollectionIterator> ZipFileCollection::Iterator() {
+ return util::make_unique<ZipFileCollectionIterator>(this);
}
ZipFileCollection::~ZipFileCollection() {
- if (mHandle) {
- CloseArchive(mHandle);
- }
+ if (handle_) {
+ CloseArchive(handle_);
+ }
}
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h
index 565588e..85ca1ae 100644
--- a/tools/aapt2/io/ZipArchive.h
+++ b/tools/aapt2/io/ZipArchive.h
@@ -17,67 +17,70 @@
#ifndef AAPT_IO_ZIPARCHIVE_H
#define AAPT_IO_ZIPARCHIVE_H
-#include "io/File.h"
-#include "util/StringPiece.h"
+#include "ziparchive/zip_archive.h"
#include <map>
-#include <ziparchive/zip_archive.h>
+
+#include "io/File.h"
+#include "util/StringPiece.h"
namespace aapt {
namespace io {
/**
- * An IFile representing a file within a ZIP archive. If the file is compressed, it is uncompressed
- * and copied into memory when opened. Otherwise it is mmapped from the ZIP archive.
+ * An IFile representing a file within a ZIP archive. If the file is compressed,
+ * it is uncompressed
+ * and copied into memory when opened. Otherwise it is mmapped from the ZIP
+ * archive.
*/
class ZipFile : public IFile {
-public:
- ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source);
+ public:
+ ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source);
- std::unique_ptr<IData> openAsData() override;
- const Source& getSource() const override;
+ std::unique_ptr<IData> OpenAsData() override;
+ const Source& GetSource() const override;
-private:
- ZipArchiveHandle mZipHandle;
- ZipEntry mZipEntry;
- Source mSource;
+ private:
+ ZipArchiveHandle zip_handle_;
+ ZipEntry zip_entry_;
+ Source source_;
};
class ZipFileCollection;
class ZipFileCollectionIterator : public IFileCollectionIterator {
-public:
- explicit ZipFileCollectionIterator(ZipFileCollection* collection);
+ public:
+ explicit ZipFileCollectionIterator(ZipFileCollection* collection);
- bool hasNext() override;
- io::IFile* next() override;
+ bool HasNext() override;
+ io::IFile* Next() override;
-private:
- std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
+ private:
+ std::map<std::string, std::unique_ptr<IFile>>::const_iterator current_, end_;
};
/**
* An IFileCollection that represents a ZIP archive and the entries within it.
*/
class ZipFileCollection : public IFileCollection {
-public:
- static std::unique_ptr<ZipFileCollection> create(const StringPiece& path,
- std::string* outError);
+ public:
+ static std::unique_ptr<ZipFileCollection> Create(const StringPiece& path,
+ std::string* outError);
- io::IFile* findFile(const StringPiece& path) override;
- std::unique_ptr<IFileCollectionIterator> iterator() override;
+ io::IFile* FindFile(const StringPiece& path) override;
+ std::unique_ptr<IFileCollectionIterator> Iterator() override;
- ~ZipFileCollection() override;
+ ~ZipFileCollection() override;
-private:
- friend class ZipFileCollectionIterator;
- ZipFileCollection();
+ private:
+ friend class ZipFileCollectionIterator;
+ ZipFileCollection();
- ZipArchiveHandle mHandle;
- std::map<std::string, std::unique_ptr<IFile>> mFiles;
+ ZipArchiveHandle handle_;
+ std::map<std::string, std::unique_ptr<IFile>> files_;
};
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
#endif /* AAPT_IO_ZIPARCHIVE_H */
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 23ff8ab..2951e5c 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -15,69 +15,71 @@
*/
#include "java/AnnotationProcessor.h"
-#include "util/Util.h"
#include <algorithm>
+#include "util/Util.h"
+
namespace aapt {
-void AnnotationProcessor::appendCommentLine(std::string& comment) {
- static const std::string sDeprecated = "@deprecated";
- static const std::string sSystemApi = "@SystemApi";
+void AnnotationProcessor::AppendCommentLine(std::string& comment) {
+ static const std::string sDeprecated = "@deprecated";
+ static const std::string sSystemApi = "@SystemApi";
- if (comment.find(sDeprecated) != std::string::npos) {
- mAnnotationBitMask |= kDeprecated;
- }
+ if (comment.find(sDeprecated) != std::string::npos) {
+ annotation_bit_mask_ |= kDeprecated;
+ }
- std::string::size_type idx = comment.find(sSystemApi);
- if (idx != std::string::npos) {
- mAnnotationBitMask |= kSystemApi;
- comment.erase(comment.begin() + idx, comment.begin() + idx + sSystemApi.size());
- }
+ std::string::size_type idx = comment.find(sSystemApi);
+ if (idx != std::string::npos) {
+ annotation_bit_mask_ |= kSystemApi;
+ comment.erase(comment.begin() + idx,
+ comment.begin() + idx + sSystemApi.size());
+ }
- if (util::trimWhitespace(comment).empty()) {
- return;
- }
+ if (util::TrimWhitespace(comment).empty()) {
+ return;
+ }
- if (!mHasComments) {
- mHasComments = true;
- mComment << "/**";
- }
+ if (!has_comments_) {
+ has_comments_ = true;
+ comment_ << "/**";
+ }
- mComment << "\n * " << std::move(comment);
+ comment_ << "\n * " << std::move(comment);
}
-void AnnotationProcessor::appendComment(const StringPiece& comment) {
- // We need to process line by line to clean-up whitespace and append prefixes.
- for (StringPiece line : util::tokenize(comment, '\n')) {
- line = util::trimWhitespace(line);
- if (!line.empty()) {
- std::string lineCopy = line.toString();
- appendCommentLine(lineCopy);
- }
+void AnnotationProcessor::AppendComment(const StringPiece& comment) {
+ // We need to process line by line to clean-up whitespace and append prefixes.
+ for (StringPiece line : util::Tokenize(comment, '\n')) {
+ line = util::TrimWhitespace(line);
+ if (!line.empty()) {
+ std::string lineCopy = line.ToString();
+ AppendCommentLine(lineCopy);
}
+ }
}
-void AnnotationProcessor::appendNewLine() {
- mComment << "\n *";
+void AnnotationProcessor::AppendNewLine() { comment_ << "\n *"; }
+
+void AnnotationProcessor::WriteToStream(std::ostream* out,
+ const StringPiece& prefix) const {
+ if (has_comments_) {
+ std::string result = comment_.str();
+ for (StringPiece line : util::Tokenize(result, '\n')) {
+ *out << prefix << line << "\n";
+ }
+ *out << prefix << " */"
+ << "\n";
+ }
+
+ if (annotation_bit_mask_ & kDeprecated) {
+ *out << prefix << "@Deprecated\n";
+ }
+
+ if (annotation_bit_mask_ & kSystemApi) {
+ *out << prefix << "@android.annotation.SystemApi\n";
+ }
}
-void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) const {
- if (mHasComments) {
- std::string result = mComment.str();
- for (StringPiece line : util::tokenize(result, '\n')) {
- *out << prefix << line << "\n";
- }
- *out << prefix << " */" << "\n";
- }
-
- if (mAnnotationBitMask & kDeprecated) {
- *out << prefix << "@Deprecated\n";
- }
-
- if (mAnnotationBitMask & kSystemApi) {
- *out << prefix << "@android.annotation.SystemApi\n";
- }
-}
-
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index cfc32f3..666a7f3 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -17,11 +17,11 @@
#ifndef AAPT_JAVA_ANNOTATIONPROCESSOR_H
#define AAPT_JAVA_ANNOTATIONPROCESSOR_H
-#include "util/StringPiece.h"
-
#include <sstream>
#include <string>
+#include "util/StringPiece.h"
+
namespace aapt {
/**
@@ -52,34 +52,36 @@
*
*/
class AnnotationProcessor {
-public:
- /**
- * Adds more comments. Since resources can have various values with different configurations,
- * we need to collect all the comments.
- */
- void appendComment(const StringPiece& comment);
+ public:
+ /**
+ * Adds more comments. Since resources can have various values with different
+ * configurations,
+ * we need to collect all the comments.
+ */
+ void AppendComment(const StringPiece& comment);
- void appendNewLine();
+ void AppendNewLine();
- /**
- * Writes the comments and annotations to the stream, with the given prefix before each line.
- */
- void writeToStream(std::ostream* out, const StringPiece& prefix) const;
+ /**
+ * Writes the comments and annotations to the stream, with the given prefix
+ * before each line.
+ */
+ void WriteToStream(std::ostream* out, const StringPiece& prefix) const;
-private:
- enum : uint32_t {
- kDeprecated = 0x01,
- kSystemApi = 0x02,
- };
+ private:
+ enum : uint32_t {
+ kDeprecated = 0x01,
+ kSystemApi = 0x02,
+ };
- std::stringstream mComment;
- std::stringstream mAnnotations;
- bool mHasComments = false;
- uint32_t mAnnotationBitMask = 0;
+ std::stringstream comment_;
+ std::stringstream mAnnotations;
+ bool has_comments_ = false;
+ uint32_t annotation_bit_mask_ = 0;
- void appendCommentLine(std::string& line);
+ void AppendCommentLine(std::string& line);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_JAVA_ANNOTATIONPROCESSOR_H */
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index 5a39add..3e43c42 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -15,38 +15,39 @@
*/
#include "java/AnnotationProcessor.h"
+
#include "test/Test.h"
namespace aapt {
TEST(AnnotationProcessorTest, EmitsDeprecated) {
- const char* comment = "Some comment, and it should contain a marker word, "
- "something that marks this resource as nor needed. "
- "{@deprecated That's the marker! }";
+ const char* comment =
+ "Some comment, and it should contain a marker word, "
+ "something that marks this resource as nor needed. "
+ "{@deprecated That's the marker! }";
- AnnotationProcessor processor;
- processor.appendComment(comment);
+ AnnotationProcessor processor;
+ processor.AppendComment(comment);
- std::stringstream result;
- processor.writeToStream(&result, "");
- std::string annotations = result.str();
+ std::stringstream result;
+ processor.WriteToStream(&result, "");
+ std::string annotations = result.str();
- EXPECT_NE(std::string::npos, annotations.find("@Deprecated"));
+ EXPECT_NE(std::string::npos, annotations.find("@Deprecated"));
}
TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
- AnnotationProcessor processor;
- processor.appendComment("@SystemApi This is a system API");
+ AnnotationProcessor processor;
+ processor.AppendComment("@SystemApi This is a system API");
- std::stringstream result;
- processor.writeToStream(&result, "");
- std::string annotations = result.str();
+ std::stringstream result;
+ processor.WriteToStream(&result, "");
+ std::string annotations = result.str();
- EXPECT_NE(std::string::npos, annotations.find("@android.annotation.SystemApi"));
- EXPECT_EQ(std::string::npos, annotations.find("@SystemApi"));
- EXPECT_NE(std::string::npos, annotations.find("This is a system API"));
+ EXPECT_NE(std::string::npos,
+ annotations.find("@android.annotation.SystemApi"));
+ EXPECT_EQ(std::string::npos, annotations.find("@SystemApi"));
+ EXPECT_NE(std::string::npos, annotations.find("This is a system API"));
}
-} // namespace aapt
-
-
+} // namespace aapt
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index 08f2c8b..f1f1f92 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -15,61 +15,59 @@
*/
#include "java/ClassDefinition.h"
-#include "util/StringPiece.h"
-#include <ostream>
+#include "util/StringPiece.h"
namespace aapt {
bool ClassDefinition::empty() const {
- for (const std::unique_ptr<ClassMember>& member : mMembers) {
- if (!member->empty()) {
- return false;
- }
+ for (const std::unique_ptr<ClassMember>& member : members_) {
+ if (!member->empty()) {
+ return false;
}
- return true;
+ }
+ return true;
}
-void ClassDefinition::writeToStream(const StringPiece& prefix, bool final,
+void ClassDefinition::WriteToStream(const StringPiece& prefix, bool final,
std::ostream* out) const {
- if (mMembers.empty() && !mCreateIfEmpty) {
- return;
- }
+ if (members_.empty() && !create_if_empty_) {
+ return;
+ }
- ClassMember::writeToStream(prefix, final, out);
+ ClassMember::WriteToStream(prefix, final, out);
- *out << prefix << "public ";
- if (mQualifier == ClassQualifier::Static) {
- *out << "static ";
- }
- *out << "final class " << mName << " {\n";
+ *out << prefix << "public ";
+ if (qualifier_ == ClassQualifier::Static) {
+ *out << "static ";
+ }
+ *out << "final class " << name_ << " {\n";
- std::string newPrefix = prefix.toString();
- newPrefix.append(kIndent);
+ std::string new_prefix = prefix.ToString();
+ new_prefix.append(kIndent);
- for (const std::unique_ptr<ClassMember>& member : mMembers) {
- member->writeToStream(newPrefix, final, out);
- *out << "\n";
- }
+ for (const std::unique_ptr<ClassMember>& member : members_) {
+ member->WriteToStream(new_prefix, final, out);
+ *out << "\n";
+ }
- *out << prefix << "}";
+ *out << prefix << "}";
}
constexpr static const char* sWarningHeader =
- "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
- " *\n"
- " * This class was automatically generated by the\n"
- " * aapt tool from the resource data it found. It\n"
- " * should not be modified by hand.\n"
- " */\n\n";
+ "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
+ " *\n"
+ " * This class was automatically generated by the\n"
+ " * aapt tool from the resource data it found. It\n"
+ " * should not be modified by hand.\n"
+ " */\n\n";
-bool ClassDefinition::writeJavaFile(const ClassDefinition* def,
- const StringPiece& package,
- bool final,
+bool ClassDefinition::WriteJavaFile(const ClassDefinition* def,
+ const StringPiece& package, bool final,
std::ostream* out) {
- *out << sWarningHeader << "package " << package << ";\n\n";
- def->writeToStream("", final, out);
- return bool(*out);
+ *out << sWarningHeader << "package " << package << ";\n\n";
+ def->WriteToStream("", final, out);
+ return bool(*out);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index e84c274..d8b61d9 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -17,15 +17,16 @@
#ifndef AAPT_JAVA_CLASSDEFINITION_H
#define AAPT_JAVA_CLASSDEFINITION_H
+#include <ostream>
+#include <string>
+
+#include "android-base/macros.h"
+
#include "Resource.h"
#include "java/AnnotationProcessor.h"
#include "util/StringPiece.h"
#include "util/Util.h"
-#include <android-base/macros.h>
-#include <sstream>
-#include <string>
-
namespace aapt {
// The number of attributes to emit per line in a Styleable array.
@@ -33,46 +34,43 @@
constexpr static const char* kIndent = " ";
class ClassMember {
-public:
- virtual ~ClassMember() = default;
+ public:
+ virtual ~ClassMember() = default;
- AnnotationProcessor* getCommentBuilder() {
- return &mProcessor;
- }
+ AnnotationProcessor* GetCommentBuilder() { return &processor_; }
- virtual bool empty() const = 0;
+ virtual bool empty() const = 0;
- virtual void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const {
- mProcessor.writeToStream(out, prefix);
- }
+ virtual void WriteToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const {
+ processor_.WriteToStream(out, prefix);
+ }
-private:
- AnnotationProcessor mProcessor;
+ private:
+ AnnotationProcessor processor_;
};
template <typename T>
class PrimitiveMember : public ClassMember {
-public:
- PrimitiveMember(const StringPiece& name, const T& val) :
- mName(name.toString()), mVal(val) {
- }
+ public:
+ PrimitiveMember(const StringPiece& name, const T& val)
+ : name_(name.ToString()), val_(val) {}
- bool empty() const override {
- return false;
- }
+ bool empty() const override { return false; }
- void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
- ClassMember::writeToStream(prefix, final, out);
+ void WriteToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const override {
+ ClassMember::WriteToStream(prefix, final, out);
- *out << prefix << "public static " << (final ? "final " : "")
- << "int " << mName << "=" << mVal << ";";
- }
+ *out << prefix << "public static " << (final ? "final " : "") << "int "
+ << name_ << "=" << val_ << ";";
+ }
-private:
- std::string mName;
- T mVal;
+ private:
+ std::string name_;
+ T val_;
- DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
};
/**
@@ -80,27 +78,25 @@
*/
template <>
class PrimitiveMember<std::string> : public ClassMember {
-public:
- PrimitiveMember(const StringPiece& name, const std::string& val) :
- mName(name.toString()), mVal(val) {
- }
+ public:
+ PrimitiveMember(const StringPiece& name, const std::string& val)
+ : name_(name.ToString()), val_(val) {}
- bool empty() const override {
- return false;
- }
+ bool empty() const override { return false; }
- void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
- ClassMember::writeToStream(prefix, final, out);
+ void WriteToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const override {
+ ClassMember::WriteToStream(prefix, final, out);
- *out << prefix << "public static " << (final ? "final " : "")
- << "String " << mName << "=\"" << mVal << "\";";
- }
+ *out << prefix << "public static " << (final ? "final " : "") << "String "
+ << name_ << "=\"" << val_ << "\";";
+ }
-private:
- std::string mName;
- std::string mVal;
+ private:
+ std::string name_;
+ std::string val_;
- DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
};
using IntMember = PrimitiveMember<uint32_t>;
@@ -109,80 +105,75 @@
template <typename T>
class PrimitiveArrayMember : public ClassMember {
-public:
- explicit PrimitiveArrayMember(const StringPiece& name) :
- mName(name.toString()) {
+ public:
+ explicit PrimitiveArrayMember(const StringPiece& name)
+ : name_(name.ToString()) {}
+
+ void AddElement(const T& val) { elements_.push_back(val); }
+
+ bool empty() const override { return false; }
+
+ void WriteToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const override {
+ ClassMember::WriteToStream(prefix, final, out);
+
+ *out << prefix << "public static final int[] " << name_ << "={";
+
+ const auto begin = elements_.begin();
+ const auto end = elements_.end();
+ for (auto current = begin; current != end; ++current) {
+ if (std::distance(begin, current) % kAttribsPerLine == 0) {
+ *out << "\n" << prefix << kIndent << kIndent;
+ }
+
+ *out << *current;
+ if (std::distance(current, end) > 1) {
+ *out << ", ";
+ }
}
+ *out << "\n" << prefix << kIndent << "};";
+ }
- void addElement(const T& val) {
- mElements.push_back(val);
- }
+ private:
+ std::string name_;
+ std::vector<T> elements_;
- bool empty() const override {
- return false;
- }
-
- void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
- ClassMember::writeToStream(prefix, final, out);
-
- *out << prefix << "public static final int[] " << mName << "={";
-
- const auto begin = mElements.begin();
- const auto end = mElements.end();
- for (auto current = begin; current != end; ++current) {
- if (std::distance(begin, current) % kAttribsPerLine == 0) {
- *out << "\n" << prefix << kIndent << kIndent;
- }
-
- *out << *current;
- if (std::distance(current, end) > 1) {
- *out << ", ";
- }
- }
- *out << "\n" << prefix << kIndent <<"};";
- }
-
-private:
- std::string mName;
- std::vector<T> mElements;
-
- DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember);
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember);
};
using ResourceArrayMember = PrimitiveArrayMember<ResourceId>;
-enum class ClassQualifier {
- None,
- Static
-};
+enum class ClassQualifier { None, Static };
class ClassDefinition : public ClassMember {
-public:
- static bool writeJavaFile(const ClassDefinition* def,
- const StringPiece& package,
- bool final,
- std::ostream* out);
+ public:
+ static bool WriteJavaFile(const ClassDefinition* def,
+ const StringPiece& package, bool final,
+ std::ostream* out);
- ClassDefinition(const StringPiece& name, ClassQualifier qualifier, bool createIfEmpty) :
- mName(name.toString()), mQualifier(qualifier), mCreateIfEmpty(createIfEmpty) {
- }
+ ClassDefinition(const StringPiece& name, ClassQualifier qualifier,
+ bool createIfEmpty)
+ : name_(name.ToString()),
+ qualifier_(qualifier),
+ create_if_empty_(createIfEmpty) {}
- void addMember(std::unique_ptr<ClassMember> member) {
- mMembers.push_back(std::move(member));
- }
+ void AddMember(std::unique_ptr<ClassMember> member) {
+ members_.push_back(std::move(member));
+ }
- bool empty() const override;
- void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override;
+ bool empty() const override;
+ void WriteToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const override;
-private:
- std::string mName;
- ClassQualifier mQualifier;
- bool mCreateIfEmpty;
- std::vector<std::unique_ptr<ClassMember>> mMembers;
+ private:
+ std::string name_;
+ ClassQualifier qualifier_;
+ bool create_if_empty_;
+ std::vector<std::unique_ptr<ClassMember>> members_;
- DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
+ DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_JAVA_CLASSDEFINITION_H */
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index fbaefb1..6e7c7078 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -14,16 +14,7 @@
* limitations under the License.
*/
-#include "NameMangler.h"
-#include "Resource.h"
-#include "ResourceTable.h"
-#include "ResourceValues.h"
-#include "ValueVisitor.h"
-#include "java/AnnotationProcessor.h"
-#include "java/ClassDefinition.h"
#include "java/JavaClassGenerator.h"
-#include "process/SymbolTable.h"
-#include "util/StringPiece.h"
#include <algorithm>
#include <ostream>
@@ -31,42 +22,49 @@
#include <sstream>
#include <tuple>
+#include "android-base/logging.h"
+
+#include "NameMangler.h"
+#include "Resource.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
+#include "java/AnnotationProcessor.h"
+#include "java/ClassDefinition.h"
+#include "process/SymbolTable.h"
+#include "util/StringPiece.h"
+
namespace aapt {
-JavaClassGenerator::JavaClassGenerator(IAaptContext* context, ResourceTable* table,
- const JavaClassGeneratorOptions& options) :
- mContext(context), mTable(table), mOptions(options) {
-}
-
static const std::set<StringPiece> sJavaIdentifiers = {
- "abstract", "assert", "boolean", "break", "byte",
- "case", "catch", "char", "class", "const", "continue",
- "default", "do", "double", "else", "enum", "extends",
- "final", "finally", "float", "for", "goto", "if",
- "implements", "import", "instanceof", "int", "interface",
- "long", "native", "new", "package", "private", "protected",
- "public", "return", "short", "static", "strictfp", "super",
- "switch", "synchronized", "this", "throw", "throws",
- "transient", "try", "void", "volatile", "while", "true",
- "false", "null"
-};
+ "abstract", "assert", "boolean", "break", "byte",
+ "case", "catch", "char", "class", "const",
+ "continue", "default", "do", "double", "else",
+ "enum", "extends", "final", "finally", "float",
+ "for", "goto", "if", "implements", "import",
+ "instanceof", "int", "interface", "long", "native",
+ "new", "package", "private", "protected", "public",
+ "return", "short", "static", "strictfp", "super",
+ "switch", "synchronized", "this", "throw", "throws",
+ "transient", "try", "void", "volatile", "while",
+ "true", "false", "null"};
-static bool isValidSymbol(const StringPiece& symbol) {
- return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
+static bool IsValidSymbol(const StringPiece& symbol) {
+ return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
}
/*
* Java symbols can not contain . or -, but those are valid in a resource name.
* Replace those with '_'.
*/
-static std::string transform(const StringPiece& symbol) {
- std::string output = symbol.toString();
- for (char& c : output) {
- if (c == '.' || c == '-') {
- c = '_';
- }
+static std::string Transform(const StringPiece& symbol) {
+ std::string output = symbol.ToString();
+ for (char& c : output) {
+ if (c == '.' || c == '-') {
+ c = '_';
}
- return output;
+ }
+ return output;
}
/**
@@ -80,475 +78,519 @@
* Foo_android_bar
* Foo_bar
*/
-static std::string transformNestedAttr(const ResourceNameRef& attrName,
- const std::string& styleableClassName,
- const StringPiece& packageNameToGenerate) {
- std::string output = styleableClassName;
+static std::string TransformNestedAttr(
+ const ResourceNameRef& attr_name, const std::string& styleable_class_name,
+ const StringPiece& package_name_to_generate) {
+ std::string output = styleable_class_name;
- // We may reference IDs from other packages, so prefix the entry name with
- // the package.
- if (!attrName.package.empty() && packageNameToGenerate != attrName.package) {
- output += "_" + transform(attrName.package);
- }
- output += "_" + transform(attrName.entry);
- return output;
+ // We may reference IDs from other packages, so prefix the entry name with
+ // the package.
+ if (!attr_name.package.empty() &&
+ package_name_to_generate != attr_name.package) {
+ output += "_" + Transform(attr_name.package);
+ }
+ output += "_" + Transform(attr_name.entry);
+ return output;
}
-static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
- const uint32_t typeMask = attr->typeMask;
- if (typeMask & android::ResTable_map::TYPE_REFERENCE) {
- processor->appendComment(
- "<p>May be a reference to another resource, in the form\n"
- "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
- "attribute in the form\n"
- "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
+static void AddAttributeFormatDoc(AnnotationProcessor* processor,
+ Attribute* attr) {
+ const uint32_t type_mask = attr->type_mask;
+ if (type_mask & android::ResTable_map::TYPE_REFERENCE) {
+ processor->AppendComment(
+ "<p>May be a reference to another resource, in the form\n"
+ "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a "
+ "theme\n"
+ "attribute in the form\n"
+ "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
+ }
+
+ if (type_mask & android::ResTable_map::TYPE_STRING) {
+ processor->AppendComment(
+ "<p>May be a string value, using '\\\\;' to escape characters such as\n"
+ "'\\\\n' or '\\\\uxxxx' for a unicode character;");
+ }
+
+ if (type_mask & android::ResTable_map::TYPE_INTEGER) {
+ processor->AppendComment(
+ "<p>May be an integer value, such as \"<code>100</code>\".");
+ }
+
+ if (type_mask & android::ResTable_map::TYPE_BOOLEAN) {
+ processor->AppendComment(
+ "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
+ "\"<code>false</code>\".");
+ }
+
+ if (type_mask & android::ResTable_map::TYPE_COLOR) {
+ processor->AppendComment(
+ "<p>May be a color value, in the form of "
+ "\"<code>#<i>rgb</i></code>\",\n"
+ "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
+ "\"<code>#<i>aarrggbb</i></code>\".");
+ }
+
+ if (type_mask & android::ResTable_map::TYPE_FLOAT) {
+ processor->AppendComment(
+ "<p>May be a floating point value, such as \"<code>1.2</code>\".");
+ }
+
+ if (type_mask & android::ResTable_map::TYPE_DIMENSION) {
+ processor->AppendComment(
+ "<p>May be a dimension value, which is a floating point number "
+ "appended with a\n"
+ "unit such as \"<code>14.5sp</code>\".\n"
+ "Available units are: px (pixels), dp (density-independent pixels),\n"
+ "sp (scaled pixels based on preferred font size), in (inches), and\n"
+ "mm (millimeters).");
+ }
+
+ if (type_mask & android::ResTable_map::TYPE_FRACTION) {
+ processor->AppendComment(
+ "<p>May be a fractional value, which is a floating point number "
+ "appended with\n"
+ "either % or %p, such as \"<code>14.5%</code>\".\n"
+ "The % suffix always means a percentage of the base size;\n"
+ "the optional %p suffix provides a size relative to some parent "
+ "container.");
+ }
+
+ if (type_mask &
+ (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
+ if (type_mask & android::ResTable_map::TYPE_FLAGS) {
+ processor->AppendComment(
+ "<p>Must be one or more (separated by '|') of the following "
+ "constant values.</p>");
+ } else {
+ processor->AppendComment(
+ "<p>Must be one of the following constant values.</p>");
}
- if (typeMask & android::ResTable_map::TYPE_STRING) {
- processor->appendComment(
- "<p>May be a string value, using '\\\\;' to escape characters such as\n"
- "'\\\\n' or '\\\\uxxxx' for a unicode character;");
+ processor->AppendComment(
+ "<table>\n<colgroup align=\"left\" />\n"
+ "<colgroup align=\"left\" />\n"
+ "<colgroup align=\"left\" />\n"
+ "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
+ for (const Attribute::Symbol& symbol : attr->symbols) {
+ std::stringstream line;
+ line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
+ << "<td>" << std::hex << symbol.value << std::dec << "</td>"
+ << "<td>" << util::TrimWhitespace(symbol.symbol.GetComment())
+ << "</td></tr>";
+ processor->AppendComment(line.str());
}
-
- if (typeMask & android::ResTable_map::TYPE_INTEGER) {
- processor->appendComment("<p>May be an integer value, such as \"<code>100</code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
- processor->appendComment(
- "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
- "\"<code>false</code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_COLOR) {
- processor->appendComment(
- "<p>May be a color value, in the form of \"<code>#<i>rgb</i></code>\",\n"
- "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
- "\"<code>#<i>aarrggbb</i></code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_FLOAT) {
- processor->appendComment(
- "<p>May be a floating point value, such as \"<code>1.2</code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_DIMENSION) {
- processor->appendComment(
- "<p>May be a dimension value, which is a floating point number appended with a\n"
- "unit such as \"<code>14.5sp</code>\".\n"
- "Available units are: px (pixels), dp (density-independent pixels),\n"
- "sp (scaled pixels based on preferred font size), in (inches), and\n"
- "mm (millimeters).");
- }
-
- if (typeMask & android::ResTable_map::TYPE_FRACTION) {
- processor->appendComment(
- "<p>May be a fractional value, which is a floating point number appended with\n"
- "either % or %p, such as \"<code>14.5%</code>\".\n"
- "The % suffix always means a percentage of the base size;\n"
- "the optional %p suffix provides a size relative to some parent container.");
- }
-
- if (typeMask & (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
- if (typeMask & android::ResTable_map::TYPE_FLAGS) {
- processor->appendComment(
- "<p>Must be one or more (separated by '|') of the following "
- "constant values.</p>");
- } else {
- processor->appendComment("<p>Must be one of the following constant values.</p>");
- }
-
- processor->appendComment("<table>\n<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\" />\n"
- "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
- for (const Attribute::Symbol& symbol : attr->symbols) {
- std::stringstream line;
- line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
- << "<td>" << std::hex << symbol.value << std::dec << "</td>"
- << "<td>" << util::trimWhitespace(symbol.symbol.getComment()) << "</td></tr>";
- processor->appendComment(line.str());
- }
- processor->appendComment("</table>");
- }
+ processor->AppendComment("</table>");
+ }
}
-bool JavaClassGenerator::skipSymbol(SymbolState state) {
- switch (mOptions.types) {
+JavaClassGenerator::JavaClassGenerator(IAaptContext* context,
+ ResourceTable* table,
+ const JavaClassGeneratorOptions& options)
+ : context_(context), table_(table), options_(options) {}
+
+bool JavaClassGenerator::SkipSymbol(SymbolState state) {
+ switch (options_.types) {
case JavaClassGeneratorOptions::SymbolTypes::kAll:
- return false;
+ return false;
case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
- return state == SymbolState::kUndefined;
+ return state == SymbolState::kUndefined;
case JavaClassGeneratorOptions::SymbolTypes::kPublic:
- return state != SymbolState::kPublic;
- }
- return true;
+ return state != SymbolState::kPublic;
+ }
+ return true;
}
struct StyleableAttr {
- const Reference* attrRef;
- std::string fieldName;
- std::unique_ptr<SymbolTable::Symbol> symbol;
+ const Reference* attr_ref;
+ std::string field_name;
+ std::unique_ptr<SymbolTable::Symbol> symbol;
};
-static bool lessStyleableAttr(const StyleableAttr& lhs, const StyleableAttr& rhs) {
- const ResourceId lhsId = lhs.attrRef->id ? lhs.attrRef->id.value() : ResourceId(0);
- const ResourceId rhsId = rhs.attrRef->id ? rhs.attrRef->id.value() : ResourceId(0);
- if (lhsId < rhsId) {
- return true;
- } else if (lhsId > rhsId) {
- return false;
+static bool less_styleable_attr(const StyleableAttr& lhs,
+ const StyleableAttr& rhs) {
+ const ResourceId lhs_id =
+ lhs.attr_ref->id ? lhs.attr_ref->id.value() : ResourceId(0);
+ const ResourceId rhs_id =
+ rhs.attr_ref->id ? rhs.attr_ref->id.value() : ResourceId(0);
+ if (lhs_id < rhs_id) {
+ return true;
+ } else if (lhs_id > rhs_id) {
+ return false;
+ } else {
+ return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
+ }
+}
+
+void JavaClassGenerator::AddMembersToStyleableClass(
+ const StringPiece& package_name_to_generate, const std::string& entry_name,
+ const Styleable* styleable, ClassDefinition* out_styleable_class_def) {
+ const std::string class_name = Transform(entry_name);
+
+ std::unique_ptr<ResourceArrayMember> styleable_array_def =
+ util::make_unique<ResourceArrayMember>(class_name);
+
+ // This must be sorted by resource ID.
+ std::vector<StyleableAttr> sorted_attributes;
+ sorted_attributes.reserve(styleable->entries.size());
+ for (const auto& attr : styleable->entries) {
+ // If we are not encoding final attributes, the styleable entry may have no
+ // ID if we are building a static library.
+ CHECK(!options_.use_final || attr.id) << "no ID set for Styleable entry";
+ CHECK(bool(attr.name)) << "no name set for Styleable entry";
+
+ // We will need the unmangled, transformed name in the comments and the
+ // field,
+ // so create it once and cache it in this StyleableAttr data structure.
+ StyleableAttr styleable_attr = {};
+ styleable_attr.attr_ref = &attr;
+ styleable_attr.field_name = TransformNestedAttr(
+ attr.name.value(), class_name, package_name_to_generate);
+
+ Reference mangled_reference;
+ mangled_reference.id = attr.id;
+ mangled_reference.name = attr.name;
+ if (mangled_reference.name.value().package.empty()) {
+ mangled_reference.name.value().package =
+ context_->GetCompilationPackage();
+ }
+
+ if (Maybe<ResourceName> mangled_name =
+ context_->GetNameMangler()->MangleName(
+ mangled_reference.name.value())) {
+ mangled_reference.name = mangled_name;
+ }
+
+ // Look up the symbol so that we can write out in the comments what are
+ // possible
+ // legal values for this attribute.
+ const SymbolTable::Symbol* symbol =
+ context_->GetExternalSymbols()->FindByReference(mangled_reference);
+ if (symbol && symbol->attribute) {
+ // Copy the symbol data structure because the returned instance can be
+ // destroyed.
+ styleable_attr.symbol = util::make_unique<SymbolTable::Symbol>(*symbol);
+ }
+ sorted_attributes.push_back(std::move(styleable_attr));
+ }
+
+ // Sort the attributes by ID.
+ std::sort(sorted_attributes.begin(), sorted_attributes.end(),
+ less_styleable_attr);
+
+ const size_t attr_count = sorted_attributes.size();
+ if (attr_count > 0) {
+ // Build the comment string for the Styleable. It includes details about the
+ // child attributes.
+ std::stringstream styleable_comment;
+ if (!styleable->GetComment().empty()) {
+ styleable_comment << styleable->GetComment() << "\n";
} else {
- return lhs.attrRef->name.value() < rhs.attrRef->name.value();
+ styleable_comment << "Attributes that can be used with a " << class_name
+ << ".\n";
}
+
+ styleable_comment << "<p>Includes the following attributes:</p>\n"
+ "<table>\n"
+ "<colgroup align=\"left\" />\n"
+ "<colgroup align=\"left\" />\n"
+ "<tr><th>Attribute</th><th>Description</th></tr>\n";
+
+ for (const StyleableAttr& entry : sorted_attributes) {
+ if (!entry.symbol) {
+ continue;
+ }
+
+ if (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
+ !entry.symbol->is_public) {
+ // Don't write entries for non-public attributes.
+ continue;
+ }
+
+ StringPiece attr_comment_line = entry.symbol->attribute->GetComment();
+ if (attr_comment_line.contains("@removed")) {
+ // Removed attributes are public but hidden from the documentation, so
+ // don't emit
+ // them as part of the class documentation.
+ continue;
+ }
+
+ const ResourceName& attr_name = entry.attr_ref->name.value();
+ styleable_comment << "<tr><td>";
+ styleable_comment << "<code>{@link #" << entry.field_name << " "
+ << (!attr_name.package.empty()
+ ? attr_name.package
+ : context_->GetCompilationPackage())
+ << ":" << attr_name.entry << "}</code>";
+ styleable_comment << "</td>";
+
+ styleable_comment << "<td>";
+
+ // Only use the comment up until the first '.'. This is to stay compatible
+ // with
+ // the way old AAPT did it (presumably to keep it short and to avoid
+ // including
+ // annotations like @hide which would affect this Styleable).
+ auto iter =
+ std::find(attr_comment_line.begin(), attr_comment_line.end(), u'.');
+ if (iter != attr_comment_line.end()) {
+ attr_comment_line =
+ attr_comment_line.substr(0, (iter - attr_comment_line.begin()) + 1);
+ }
+ styleable_comment << attr_comment_line << "</td></tr>\n";
+ }
+ styleable_comment << "</table>\n";
+
+ for (const StyleableAttr& entry : sorted_attributes) {
+ if (!entry.symbol) {
+ continue;
+ }
+
+ if (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
+ !entry.symbol->is_public) {
+ // Don't write entries for non-public attributes.
+ continue;
+ }
+ styleable_comment << "@see #" << entry.field_name << "\n";
+ }
+
+ styleable_array_def->GetCommentBuilder()->AppendComment(
+ styleable_comment.str());
+ }
+
+ // Add the ResourceIds to the array member.
+ for (const StyleableAttr& styleable_attr : sorted_attributes) {
+ styleable_array_def->AddElement(styleable_attr.attr_ref->id
+ ? styleable_attr.attr_ref->id.value()
+ : ResourceId(0));
+ }
+
+ // Add the Styleable array to the Styleable class.
+ out_styleable_class_def->AddMember(std::move(styleable_array_def));
+
+ // Now we emit the indices into the array.
+ for (size_t i = 0; i < attr_count; i++) {
+ const StyleableAttr& styleable_attr = sorted_attributes[i];
+
+ if (!styleable_attr.symbol) {
+ continue;
+ }
+
+ if (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
+ !styleable_attr.symbol->is_public) {
+ // Don't write entries for non-public attributes.
+ continue;
+ }
+
+ StringPiece comment = styleable_attr.attr_ref->GetComment();
+ if (styleable_attr.symbol->attribute && comment.empty()) {
+ comment = styleable_attr.symbol->attribute->GetComment();
+ }
+
+ if (comment.contains("@removed")) {
+ // Removed attributes are public but hidden from the documentation, so
+ // don't emit them
+ // as part of the class documentation.
+ continue;
+ }
+
+ const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
+
+ StringPiece package_name = attr_name.package;
+ if (package_name.empty()) {
+ package_name = context_->GetCompilationPackage();
+ }
+
+ std::unique_ptr<IntMember> index_member = util::make_unique<IntMember>(
+ sorted_attributes[i].field_name, static_cast<uint32_t>(i));
+
+ AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
+
+ if (!comment.empty()) {
+ attr_processor->AppendComment("<p>\n@attr description");
+ attr_processor->AppendComment(comment);
+ } else {
+ std::stringstream default_comment;
+ default_comment << "<p>This symbol is the offset where the "
+ << "{@link " << package_name << ".R.attr#"
+ << Transform(attr_name.entry) << "}\n"
+ << "attribute's value can be found in the "
+ << "{@link #" << class_name << "} array.";
+ attr_processor->AppendComment(default_comment.str());
+ }
+
+ attr_processor->AppendNewLine();
+
+ AddAttributeFormatDoc(attr_processor,
+ styleable_attr.symbol->attribute.get());
+ attr_processor->AppendNewLine();
+
+ std::stringstream doclava_name;
+ doclava_name << "@attr name " << package_name << ":" << attr_name.entry;
+
+ attr_processor->AppendComment(doclava_name.str());
+
+ out_styleable_class_def->AddMember(std::move(index_member));
+ }
}
-void JavaClassGenerator::addMembersToStyleableClass(const StringPiece& packageNameToGenerate,
- const std::string& entryName,
- const Styleable* styleable,
- ClassDefinition* outStyleableClassDef) {
- const std::string className = transform(entryName);
-
- std::unique_ptr<ResourceArrayMember> styleableArrayDef =
- util::make_unique<ResourceArrayMember>(className);
-
- // This must be sorted by resource ID.
- std::vector<StyleableAttr> sortedAttributes;
- sortedAttributes.reserve(styleable->entries.size());
- for (const auto& attr : styleable->entries) {
- // If we are not encoding final attributes, the styleable entry may have no ID
- // if we are building a static library.
- assert((!mOptions.useFinal || attr.id) && "no ID set for Styleable entry");
- assert(attr.name && "no name set for Styleable entry");
-
- // We will need the unmangled, transformed name in the comments and the field,
- // so create it once and cache it in this StyleableAttr data structure.
- StyleableAttr styleableAttr = {};
- styleableAttr.attrRef = &attr;
- styleableAttr.fieldName = transformNestedAttr(attr.name.value(), className,
- packageNameToGenerate);
-
- Reference mangledReference;
- mangledReference.id = attr.id;
- mangledReference.name = attr.name;
- if (mangledReference.name.value().package.empty()) {
- mangledReference.name.value().package = mContext->getCompilationPackage();
- }
-
- if (Maybe<ResourceName> mangledName =
- mContext->getNameMangler()->mangleName(mangledReference.name.value())) {
- mangledReference.name = mangledName;
- }
-
- // Look up the symbol so that we can write out in the comments what are possible
- // legal values for this attribute.
- const SymbolTable::Symbol* symbol = mContext->getExternalSymbols()->findByReference(
- mangledReference);
- if (symbol && symbol->attribute) {
- // Copy the symbol data structure because the returned instance can be destroyed.
- styleableAttr.symbol = util::make_unique<SymbolTable::Symbol>(*symbol);
- }
- sortedAttributes.push_back(std::move(styleableAttr));
+bool JavaClassGenerator::AddMembersToTypeClass(
+ const StringPiece& package_name_to_generate,
+ const ResourceTablePackage* package, const ResourceTableType* type,
+ ClassDefinition* out_type_class_def) {
+ for (const auto& entry : type->entries) {
+ if (SkipSymbol(entry->symbol_status.state)) {
+ continue;
}
- // Sort the attributes by ID.
- std::sort(sortedAttributes.begin(), sortedAttributes.end(), lessStyleableAttr);
-
- const size_t attrCount = sortedAttributes.size();
- if (attrCount > 0) {
- // Build the comment string for the Styleable. It includes details about the
- // child attributes.
- std::stringstream styleableComment;
- if (!styleable->getComment().empty()) {
- styleableComment << styleable->getComment() << "\n";
- } else {
- styleableComment << "Attributes that can be used with a " << className << ".\n";
- }
-
- styleableComment <<
- "<p>Includes the following attributes:</p>\n"
- "<table>\n"
- "<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\" />\n"
- "<tr><th>Attribute</th><th>Description</th></tr>\n";
-
- for (const StyleableAttr& entry : sortedAttributes) {
- if (!entry.symbol) {
- continue;
- }
-
- if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
- !entry.symbol->isPublic) {
- // Don't write entries for non-public attributes.
- continue;
- }
-
- StringPiece attrCommentLine = entry.symbol->attribute->getComment();
- if (attrCommentLine.contains("@removed")) {
- // Removed attributes are public but hidden from the documentation, so don't emit
- // them as part of the class documentation.
- continue;
- }
-
- const ResourceName& attrName = entry.attrRef->name.value();
- styleableComment << "<tr><td>";
- styleableComment << "<code>{@link #"
- << entry.fieldName << " "
- << (!attrName.package.empty()
- ? attrName.package : mContext->getCompilationPackage())
- << ":" << attrName.entry
- << "}</code>";
- styleableComment << "</td>";
-
- styleableComment << "<td>";
-
- // Only use the comment up until the first '.'. This is to stay compatible with
- // the way old AAPT did it (presumably to keep it short and to avoid including
- // annotations like @hide which would affect this Styleable).
- auto iter = std::find(attrCommentLine.begin(), attrCommentLine.end(), u'.');
- if (iter != attrCommentLine.end()) {
- attrCommentLine = attrCommentLine.substr(
- 0, (iter - attrCommentLine.begin()) + 1);
- }
- styleableComment << attrCommentLine << "</td></tr>\n";
- }
- styleableComment << "</table>\n";
-
- for (const StyleableAttr& entry : sortedAttributes) {
- if (!entry.symbol) {
- continue;
- }
-
- if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
- !entry.symbol->isPublic) {
- // Don't write entries for non-public attributes.
- continue;
- }
- styleableComment << "@see #" << entry.fieldName << "\n";
- }
-
- styleableArrayDef->getCommentBuilder()->appendComment(styleableComment.str());
+ ResourceId id;
+ if (package->id && type->id && entry->id) {
+ id = ResourceId(package->id.value(), type->id.value(), entry->id.value());
}
- // Add the ResourceIds to the array member.
- for (const StyleableAttr& styleableAttr : sortedAttributes) {
- styleableArrayDef->addElement(
- styleableAttr.attrRef->id ? styleableAttr.attrRef->id.value() : ResourceId(0));
+ std::string unmangled_package;
+ std::string unmangled_name = entry->name;
+ if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
+ // The entry name was mangled, and we successfully unmangled it.
+ // Check that we want to emit this symbol.
+ if (package->name != unmangled_package) {
+ // Skip the entry if it doesn't belong to the package we're writing.
+ continue;
+ }
+ } else if (package_name_to_generate != package->name) {
+ // We are processing a mangled package name,
+ // but this is a non-mangled resource.
+ continue;
}
- // Add the Styleable array to the Styleable class.
- outStyleableClassDef->addMember(std::move(styleableArrayDef));
-
- // Now we emit the indices into the array.
- for (size_t i = 0; i < attrCount; i++) {
- const StyleableAttr& styleableAttr = sortedAttributes[i];
-
- if (!styleableAttr.symbol) {
- continue;
- }
-
- if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
- !styleableAttr.symbol->isPublic) {
- // Don't write entries for non-public attributes.
- continue;
- }
-
- StringPiece comment = styleableAttr.attrRef->getComment();
- if (styleableAttr.symbol->attribute && comment.empty()) {
- comment = styleableAttr.symbol->attribute->getComment();
- }
-
- if (comment.contains("@removed")) {
- // Removed attributes are public but hidden from the documentation, so don't emit them
- // as part of the class documentation.
- continue;
- }
-
- const ResourceName& attrName = styleableAttr.attrRef->name.value();
-
- StringPiece packageName = attrName.package;
- if (packageName.empty()) {
- packageName = mContext->getCompilationPackage();
- }
-
- std::unique_ptr<IntMember> indexMember = util::make_unique<IntMember>(
- sortedAttributes[i].fieldName, static_cast<uint32_t>(i));
-
- AnnotationProcessor* attrProcessor = indexMember->getCommentBuilder();
-
- if (!comment.empty()) {
- attrProcessor->appendComment("<p>\n@attr description");
- attrProcessor->appendComment(comment);
- } else {
- std::stringstream defaultComment;
- defaultComment
- << "<p>This symbol is the offset where the "
- << "{@link " << packageName << ".R.attr#" << transform(attrName.entry) << "}\n"
- << "attribute's value can be found in the "
- << "{@link #" << className << "} array.";
- attrProcessor->appendComment(defaultComment.str());
- }
-
- attrProcessor->appendNewLine();
-
- addAttributeFormatDoc(attrProcessor, styleableAttr.symbol->attribute.get());
- attrProcessor->appendNewLine();
-
- std::stringstream doclavaName;
- doclavaName << "@attr name " << packageName << ":" << attrName.entry;;
- attrProcessor->appendComment(doclavaName.str());
-
- outStyleableClassDef->addMember(std::move(indexMember));
+ if (!IsValidSymbol(unmangled_name)) {
+ ResourceNameRef resource_name(package_name_to_generate, type->type,
+ unmangled_name);
+ std::stringstream err;
+ err << "invalid symbol name '" << resource_name << "'";
+ error_ = err.str();
+ return false;
}
+
+ if (type->type == ResourceType::kStyleable) {
+ CHECK(!entry->values.empty());
+
+ const Styleable* styleable =
+ static_cast<const Styleable*>(entry->values.front()->value.get());
+
+ // Comments are handled within this method.
+ AddMembersToStyleableClass(package_name_to_generate, unmangled_name,
+ styleable, out_type_class_def);
+ } else {
+ std::unique_ptr<ResourceMember> resource_member =
+ util::make_unique<ResourceMember>(Transform(unmangled_name), id);
+
+ // Build the comments and annotations for this entry.
+ AnnotationProcessor* processor = resource_member->GetCommentBuilder();
+
+ // Add the comments from any <public> tags.
+ if (entry->symbol_status.state != SymbolState::kUndefined) {
+ processor->AppendComment(entry->symbol_status.comment);
+ }
+
+ // Add the comments from all configurations of this entry.
+ for (const auto& config_value : entry->values) {
+ processor->AppendComment(config_value->value->GetComment());
+ }
+
+ // If this is an Attribute, append the format Javadoc.
+ if (!entry->values.empty()) {
+ if (Attribute* attr =
+ ValueCast<Attribute>(entry->values.front()->value.get())) {
+ // We list out the available values for the given attribute.
+ AddAttributeFormatDoc(processor, attr);
+ }
+ }
+
+ out_type_class_def->AddMember(std::move(resource_member));
+ }
+ }
+ return true;
}
-bool JavaClassGenerator::addMembersToTypeClass(const StringPiece& packageNameToGenerate,
- const ResourceTablePackage* package,
- const ResourceTableType* type,
- ClassDefinition* outTypeClassDef) {
-
- for (const auto& entry : type->entries) {
- if (skipSymbol(entry->symbolStatus.state)) {
- continue;
- }
-
- ResourceId id;
- if (package->id && type->id && entry->id) {
- id = ResourceId(package->id.value(), type->id.value(), entry->id.value());
- }
-
- std::string unmangledPackage;
- std::string unmangledName = entry->name;
- if (NameMangler::unmangle(&unmangledName, &unmangledPackage)) {
- // The entry name was mangled, and we successfully unmangled it.
- // Check that we want to emit this symbol.
- if (package->name != unmangledPackage) {
- // Skip the entry if it doesn't belong to the package we're writing.
- continue;
- }
- } else if (packageNameToGenerate != package->name) {
- // We are processing a mangled package name,
- // but this is a non-mangled resource.
- continue;
- }
-
- if (!isValidSymbol(unmangledName)) {
- ResourceNameRef resourceName(packageNameToGenerate, type->type, unmangledName);
- std::stringstream err;
- err << "invalid symbol name '" << resourceName << "'";
- mError = err.str();
- return false;
- }
-
- if (type->type == ResourceType::kStyleable) {
- assert(!entry->values.empty());
-
- const Styleable* styleable = static_cast<const Styleable*>(
- entry->values.front()->value.get());
-
- // Comments are handled within this method.
- addMembersToStyleableClass(packageNameToGenerate, unmangledName, styleable,
- outTypeClassDef);
- } else {
- std::unique_ptr<ResourceMember> resourceMember =
- util::make_unique<ResourceMember>(transform(unmangledName), id);
-
- // Build the comments and annotations for this entry.
- AnnotationProcessor* processor = resourceMember->getCommentBuilder();
-
- // Add the comments from any <public> tags.
- if (entry->symbolStatus.state != SymbolState::kUndefined) {
- processor->appendComment(entry->symbolStatus.comment);
- }
-
- // Add the comments from all configurations of this entry.
- for (const auto& configValue : entry->values) {
- processor->appendComment(configValue->value->getComment());
- }
-
- // If this is an Attribute, append the format Javadoc.
- if (!entry->values.empty()) {
- if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) {
- // We list out the available values for the given attribute.
- addAttributeFormatDoc(processor, attr);
- }
- }
-
- outTypeClassDef->addMember(std::move(resourceMember));
- }
- }
- return true;
+bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
+ std::ostream* out) {
+ return Generate(package_name_to_generate, package_name_to_generate, out);
}
-bool JavaClassGenerator::generate(const StringPiece& packageNameToGenerate, std::ostream* out) {
- return generate(packageNameToGenerate, packageNameToGenerate, out);
+static void AppendJavaDocAnnotations(
+ const std::vector<std::string>& annotations,
+ AnnotationProcessor* processor) {
+ for (const std::string& annotation : annotations) {
+ std::string proper_annotation = "@";
+ proper_annotation += annotation;
+ processor->AppendComment(proper_annotation);
+ }
}
-static void appendJavaDocAnnotations(const std::vector<std::string>& annotations,
- AnnotationProcessor* processor) {
- for (const std::string& annotation : annotations) {
- std::string properAnnotation = "@";
- properAnnotation += annotation;
- processor->appendComment(properAnnotation);
- }
-}
+bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
+ const StringPiece& out_package_name,
+ std::ostream* out) {
+ ClassDefinition r_class("R", ClassQualifier::None, true);
-bool JavaClassGenerator::generate(const StringPiece& packageNameToGenerate,
- const StringPiece& outPackageName, std::ostream* out) {
+ for (const auto& package : table_->packages) {
+ for (const auto& type : package->types) {
+ if (type->type == ResourceType::kAttrPrivate) {
+ continue;
+ }
- ClassDefinition rClass("R", ClassQualifier::None, true);
+ const bool force_creation_if_empty =
+ (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
- for (const auto& package : mTable->packages) {
- for (const auto& type : package->types) {
- if (type->type == ResourceType::kAttrPrivate) {
- continue;
- }
+ std::unique_ptr<ClassDefinition> class_def =
+ util::make_unique<ClassDefinition>(ToString(type->type),
+ ClassQualifier::Static,
+ force_creation_if_empty);
- const bool forceCreationIfEmpty =
- (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
-
- std::unique_ptr<ClassDefinition> classDef = util::make_unique<ClassDefinition>(
- toString(type->type), ClassQualifier::Static, forceCreationIfEmpty);
-
- bool result = addMembersToTypeClass(packageNameToGenerate, package.get(), type.get(),
- classDef.get());
- if (!result) {
- return false;
- }
-
- if (type->type == ResourceType::kAttr) {
- // Also include private attributes in this same class.
- ResourceTableType* privType = package->findType(ResourceType::kAttrPrivate);
- if (privType) {
- result = addMembersToTypeClass(packageNameToGenerate, package.get(), privType,
- classDef.get());
- if (!result) {
- return false;
- }
- }
- }
-
- if (type->type == ResourceType::kStyleable &&
- mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
- // When generating a public R class, we don't want Styleable to be part of the API.
- // It is only emitted for documentation purposes.
- classDef->getCommentBuilder()->appendComment("@doconly");
- }
-
- appendJavaDocAnnotations(mOptions.javadocAnnotations, classDef->getCommentBuilder());
-
- rClass.addMember(std::move(classDef));
- }
- }
-
- appendJavaDocAnnotations(mOptions.javadocAnnotations, rClass.getCommentBuilder());
-
- if (!ClassDefinition::writeJavaFile(&rClass, outPackageName, mOptions.useFinal, out)) {
+ bool result = AddMembersToTypeClass(
+ package_name_to_generate, package.get(), type.get(), class_def.get());
+ if (!result) {
return false;
- }
+ }
- out->flush();
- return true;
+ if (type->type == ResourceType::kAttr) {
+ // Also include private attributes in this same class.
+ ResourceTableType* priv_type =
+ package->FindType(ResourceType::kAttrPrivate);
+ if (priv_type) {
+ result =
+ AddMembersToTypeClass(package_name_to_generate, package.get(),
+ priv_type, class_def.get());
+ if (!result) {
+ return false;
+ }
+ }
+ }
+
+ if (type->type == ResourceType::kStyleable &&
+ options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
+ // When generating a public R class, we don't want Styleable to be part
+ // of the API.
+ // It is only emitted for documentation purposes.
+ class_def->GetCommentBuilder()->AppendComment("@doconly");
+ }
+
+ AppendJavaDocAnnotations(options_.javadoc_annotations,
+ class_def->GetCommentBuilder());
+
+ r_class.AddMember(std::move(class_def));
+ }
+ }
+
+ AppendJavaDocAnnotations(options_.javadoc_annotations,
+ r_class.GetCommentBuilder());
+
+ if (!ClassDefinition::WriteJavaFile(&r_class, out_package_name,
+ options_.use_final, out)) {
+ return false;
+ }
+
+ out->flush();
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 901a86e..190e73b 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -17,86 +17,88 @@
#ifndef AAPT_JAVA_CLASS_GENERATOR_H
#define AAPT_JAVA_CLASS_GENERATOR_H
+#include <ostream>
+#include <string>
+
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "process/IResourceTableConsumer.h"
#include "util/StringPiece.h"
-#include <ostream>
-#include <string>
-
namespace aapt {
class AnnotationProcessor;
class ClassDefinition;
struct JavaClassGeneratorOptions {
- /*
- * Specifies whether to use the 'final' modifier
- * on resource entries. Default is true.
- */
- bool useFinal = true;
+ /*
+ * Specifies whether to use the 'final' modifier
+ * on resource entries. Default is true.
+ */
+ bool use_final = true;
- enum class SymbolTypes {
- kAll,
- kPublicPrivate,
- kPublic,
- };
+ enum class SymbolTypes {
+ kAll,
+ kPublicPrivate,
+ kPublic,
+ };
- SymbolTypes types = SymbolTypes::kAll;
+ SymbolTypes types = SymbolTypes::kAll;
- /**
- * A list of JavaDoc annotations to add to the comments of all generated classes.
- */
- std::vector<std::string> javadocAnnotations;
+ /**
+ * A list of JavaDoc annotations to add to the comments of all generated
+ * classes.
+ */
+ std::vector<std::string> javadoc_annotations;
};
/*
* Generates the R.java file for a resource table.
*/
class JavaClassGenerator {
-public:
- JavaClassGenerator(IAaptContext* context, ResourceTable* table,
- const JavaClassGeneratorOptions& options);
+ public:
+ JavaClassGenerator(IAaptContext* context, ResourceTable* table,
+ const JavaClassGeneratorOptions& options);
- /*
- * Writes the R.java file to `out`. Only symbols belonging to `package` are written.
- * All symbols technically belong to a single package, but linked libraries will
- * have their names mangled, denoting that they came from a different package.
- * We need to generate these symbols in a separate file.
- * Returns true on success.
- */
- bool generate(const StringPiece& packageNameToGenerate, std::ostream* out);
+ /*
+ * Writes the R.java file to `out`. Only symbols belonging to `package` are
+ * written.
+ * All symbols technically belong to a single package, but linked libraries
+ * will
+ * have their names mangled, denoting that they came from a different package.
+ * We need to generate these symbols in a separate file.
+ * Returns true on success.
+ */
+ bool Generate(const StringPiece& packageNameToGenerate, std::ostream* out);
- bool generate(const StringPiece& packageNameToGenerate,
- const StringPiece& outputPackageName,
- std::ostream* out);
+ bool Generate(const StringPiece& packageNameToGenerate,
+ const StringPiece& outputPackageName, std::ostream* out);
- const std::string& getError() const;
+ const std::string& getError() const;
-private:
- bool addMembersToTypeClass(const StringPiece& packageNameToGenerate,
- const ResourceTablePackage* package,
- const ResourceTableType* type,
- ClassDefinition* outTypeClassDef);
+ private:
+ bool AddMembersToTypeClass(const StringPiece& packageNameToGenerate,
+ const ResourceTablePackage* package,
+ const ResourceTableType* type,
+ ClassDefinition* outTypeClassDef);
- void addMembersToStyleableClass(const StringPiece& packageNameToGenerate,
- const std::string& entryName,
- const Styleable* styleable,
- ClassDefinition* outStyleableClassDef);
+ void AddMembersToStyleableClass(const StringPiece& packageNameToGenerate,
+ const std::string& entryName,
+ const Styleable* styleable,
+ ClassDefinition* outStyleableClassDef);
- bool skipSymbol(SymbolState state);
+ bool SkipSymbol(SymbolState state);
- IAaptContext* mContext;
- ResourceTable* mTable;
- JavaClassGeneratorOptions mOptions;
- std::string mError;
+ IAaptContext* context_;
+ ResourceTable* table_;
+ JavaClassGeneratorOptions options_;
+ std::string error_;
};
inline const std::string& JavaClassGenerator::getError() const {
- return mError;
+ return error_;
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_JAVA_CLASS_GENERATOR_H
+#endif // AAPT_JAVA_CLASS_GENERATOR_H
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index ed7c6bd..3d3d24e 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -15,154 +15,181 @@
*/
#include "java/JavaClassGenerator.h"
-#include "test/Test.h"
-#include "util/Util.h"
#include <sstream>
#include <string>
+#include "test/Test.h"
+#include "util/Util.h"
+
namespace aapt {
TEST(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("android", 0x01)
- .addSimple("android:id/class", ResourceId(0x01020000))
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddSimple("android:id/class", ResourceId(0x01020000))
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .setNameManglerPolicy(NameManglerPolicy{ "android" })
- .build();
- JavaClassGenerator generator(context.get(), table.get(), {});
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
- EXPECT_FALSE(generator.generate("android", &out));
+ std::stringstream out;
+ EXPECT_FALSE(generator.Generate("android", &out));
}
TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("android", 0x01)
- .addSimple("android:id/hey-man", ResourceId(0x01020000))
- .addValue("android:attr/cool.attr", ResourceId(0x01010000),
- test::AttributeBuilder(false).build())
- .addValue("android:styleable/hey.dude", ResourceId(0x01030000),
- test::StyleableBuilder()
- .addItem("android:attr/cool.attr", ResourceId(0x01010000))
- .build())
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddSimple("android:id/hey-man", ResourceId(0x01020000))
+ .AddValue("android:attr/cool.attr", ResourceId(0x01010000),
+ test::AttributeBuilder(false).Build())
+ .AddValue(
+ "android:styleable/hey.dude", ResourceId(0x01030000),
+ test::StyleableBuilder()
+ .AddItem("android:attr/cool.attr", ResourceId(0x01010000))
+ .Build())
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .setNameManglerPolicy(NameManglerPolicy{ "android" })
- .build();
- JavaClassGenerator generator(context.get(), table.get(), {});
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
- EXPECT_TRUE(generator.generate("android", &out));
+ std::stringstream out;
+ EXPECT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ std::string output = out.str();
- EXPECT_NE(std::string::npos,
- output.find("public static final int hey_man=0x01020000;"));
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int hey_man=0x01020000;"));
- EXPECT_NE(std::string::npos,
- output.find("public static final int[] hey_dude={"));
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int[] hey_dude={"));
- EXPECT_NE(std::string::npos,
- output.find("public static final int hey_dude_cool_attr=0;"));
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int hey_dude_cool_attr=0;"));
}
TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("android", 0x01)
- .addSimple("android:id/one", ResourceId(0x01020000))
- .addSimple("android:id/com.foo$two", ResourceId(0x01020001))
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddSimple("android:id/one", ResourceId(0x01020000))
+ .AddSimple("android:id/com.foo$two", ResourceId(0x01020001))
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .setNameManglerPolicy(NameManglerPolicy{ "android" })
- .build();
- JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
- ASSERT_TRUE(generator.generate("android", "com.android.internal", &out));
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
+ std::stringstream out;
+ ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
- std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("package com.android.internal;"));
- EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
- EXPECT_EQ(std::string::npos, output.find("two"));
- EXPECT_EQ(std::string::npos, output.find("com_foo$two"));
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos, output.find("package com.android.internal;"));
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int one=0x01020000;"));
+ EXPECT_EQ(std::string::npos, output.find("two"));
+ EXPECT_EQ(std::string::npos, output.find("com_foo$two"));
}
TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("android", 0x01)
- .addSimple("android:attr/two", ResourceId(0x01010001))
- .addSimple("android:^attr-private/one", ResourceId(0x01010000))
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddSimple("android:attr/two", ResourceId(0x01010001))
+ .AddSimple("android:^attr-private/one", ResourceId(0x01010000))
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .setNameManglerPolicy(NameManglerPolicy{ "android" })
- .build();
- JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
- ASSERT_TRUE(generator.generate("android", &out));
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
+ std::stringstream out;
+ ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("public static final class attr"));
- EXPECT_EQ(std::string::npos, output.find("public static final class ^attr-private"));
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos, output.find("public static final class attr"));
+ EXPECT_EQ(std::string::npos,
+ output.find("public static final class ^attr-private"));
}
TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
- StdErrDiagnostics diag;
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("android", 0x01)
- .addSimple("android:id/one", ResourceId(0x01020000))
- .addSimple("android:id/two", ResourceId(0x01020001))
- .addSimple("android:id/three", ResourceId(0x01020002))
- .setSymbolState("android:id/one", ResourceId(0x01020000), SymbolState::kPublic)
- .setSymbolState("android:id/two", ResourceId(0x01020001), SymbolState::kPrivate)
- .build();
+ StdErrDiagnostics diag;
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddSimple("android:id/one", ResourceId(0x01020000))
+ .AddSimple("android:id/two", ResourceId(0x01020001))
+ .AddSimple("android:id/three", ResourceId(0x01020002))
+ .SetSymbolState("android:id/one", ResourceId(0x01020000),
+ SymbolState::kPublic)
+ .SetSymbolState("android:id/two", ResourceId(0x01020001),
+ SymbolState::kPrivate)
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .setNameManglerPolicy(NameManglerPolicy{ "android" })
- .build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
- JavaClassGeneratorOptions options;
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
- {
- JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
- ASSERT_TRUE(generator.generate("android", &out));
- std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
- EXPECT_EQ(std::string::npos, output.find("two"));
- EXPECT_EQ(std::string::npos, output.find("three"));
- }
+ JavaClassGeneratorOptions options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
+ {
+ JavaClassGenerator generator(context.get(), table.get(), options);
+ std::stringstream out;
+ ASSERT_TRUE(generator.Generate("android", &out));
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int one=0x01020000;"));
+ EXPECT_EQ(std::string::npos, output.find("two"));
+ EXPECT_EQ(std::string::npos, output.find("three"));
+ }
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
- {
- JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
- ASSERT_TRUE(generator.generate("android", &out));
- std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
- EXPECT_NE(std::string::npos, output.find("public static final int two=0x01020001;"));
- EXPECT_EQ(std::string::npos, output.find("three"));
- }
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
+ {
+ JavaClassGenerator generator(context.get(), table.get(), options);
+ std::stringstream out;
+ ASSERT_TRUE(generator.Generate("android", &out));
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int one=0x01020000;"));
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int two=0x01020001;"));
+ EXPECT_EQ(std::string::npos, output.find("three"));
+ }
- options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
- {
- JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
- ASSERT_TRUE(generator.generate("android", &out));
- std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("public static final int one=0x01020000;"));
- EXPECT_NE(std::string::npos, output.find("public static final int two=0x01020001;"));
- EXPECT_NE(std::string::npos, output.find("public static final int three=0x01020002;"));
- }
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ {
+ JavaClassGenerator generator(context.get(), table.get(), options);
+ std::stringstream out;
+ ASSERT_TRUE(generator.Generate("android", &out));
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int one=0x01020000;"));
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int two=0x01020001;"));
+ EXPECT_NE(std::string::npos,
+ output.find("public static final int three=0x01020002;"));
+ }
}
/*
@@ -172,12 +199,15 @@
ResourceId{ 0x01, 0x02, 0x0000 }));
ResourceTable table;
table.setPackage(u"com.lib");
- ASSERT_TRUE(table.addResource(ResourceName{ {}, ResourceType::kId, u"test" }, {},
- Source{ "lib.xml", 33 }, util::make_unique<Id>()));
+ ASSERT_TRUE(table.addResource(ResourceName{ {}, ResourceType::kId, u"test"
+}, {},
+ Source{ "lib.xml", 33 },
+util::make_unique<Id>()));
ASSERT_TRUE(mTable->merge(std::move(table)));
Linker linker(mTable,
- std::make_shared<MockResolver>(mTable, std::map<ResourceName, ResourceId>()),
+ std::make_shared<MockResolver>(mTable, std::map<ResourceName,
+ResourceId>()),
{});
ASSERT_TRUE(linker.linkAndValidate());
@@ -197,126 +227,139 @@
}*/
TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("android", 0x01)
- .setPackageId("com.lib", 0x02)
- .addValue("android:attr/bar", ResourceId(0x01010000),
- test::AttributeBuilder(false).build())
- .addValue("com.lib:attr/bar", ResourceId(0x02010000),
- test::AttributeBuilder(false).build())
- .addValue("android:styleable/foo", ResourceId(0x01030000),
- test::StyleableBuilder()
- .addItem("android:attr/bar", ResourceId(0x01010000))
- .addItem("com.lib:attr/bar", ResourceId(0x02010000))
- .build())
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .SetPackageId("com.lib", 0x02)
+ .AddValue("android:attr/bar", ResourceId(0x01010000),
+ test::AttributeBuilder(false).Build())
+ .AddValue("com.lib:attr/bar", ResourceId(0x02010000),
+ test::AttributeBuilder(false).Build())
+ .AddValue("android:styleable/foo", ResourceId(0x01030000),
+ test::StyleableBuilder()
+ .AddItem("android:attr/bar", ResourceId(0x01010000))
+ .AddItem("com.lib:attr/bar", ResourceId(0x02010000))
+ .Build())
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .setNameManglerPolicy(NameManglerPolicy{ "android" })
- .build();
- JavaClassGenerator generator(context.get(), table.get(), {});
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
- EXPECT_TRUE(generator.generate("android", &out));
+ std::stringstream out;
+ EXPECT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
- EXPECT_NE(std::string::npos, output.find("int foo_bar="));
- EXPECT_NE(std::string::npos, output.find("int foo_com_lib_bar="));
+ std::string output = out.str();
+ EXPECT_NE(std::string::npos, output.find("int foo_bar="));
+ EXPECT_NE(std::string::npos, output.find("int foo_com_lib_bar="));
}
TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("android", 0x01)
- .addSimple("android:id/foo", ResourceId(0x01010000))
- .build();
- test::getValue<Id>(table.get(), "android:id/foo")
- ->setComment(std::string("This is a comment\n@deprecated"));
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddSimple("android:id/foo", ResourceId(0x01010000))
+ .Build();
+ test::GetValue<Id>(table.get(), "android:id/foo")
+ ->SetComment(std::string("This is a comment\n@deprecated"));
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .setNameManglerPolicy(NameManglerPolicy{ "android" })
- .build();
- JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
- ASSERT_TRUE(generator.generate("android", &out));
- std::string actual = out.str();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
+ std::stringstream out;
+ ASSERT_TRUE(generator.Generate("android", &out));
+ std::string actual = out.str();
- const char* expectedText =
-R"EOF(/**
+ const char* expectedText =
+ R"EOF(/**
* This is a comment
* @deprecated
*/
@Deprecated
public static final int foo=0x01010000;)EOF";
- EXPECT_NE(std::string::npos, actual.find(expectedText));
+ EXPECT_NE(std::string::npos, actual.find(expectedText));
}
-TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {
+TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {}
-}
+TEST(JavaClassGeneratorTest,
+ CommentsForStyleablesAndNestedAttributesArePresent) {
+ Attribute attr(false);
+ attr.SetComment(StringPiece("This is an attribute"));
-TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
- Attribute attr(false);
- attr.setComment(StringPiece("This is an attribute"));
+ Styleable styleable;
+ styleable.entries.push_back(
+ Reference(test::ParseNameOrDie("android:attr/one")));
+ styleable.SetComment(StringPiece("This is a styleable"));
- Styleable styleable;
- styleable.entries.push_back(Reference(test::parseNameOrDie("android:attr/one")));
- styleable.setComment(StringPiece("This is a styleable"));
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
+ .AddValue("android:styleable/Container",
+ std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
+ .Build();
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("android", 0x01)
- .addValue("android:attr/one", util::make_unique<Attribute>(attr))
- .addValue("android:styleable/Container",
- std::unique_ptr<Styleable>(styleable.clone(nullptr)))
- .build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGeneratorOptions options;
+ options.use_final = false;
+ JavaClassGenerator generator(context.get(), table.get(), options);
+ std::stringstream out;
+ ASSERT_TRUE(generator.Generate("android", &out));
+ std::string actual = out.str();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .setNameManglerPolicy(NameManglerPolicy{ "android" })
- .build();
- JavaClassGeneratorOptions options;
- options.useFinal = false;
- JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
- ASSERT_TRUE(generator.generate("android", &out));
- std::string actual = out.str();
-
- EXPECT_NE(std::string::npos, actual.find("attr name android:one"));
- EXPECT_NE(std::string::npos, actual.find("attr description"));
- EXPECT_NE(std::string::npos, actual.find(attr.getComment().data()));
- EXPECT_NE(std::string::npos, actual.find(styleable.getComment().data()));
+ EXPECT_NE(std::string::npos, actual.find("attr name android:one"));
+ EXPECT_NE(std::string::npos, actual.find("attr description"));
+ EXPECT_NE(std::string::npos, actual.find(attr.GetComment().data()));
+ EXPECT_NE(std::string::npos, actual.find(styleable.GetComment().data()));
}
TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
- Attribute attr(false);
- attr.setComment(StringPiece("removed"));
+ Attribute attr(false);
+ attr.SetComment(StringPiece("removed"));
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("android", 0x01)
- .addValue("android:attr/one", util::make_unique<Attribute>(attr))
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .setNameManglerPolicy(NameManglerPolicy{ "android" })
- .build();
- JavaClassGeneratorOptions options;
- options.useFinal = false;
- JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
- ASSERT_TRUE(generator.generate("android", &out));
- std::string actual = out.str();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGeneratorOptions options;
+ options.use_final = false;
+ JavaClassGenerator generator(context.get(), table.get(), options);
+ std::stringstream out;
+ ASSERT_TRUE(generator.Generate("android", &out));
+ std::string actual = out.str();
- EXPECT_EQ(std::string::npos, actual.find("@attr name android:one"));
- EXPECT_EQ(std::string::npos, actual.find("@attr description"));
+ EXPECT_EQ(std::string::npos, actual.find("@attr name android:one"));
+ EXPECT_EQ(std::string::npos, actual.find("@attr description"));
- // We should find @removed only in the attribute javadoc and not anywhere else (i.e. the class
- // javadoc).
- const size_t pos = actual.find("removed");
- EXPECT_NE(std::string::npos, pos);
- EXPECT_EQ(std::string::npos, actual.find("removed", pos + 1));
+ // We should find @removed only in the attribute javadoc and not anywhere else
+ // (i.e. the class
+ // javadoc).
+ const size_t pos = actual.find("removed");
+ EXPECT_NE(std::string::npos, pos);
+ EXPECT_EQ(std::string::npos, actual.find("removed", pos + 1));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index 5ff11b1..db84f29 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -14,111 +14,120 @@
* limitations under the License.
*/
-#include "Source.h"
-#include "java/AnnotationProcessor.h"
-#include "java/ClassDefinition.h"
#include "java/ManifestClassGenerator.h"
-#include "util/Maybe.h"
-#include "xml/XmlDom.h"
#include <algorithm>
+#include "Source.h"
+#include "java/AnnotationProcessor.h"
+#include "java/ClassDefinition.h"
+#include "util/Maybe.h"
+#include "xml/XmlDom.h"
+
namespace aapt {
-static Maybe<StringPiece> extractJavaIdentifier(IDiagnostics* diag, const Source& source,
+static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag,
+ const Source& source,
const StringPiece& value) {
- const StringPiece sep = ".";
- auto iter = std::find_end(value.begin(), value.end(), sep.begin(), sep.end());
+ const StringPiece sep = ".";
+ auto iter = std::find_end(value.begin(), value.end(), sep.begin(), sep.end());
- StringPiece result;
- if (iter != value.end()) {
- result.assign(iter + sep.size(), value.end() - (iter + sep.size()));
- } else {
- result = value;
- }
+ StringPiece result;
+ if (iter != value.end()) {
+ result.assign(iter + sep.size(), value.end() - (iter + sep.size()));
+ } else {
+ result = value;
+ }
- if (result.empty()) {
- diag->error(DiagMessage(source) << "empty symbol");
- return {};
- }
+ if (result.empty()) {
+ diag->Error(DiagMessage(source) << "empty symbol");
+ return {};
+ }
- iter = util::findNonAlphaNumericAndNotInSet(result, "_");
- if (iter != result.end()) {
- diag->error(DiagMessage(source)
- << "invalid character '" << StringPiece(iter, 1)
- << "' in '" << result << "'");
- return {};
- }
+ iter = util::FindNonAlphaNumericAndNotInSet(result, "_");
+ if (iter != result.end()) {
+ diag->Error(DiagMessage(source) << "invalid character '"
+ << StringPiece(iter, 1) << "' in '"
+ << result << "'");
+ return {};
+ }
- if (*result.begin() >= '0' && *result.begin() <= '9') {
- diag->error(DiagMessage(source) << "symbol can not start with a digit");
- return {};
- }
+ if (*result.begin() >= '0' && *result.begin() <= '9') {
+ diag->Error(DiagMessage(source) << "symbol can not start with a digit");
+ return {};
+ }
- return result;
+ return result;
}
-static bool writeSymbol(const Source& source, IDiagnostics* diag, xml::Element* el,
- ClassDefinition* classDef) {
- xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "name");
- if (!attr) {
- diag->error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
- return false;
- }
+static bool WriteSymbol(const Source& source, IDiagnostics* diag,
+ xml::Element* el, ClassDefinition* class_def) {
+ xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name");
+ if (!attr) {
+ diag->Error(DiagMessage(source) << "<" << el->name
+ << "> must define 'android:name'");
+ return false;
+ }
- Maybe<StringPiece> result = extractJavaIdentifier(diag, source.withLine(el->lineNumber),
- attr->value);
- if (!result) {
- return false;
- }
+ Maybe<StringPiece> result = ExtractJavaIdentifier(
+ diag, source.WithLine(el->line_number), attr->value);
+ if (!result) {
+ return false;
+ }
- std::unique_ptr<StringMember> stringMember = util::make_unique<StringMember>(
- result.value(), attr->value);
- stringMember->getCommentBuilder()->appendComment(el->comment);
+ std::unique_ptr<StringMember> string_member =
+ util::make_unique<StringMember>(result.value(), attr->value);
+ string_member->GetCommentBuilder()->AppendComment(el->comment);
- classDef->addMember(std::move(stringMember));
- return true;
+ class_def->AddMember(std::move(string_member));
+ return true;
}
-std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml::XmlResource* res) {
- xml::Element* el = xml::findRootElement(res->root.get());
- if (!el) {
- diag->error(DiagMessage(res->file.source) << "no root tag defined");
- return {};
+std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
+ xml::XmlResource* res) {
+ xml::Element* el = xml::FindRootElement(res->root.get());
+ if (!el) {
+ diag->Error(DiagMessage(res->file.source) << "no root tag defined");
+ return {};
+ }
+
+ if (el->name != "manifest" && !el->namespace_uri.empty()) {
+ diag->Error(DiagMessage(res->file.source)
+ << "no <manifest> root tag defined");
+ return {};
+ }
+
+ std::unique_ptr<ClassDefinition> permission_class =
+ util::make_unique<ClassDefinition>("permission", ClassQualifier::Static,
+ false);
+ std::unique_ptr<ClassDefinition> permission_group_class =
+ util::make_unique<ClassDefinition>("permission_group",
+ ClassQualifier::Static, false);
+
+ bool error = false;
+ std::vector<xml::Element*> children = el->GetChildElements();
+ for (xml::Element* child_el : children) {
+ if (child_el->namespace_uri.empty()) {
+ if (child_el->name == "permission") {
+ error |= !WriteSymbol(res->file.source, diag, child_el,
+ permission_class.get());
+ } else if (child_el->name == "permission-group") {
+ error |= !WriteSymbol(res->file.source, diag, child_el,
+ permission_group_class.get());
+ }
}
+ }
- if (el->name != "manifest" && !el->namespaceUri.empty()) {
- diag->error(DiagMessage(res->file.source) << "no <manifest> root tag defined");
- return {};
- }
+ if (error) {
+ return {};
+ }
- std::unique_ptr<ClassDefinition> permissionClass =
- util::make_unique<ClassDefinition>("permission", ClassQualifier::Static, false);
- std::unique_ptr<ClassDefinition> permissionGroupClass =
- util::make_unique<ClassDefinition>("permission_group", ClassQualifier::Static, false);
-
- bool error = false;
-
- std::vector<xml::Element*> children = el->getChildElements();
- for (xml::Element* childEl : children) {
- if (childEl->namespaceUri.empty()) {
- if (childEl->name == "permission") {
- error |= !writeSymbol(res->file.source, diag, childEl, permissionClass.get());
- } else if (childEl->name == "permission-group") {
- error |= !writeSymbol(res->file.source, diag, childEl, permissionGroupClass.get());
- }
- }
- }
-
- if (error) {
- return {};
- }
-
- std::unique_ptr<ClassDefinition> manifestClass =
- util::make_unique<ClassDefinition>("Manifest", ClassQualifier::None, false);
- manifestClass->addMember(std::move(permissionClass));
- manifestClass->addMember(std::move(permissionGroupClass));
- return manifestClass;
+ std::unique_ptr<ClassDefinition> manifest_class =
+ util::make_unique<ClassDefinition>("Manifest", ClassQualifier::None,
+ false);
+ manifest_class->AddMember(std::move(permission_class));
+ manifest_class->AddMember(std::move(permission_group_class));
+ return manifest_class;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
index f565289..b12202a 100644
--- a/tools/aapt2/java/ManifestClassGenerator.h
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -19,15 +19,13 @@
#include "Diagnostics.h"
#include "java/ClassDefinition.h"
-#include "util/StringPiece.h"
#include "xml/XmlDom.h"
-#include <iostream>
-
namespace aapt {
-std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml::XmlResource* res);
+std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
+ xml::XmlResource* res);
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_JAVA_MANIFESTCLASSGENERATOR_H */
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index eecb544..5ebf508 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -15,30 +15,33 @@
*/
#include "java/ManifestClassGenerator.h"
+
#include "test/Test.h"
namespace aapt {
-static ::testing::AssertionResult getManifestClassText(IAaptContext* context, xml::XmlResource* res,
- std::string* outStr) {
- std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
- context->getDiagnostics(), res);
- if (!manifestClass) {
- return ::testing::AssertionFailure() << "manifestClass == nullptr";
- }
+static ::testing::AssertionResult GetManifestClassText(IAaptContext* context,
+ xml::XmlResource* res,
+ std::string* out_str) {
+ std::unique_ptr<ClassDefinition> manifest_class =
+ GenerateManifestClass(context->GetDiagnostics(), res);
+ if (!manifest_class) {
+ return ::testing::AssertionFailure() << "manifest_class == nullptr";
+ }
- std::stringstream out;
- if (!manifestClass->writeJavaFile(manifestClass.get(), "android", true, &out)) {
- return ::testing::AssertionFailure() << "failed to write java file";
- }
+ std::stringstream out;
+ if (!manifest_class->WriteJavaFile(manifest_class.get(), "android", true,
+ &out)) {
+ return ::testing::AssertionFailure() << "failed to write java file";
+ }
- *outStr = out.str();
- return ::testing::AssertionSuccess();
+ *out_str = out.str();
+ return ::testing::AssertionSuccess();
}
TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF(
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<permission android:name="android.permission.ACCESS_INTERNET" />
<permission android:name="android.DO_DANGEROUS_THINGS" />
@@ -46,46 +49,51 @@
<permission-group android:name="foo.bar.PERMISSION" />
</manifest>)EOF");
- std::string actual;
- ASSERT_TRUE(getManifestClassText(context.get(), manifest.get(), &actual));
+ std::string actual;
+ ASSERT_TRUE(GetManifestClassText(context.get(), manifest.get(), &actual));
- const size_t permissionClassPos = actual.find("public static final class permission {");
- const size_t permissionGroupClassPos =
- actual.find("public static final class permission_group {");
- ASSERT_NE(std::string::npos, permissionClassPos);
- ASSERT_NE(std::string::npos, permissionGroupClassPos);
+ const size_t permission_class_pos =
+ actual.find("public static final class permission {");
+ const size_t permission_croup_class_pos =
+ actual.find("public static final class permission_group {");
+ ASSERT_NE(std::string::npos, permission_class_pos);
+ ASSERT_NE(std::string::npos, permission_croup_class_pos);
- //
- // Make sure these permissions are in the permission class.
- //
+ //
+ // Make sure these permissions are in the permission class.
+ //
- size_t pos = actual.find("public static final String ACCESS_INTERNET="
- "\"android.permission.ACCESS_INTERNET\";");
- EXPECT_GT(pos, permissionClassPos);
- EXPECT_LT(pos, permissionGroupClassPos);
+ size_t pos = actual.find(
+ "public static final String ACCESS_INTERNET="
+ "\"android.permission.ACCESS_INTERNET\";");
+ EXPECT_GT(pos, permission_class_pos);
+ EXPECT_LT(pos, permission_croup_class_pos);
- pos = actual.find("public static final String DO_DANGEROUS_THINGS="
- "\"android.DO_DANGEROUS_THINGS\";");
- EXPECT_GT(pos, permissionClassPos);
- EXPECT_LT(pos, permissionGroupClassPos);
+ pos = actual.find(
+ "public static final String DO_DANGEROUS_THINGS="
+ "\"android.DO_DANGEROUS_THINGS\";");
+ EXPECT_GT(pos, permission_class_pos);
+ EXPECT_LT(pos, permission_croup_class_pos);
- pos = actual.find("public static final String HUH=\"com.test.sample.permission.HUH\";");
- EXPECT_GT(pos, permissionClassPos);
- EXPECT_LT(pos, permissionGroupClassPos);
+ pos = actual.find(
+ "public static final String HUH=\"com.test.sample.permission.HUH\";");
+ EXPECT_GT(pos, permission_class_pos);
+ EXPECT_LT(pos, permission_croup_class_pos);
- //
- // Make sure these permissions are in the permission_group class
- //
+ //
+ // Make sure these permissions are in the permission_group class
+ //
- pos = actual.find("public static final String PERMISSION="
- "\"foo.bar.PERMISSION\";");
- EXPECT_GT(pos, permissionGroupClassPos);
- EXPECT_LT(pos, std::string::npos);
+ pos = actual.find(
+ "public static final String PERMISSION="
+ "\"foo.bar.PERMISSION\";");
+ EXPECT_GT(pos, permission_croup_class_pos);
+ EXPECT_LT(pos, std::string::npos);
}
TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF(
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required to access the internet.
Added in API 1. -->
@@ -98,36 +106,36 @@
<permission android:name="android.permission.SECRET" />
</manifest>)EOF");
- std::string actual;
- ASSERT_TRUE(getManifestClassText(context.get(), manifest.get(), &actual));
+ std::string actual;
+ ASSERT_TRUE(GetManifestClassText(context.get(), manifest.get(), &actual));
- const char* expectedAccessInternet =
-R"EOF( /**
+ const char* expected_access_internet =
+ R"EOF( /**
* Required to access the internet.
* Added in API 1.
*/
public static final String ACCESS_INTERNET="android.permission.ACCESS_INTERNET";)EOF";
- EXPECT_NE(std::string::npos, actual.find(expectedAccessInternet));
+ EXPECT_NE(std::string::npos, actual.find(expected_access_internet));
- const char* expectedPlayOutside =
-R"EOF( /**
+ const char* expected_play_outside =
+ R"EOF( /**
* @deprecated This permission is for playing outside.
*/
@Deprecated
public static final String PLAY_OUTSIDE="android.permission.PLAY_OUTSIDE";)EOF";
- EXPECT_NE(std::string::npos, actual.find(expectedPlayOutside));
+ EXPECT_NE(std::string::npos, actual.find(expected_play_outside));
- const char* expectedSecret =
-R"EOF( /**
+ const char* expected_secret =
+ R"EOF( /**
* This is a private permission for system only!
* @hide
*/
@android.annotation.SystemApi
public static final String SECRET="android.permission.SECRET";)EOF";
- EXPECT_NE(std::string::npos, actual.find(expectedSecret));
+ EXPECT_NE(std::string::npos, actual.find(expected_secret));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 902ec4c..624a559 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -15,254 +15,281 @@
*/
#include "java/ProguardRules.h"
-#include "util/Util.h"
-#include "xml/XmlDom.h"
#include <memory>
#include <string>
+#include "android-base/macros.h"
+
+#include "util/Util.h"
+#include "xml/XmlDom.h"
+
namespace aapt {
namespace proguard {
class BaseVisitor : public xml::Visitor {
-public:
- BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
+ public:
+ BaseVisitor(const Source& source, KeepSet* keep_set)
+ : source_(source), keep_set_(keep_set) {}
+
+ virtual void Visit(xml::Text*) override{};
+
+ virtual void Visit(xml::Namespace* node) override {
+ for (const auto& child : node->children) {
+ child->Accept(this);
}
+ }
- virtual void visit(xml::Text*) override {};
-
- virtual void visit(xml::Namespace* node) override {
- for (const auto& child : node->children) {
- child->accept(this);
+ virtual void Visit(xml::Element* node) override {
+ if (!node->namespace_uri.empty()) {
+ Maybe<xml::ExtractedPackage> maybe_package =
+ xml::ExtractPackageFromNamespace(node->namespace_uri);
+ if (maybe_package) {
+ // This is a custom view, let's figure out the class name from this.
+ std::string package = maybe_package.value().package + "." + node->name;
+ if (util::IsJavaClassName(package)) {
+ AddClass(node->line_number, package);
}
+ }
+ } else if (util::IsJavaClassName(node->name)) {
+ AddClass(node->line_number, node->name);
}
- virtual void visit(xml::Element* node) override {
- if (!node->namespaceUri.empty()) {
- Maybe<xml::ExtractedPackage> maybePackage = xml::extractPackageFromNamespace(
- node->namespaceUri);
- if (maybePackage) {
- // This is a custom view, let's figure out the class name from this.
- std::string package = maybePackage.value().package + "." + node->name;
- if (util::isJavaClassName(package)) {
- addClass(node->lineNumber, package);
- }
- }
- } else if (util::isJavaClassName(node->name)) {
- addClass(node->lineNumber, node->name);
- }
-
- for (const auto& child: node->children) {
- child->accept(this);
- }
+ for (const auto& child : node->children) {
+ child->Accept(this);
}
+ }
-protected:
- void addClass(size_t lineNumber, const std::string& className) {
- mKeepSet->addClass(Source(mSource.path, lineNumber), className);
- }
+ protected:
+ void AddClass(size_t line_number, const std::string& class_name) {
+ keep_set_->AddClass(Source(source_.path, line_number), class_name);
+ }
- void addMethod(size_t lineNumber, const std::string& methodName) {
- mKeepSet->addMethod(Source(mSource.path, lineNumber), methodName);
- }
+ void AddMethod(size_t line_number, const std::string& method_name) {
+ keep_set_->AddMethod(Source(source_.path, line_number), method_name);
+ }
-private:
- Source mSource;
- KeepSet* mKeepSet;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BaseVisitor);
+
+ Source source_;
+ KeepSet* keep_set_;
};
-struct LayoutVisitor : public BaseVisitor {
- LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+class LayoutVisitor : public BaseVisitor {
+ public:
+ LayoutVisitor(const Source& source, KeepSet* keep_set)
+ : BaseVisitor(source, keep_set) {}
+
+ virtual void Visit(xml::Element* node) override {
+ bool check_class = false;
+ bool check_name = false;
+ if (node->namespace_uri.empty()) {
+ check_class = node->name == "view" || node->name == "fragment";
+ } else if (node->namespace_uri == xml::kSchemaAndroid) {
+ check_name = node->name == "fragment";
}
- virtual void visit(xml::Element* node) override {
- bool checkClass = false;
- bool checkName = false;
- if (node->namespaceUri.empty()) {
- checkClass = node->name == "view" || node->name == "fragment";
- } else if (node->namespaceUri == xml::kSchemaAndroid) {
- checkName = node->name == "fragment";
- }
-
- for (const auto& attr : node->attributes) {
- if (checkClass && attr.namespaceUri.empty() && attr.name == "class" &&
- util::isJavaClassName(attr.value)) {
- addClass(node->lineNumber, attr.value);
- } else if (checkName && attr.namespaceUri == xml::kSchemaAndroid &&
- attr.name == "name" && util::isJavaClassName(attr.value)) {
- addClass(node->lineNumber, attr.value);
- } else if (attr.namespaceUri == xml::kSchemaAndroid && attr.name == "onClick") {
- addMethod(node->lineNumber, attr.value);
- }
- }
-
- BaseVisitor::visit(node);
+ for (const auto& attr : node->attributes) {
+ if (check_class && attr.namespace_uri.empty() && attr.name == "class" &&
+ util::IsJavaClassName(attr.value)) {
+ AddClass(node->line_number, attr.value);
+ } else if (check_name && attr.namespace_uri == xml::kSchemaAndroid &&
+ attr.name == "name" && util::IsJavaClassName(attr.value)) {
+ AddClass(node->line_number, attr.value);
+ } else if (attr.namespace_uri == xml::kSchemaAndroid &&
+ attr.name == "onClick") {
+ AddMethod(node->line_number, attr.value);
+ }
}
+
+ BaseVisitor::Visit(node);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LayoutVisitor);
};
-struct XmlResourceVisitor : public BaseVisitor {
- XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+class XmlResourceVisitor : public BaseVisitor {
+ public:
+ XmlResourceVisitor(const Source& source, KeepSet* keep_set)
+ : BaseVisitor(source, keep_set) {}
+
+ virtual void Visit(xml::Element* node) override {
+ bool check_fragment = false;
+ if (node->namespace_uri.empty()) {
+ check_fragment =
+ node->name == "PreferenceScreen" || node->name == "header";
}
- virtual void visit(xml::Element* node) override {
- bool checkFragment = false;
- if (node->namespaceUri.empty()) {
- checkFragment = node->name == "PreferenceScreen" || node->name == "header";
- }
-
- if (checkFragment) {
- xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "fragment");
- if (attr && util::isJavaClassName(attr->value)) {
- addClass(node->lineNumber, attr->value);
- }
- }
-
- BaseVisitor::visit(node);
+ if (check_fragment) {
+ xml::Attribute* attr =
+ node->FindAttribute(xml::kSchemaAndroid, "fragment");
+ if (attr && util::IsJavaClassName(attr->value)) {
+ AddClass(node->line_number, attr->value);
+ }
}
+
+ BaseVisitor::Visit(node);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XmlResourceVisitor);
};
-struct TransitionVisitor : public BaseVisitor {
- TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+class TransitionVisitor : public BaseVisitor {
+ public:
+ TransitionVisitor(const Source& source, KeepSet* keep_set)
+ : BaseVisitor(source, keep_set) {}
+
+ virtual void Visit(xml::Element* node) override {
+ bool check_class =
+ node->namespace_uri.empty() &&
+ (node->name == "transition" || node->name == "pathMotion");
+ if (check_class) {
+ xml::Attribute* attr = node->FindAttribute({}, "class");
+ if (attr && util::IsJavaClassName(attr->value)) {
+ AddClass(node->line_number, attr->value);
+ }
}
- virtual void visit(xml::Element* node) override {
- bool checkClass = node->namespaceUri.empty() &&
- (node->name == "transition" || node->name == "pathMotion");
- if (checkClass) {
- xml::Attribute* attr = node->findAttribute({}, "class");
- if (attr && util::isJavaClassName(attr->value)) {
- addClass(node->lineNumber, attr->value);
- }
- }
+ BaseVisitor::Visit(node);
+ }
- BaseVisitor::visit(node);
- }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TransitionVisitor);
};
-struct ManifestVisitor : public BaseVisitor {
- ManifestVisitor(const Source& source, KeepSet* keepSet, bool mainDexOnly)
- : BaseVisitor(source, keepSet), mMainDexOnly(mainDexOnly) {
- }
+class ManifestVisitor : public BaseVisitor {
+ public:
+ ManifestVisitor(const Source& source, KeepSet* keep_set, bool main_dex_only)
+ : BaseVisitor(source, keep_set), main_dex_only_(main_dex_only) {}
- virtual void visit(xml::Element* node) override {
- if (node->namespaceUri.empty()) {
- bool getName = false;
- if (node->name == "manifest") {
- xml::Attribute* attr = node->findAttribute({}, "package");
- if (attr) {
- mPackage = attr->value;
- }
- } else if (node->name == "application") {
- getName = true;
- xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "backupAgent");
- if (attr) {
- Maybe<std::string> result = util::getFullyQualifiedClassName(mPackage,
- attr->value);
- if (result) {
- addClass(node->lineNumber, result.value());
- }
- }
- if (mMainDexOnly) {
- xml::Attribute* defaultProcess = node->findAttribute(xml::kSchemaAndroid,
- "process");
- if (defaultProcess) {
- mDefaultProcess = defaultProcess->value;
- }
- }
- } else if (node->name == "activity" || node->name == "service" ||
- node->name == "receiver" || node->name == "provider") {
- getName = true;
-
- if (mMainDexOnly) {
- xml::Attribute* componentProcess = node->findAttribute(xml::kSchemaAndroid,
- "process");
-
- const std::string& process = componentProcess ? componentProcess->value
- : mDefaultProcess;
- getName = !process.empty() && process[0] != ':';
- }
- } else if (node-> name == "instrumentation") {
- getName = true;
- }
-
- if (getName) {
- xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "name");
- getName = attr != nullptr;
-
- if (getName) {
- Maybe<std::string> result = util::getFullyQualifiedClassName(mPackage,
- attr->value);
- if (result) {
- addClass(node->lineNumber, result.value());
- }
- }
- }
+ virtual void Visit(xml::Element* node) override {
+ if (node->namespace_uri.empty()) {
+ bool get_name = false;
+ if (node->name == "manifest") {
+ xml::Attribute* attr = node->FindAttribute({}, "package");
+ if (attr) {
+ package_ = attr->value;
}
- BaseVisitor::visit(node);
- }
+ } else if (node->name == "application") {
+ get_name = true;
+ xml::Attribute* attr =
+ node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
+ if (attr) {
+ Maybe<std::string> result =
+ util::GetFullyQualifiedClassName(package_, attr->value);
+ if (result) {
+ AddClass(node->line_number, result.value());
+ }
+ }
+ if (main_dex_only_) {
+ xml::Attribute* default_process =
+ node->FindAttribute(xml::kSchemaAndroid, "process");
+ if (default_process) {
+ default_process_ = default_process->value;
+ }
+ }
+ } else if (node->name == "activity" || node->name == "service" ||
+ node->name == "receiver" || node->name == "provider") {
+ get_name = true;
-private:
- std::string mPackage;
- const bool mMainDexOnly;
- std::string mDefaultProcess;
+ if (main_dex_only_) {
+ xml::Attribute* component_process =
+ node->FindAttribute(xml::kSchemaAndroid, "process");
+
+ const std::string& process =
+ component_process ? component_process->value : default_process_;
+ get_name = !process.empty() && process[0] != ':';
+ }
+ } else if (node->name == "instrumentation") {
+ get_name = true;
+ }
+
+ if (get_name) {
+ xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "name");
+ get_name = attr != nullptr;
+
+ if (get_name) {
+ Maybe<std::string> result =
+ util::GetFullyQualifiedClassName(package_, attr->value);
+ if (result) {
+ AddClass(node->line_number, result.value());
+ }
+ }
+ }
+ }
+ BaseVisitor::Visit(node);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ManifestVisitor);
+
+ std::string package_;
+ const bool main_dex_only_;
+ std::string default_process_;
};
-bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res,
- KeepSet* keepSet, bool mainDexOnly) {
- ManifestVisitor visitor(source, keepSet, mainDexOnly);
- if (res->root) {
- res->root->accept(&visitor);
- return true;
- }
+bool CollectProguardRulesForManifest(const Source& source,
+ xml::XmlResource* res, KeepSet* keep_set,
+ bool main_dex_only) {
+ ManifestVisitor visitor(source, keep_set, main_dex_only);
+ if (res->root) {
+ res->root->Accept(&visitor);
+ return true;
+ }
+ return false;
+}
+
+bool CollectProguardRules(const Source& source, xml::XmlResource* res,
+ KeepSet* keep_set) {
+ if (!res->root) {
return false;
+ }
+
+ switch (res->file.name.type) {
+ case ResourceType::kLayout: {
+ LayoutVisitor visitor(source, keep_set);
+ res->root->Accept(&visitor);
+ break;
+ }
+
+ case ResourceType::kXml: {
+ XmlResourceVisitor visitor(source, keep_set);
+ res->root->Accept(&visitor);
+ break;
+ }
+
+ case ResourceType::kTransition: {
+ TransitionVisitor visitor(source, keep_set);
+ res->root->Accept(&visitor);
+ break;
+ }
+
+ default:
+ break;
+ }
+ return true;
}
-bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet) {
- if (!res->root) {
- return false;
+bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
+ for (const auto& entry : keep_set.keep_set_) {
+ for (const Source& source : entry.second) {
+ *out << "# Referenced at " << source << "\n";
}
+ *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+ }
- switch (res->file.name.type) {
- case ResourceType::kLayout: {
- LayoutVisitor visitor(source, keepSet);
- res->root->accept(&visitor);
- break;
- }
-
- case ResourceType::kXml: {
- XmlResourceVisitor visitor(source, keepSet);
- res->root->accept(&visitor);
- break;
- }
-
- case ResourceType::kTransition: {
- TransitionVisitor visitor(source, keepSet);
- res->root->accept(&visitor);
- break;
- }
-
- default:
- break;
+ for (const auto& entry : keep_set.keep_method_set_) {
+ for (const Source& source : entry.second) {
+ *out << "# Referenced at " << source << "\n";
}
- return true;
+ *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n"
+ << std::endl;
+ }
+ return true;
}
-bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
- for (const auto& entry : keepSet.mKeepSet) {
- for (const Source& source : entry.second) {
- *out << "# Referenced at " << source << "\n";
- }
- *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
- }
-
- for (const auto& entry : keepSet.mKeepMethodSet) {
- for (const Source& source : entry.second) {
- *out << "# Referenced at " << source << "\n";
- }
- *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
- }
- return true;
-}
-
-} // namespace proguard
-} // namespace aapt
+} // namespace proguard
+} // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index c2d2bd9..3c349ba 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -17,42 +17,44 @@
#ifndef AAPT_PROGUARD_RULES_H
#define AAPT_PROGUARD_RULES_H
-#include "Resource.h"
-#include "Source.h"
-#include "xml/XmlDom.h"
-
#include <map>
#include <ostream>
#include <set>
#include <string>
+#include "Resource.h"
+#include "Source.h"
+#include "xml/XmlDom.h"
+
namespace aapt {
namespace proguard {
class KeepSet {
-public:
- inline void addClass(const Source& source, const std::string& className) {
- mKeepSet[className].insert(source);
- }
+ public:
+ inline void AddClass(const Source& source, const std::string& class_name) {
+ keep_set_[class_name].insert(source);
+ }
- inline void addMethod(const Source& source, const std::string& methodName) {
- mKeepMethodSet[methodName].insert(source);
- }
+ inline void AddMethod(const Source& source, const std::string& method_name) {
+ keep_method_set_[method_name].insert(source);
+ }
-private:
- friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+ private:
+ friend bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);
- std::map<std::string, std::set<Source>> mKeepSet;
- std::map<std::string, std::set<Source>> mKeepMethodSet;
+ std::map<std::string, std::set<Source>> keep_set_;
+ std::map<std::string, std::set<Source>> keep_method_set_;
};
-bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keepSet,
- bool mainDexOnly = false);
-bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet);
+bool CollectProguardRulesForManifest(const Source& source,
+ xml::XmlResource* res, KeepSet* keep_set,
+ bool main_dex_only = false);
+bool CollectProguardRules(const Source& source, xml::XmlResource* res,
+ KeepSet* keep_set);
-bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);
-} // namespace proguard
-} // namespace aapt
+} // namespace proguard
+} // namespace aapt
-#endif // AAPT_PROGUARD_RULES_H
+#endif // AAPT_PROGUARD_RULES_H
diff --git a/tools/aapt2/jni/Aapt2.java b/tools/aapt2/jni/Aapt2.java
new file mode 100644
index 0000000..aed23de
--- /dev/null
+++ b/tools/aapt2/jni/Aapt2.java
@@ -0,0 +1,44 @@
+package com.android.tools.aapt2;
+
+import java.util.List;
+
+/**
+ * {@code aapt2} JNI interface. To use the {@code aapt2} native interface, the
+ * shared library must first be loaded and then a new instance of this class can
+ * be used to access the library.
+ */
+public class Aapt2 {
+
+ /**
+ * Invokes {@code aapt2} to perform resource compilation.
+ *
+ * @param arguments arguments for compilation (see {@code Compile.cpp})
+ */
+ public static void compile(List<String> arguments) {
+ nativeCompile(arguments);
+ }
+
+ /**
+ * Invokes {@code aapt2} to perform linking.
+ *
+ * @param arguments arguments for linking (see {@code Link.cpp})
+ */
+ public static void link(List<String> arguments) {
+ nativeLink(arguments);
+ }
+
+ /**
+ * JNI call.
+ *
+ * @param arguments arguments for compilation (see {@code Compile.cpp})
+ */
+ private static native void nativeCompile(List<String> arguments);
+
+ /**
+ * JNI call.
+ *
+ * @param arguments arguments for linking (see {@code Link.cpp})
+ */
+ private static native void nativeLink(List<String> arguments);
+}
+
diff --git a/tools/aapt2/jni/Makefile b/tools/aapt2/jni/Makefile
new file mode 100644
index 0000000..a9e628f
--- /dev/null
+++ b/tools/aapt2/jni/Makefile
@@ -0,0 +1,25 @@
+#
+# This Makefile will generate the JNI headers for the Aapt2 class.
+#
+
+AAPT2_PKG=com.android.tools.aapt2
+AAPT2_DIR=$(shell echo -n com/android/tools/aapt2 | tr . /)
+OUT=out
+OUT_CLASSES=$(OUT)/classes
+OUT_HEADERS=.
+
+AAPT2_JAVA=Aapt2.java
+AAPT2_CLASSES=$(OUT_CLASSES)/$(AAPT2_DIR)/Aapt2.class
+
+AAPT2_HEADERS=$(OUT_HEADERS)/Aapt2.h
+
+all: $(AAPT2_HEADERS)
+
+$(AAPT2_HEADERS): $(AAPT2_JAVA) $(AAPT2_CLASSES)
+ mkdir -p $(OUT_HEADERS)
+ $(JAVA_HOME)/bin/javah -d $(OUT_HEADERS) -cp $(OUT_CLASSES) $(AAPT2_PKG).Aapt2
+
+$(AAPT2_CLASSES): $(AAPT2_JAVA)
+ mkdir -p $(OUT_CLASSES)
+ javac -d $(OUT_CLASSES) $(AAPT2_JAVA)
+
diff --git a/tools/aapt2/jni/aapt2_jni.cpp b/tools/aapt2/jni/aapt2_jni.cpp
new file mode 100644
index 0000000..211ada8
--- /dev/null
+++ b/tools/aapt2/jni/aapt2_jni.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "com_android_tools_aapt2_Aapt2.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "nativehelper/ScopedUtfChars.h"
+
+#include "util/Util.h"
+
+namespace aapt {
+extern int Compile(const std::vector<StringPiece> &args);
+extern int Link(const std::vector<StringPiece> &args);
+}
+
+/*
+ * Converts a java List<String> into C++ vector<ScopedUtfChars>.
+ */
+static std::vector<ScopedUtfChars> list_to_utfchars(JNIEnv *env, jobject obj) {
+ std::vector<ScopedUtfChars> converted;
+
+ // Call size() method on the list to know how many elements there are.
+ jclass list_cls = env->GetObjectClass(obj);
+ jmethodID size_method_id = env->GetMethodID(list_cls, "size", "()I");
+ CHECK(size_method_id != 0);
+ jint size = env->CallIntMethod(obj, size_method_id);
+ CHECK(size >= 0);
+
+ // Now, iterate all strings in the list
+ // (note: generic erasure means get() return an Object)
+ jmethodID get_method_id =
+ env->GetMethodID(list_cls, "get", "()Ljava/lang/Object;");
+ for (jint i = 0; i < size; i++) {
+ // Call get(i) to get the string in the ith position.
+ jobject string_obj_uncast = env->CallObjectMethod(obj, get_method_id, i);
+ CHECK(string_obj_uncast != nullptr);
+ jstring string_obj = static_cast<jstring>(string_obj_uncast);
+ converted.push_back(ScopedUtfChars(env, string_obj));
+ }
+
+ return converted;
+}
+
+/*
+ * Extracts all StringPiece from the ScopedUtfChars instances.
+ *
+ * The returned pieces can only be used while the original ones have not been
+ * destroyed.
+ */
+static std::vector<aapt::StringPiece> extract_pieces(
+ const std::vector<ScopedUtfChars> &strings) {
+ std::vector<aapt::StringPiece> pieces;
+
+ std::for_each(
+ strings.begin(), strings.end(),
+ [&pieces](const ScopedUtfChars &p) { pieces.push_back(p.c_str()); });
+
+ return pieces;
+}
+
+JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2_nativeCompile(
+ JNIEnv *env, jclass aapt_obj, jobject arguments_obj) {
+ std::vector<ScopedUtfChars> compile_args_jni =
+ list_to_utfchars(env, arguments_obj);
+ std::vector<aapt::StringPiece> compile_args =
+ extract_pieces(compile_args_jni);
+ aapt::Compile(compile_args);
+}
+
+JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2_nativeLink(
+ JNIEnv *env, jclass aapt_obj, jobject arguments_obj) {
+ std::vector<ScopedUtfChars> link_args_jni =
+ list_to_utfchars(env, arguments_obj);
+ std::vector<aapt::StringPiece> link_args = extract_pieces(link_args_jni);
+ aapt::Link(link_args);
+}
diff --git a/tools/aapt2/jni/com_android_tools_aapt2_Aapt2.h b/tools/aapt2/jni/com_android_tools_aapt2_Aapt2.h
new file mode 100644
index 0000000..443b98f
--- /dev/null
+++ b/tools/aapt2/jni/com_android_tools_aapt2_Aapt2.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_android_tools_aapt2_Aapt2 */
+
+#ifndef _Included_com_android_tools_aapt2_Aapt2
+#define _Included_com_android_tools_aapt2_Aapt2
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_android_tools_aapt2_Aapt2
+ * Method: nativeCompile
+ * Signature: (Ljava/util/List;)V
+ */
+JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2_nativeCompile
+ (JNIEnv *, jclass, jobject);
+
+/*
+ * Class: com_android_tools_aapt2_Aapt2
+ * Method: nativeLink
+ * Signature: (Ljava/util/List;)V
+ */
+JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2_nativeLink
+ (JNIEnv *, jclass, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index 8ed27c3..77471ea 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -14,128 +14,142 @@
* limitations under the License.
*/
+#include "link/Linkers.h"
+
+#include <algorithm>
+
+#include "android-base/logging.h"
+
#include "ConfigDescription.h"
#include "ResourceTable.h"
#include "SdkConstants.h"
#include "ValueVisitor.h"
-#include "link/Linkers.h"
-
-#include <algorithm>
-#include <cassert>
namespace aapt {
-bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
- const int sdkVersionToGenerate) {
- // We assume the caller is trying to generate a version greater than the current configuration.
- assert(sdkVersionToGenerate > config.sdkVersion);
+bool ShouldGenerateVersionedResource(const ResourceEntry* entry,
+ const ConfigDescription& config,
+ const int sdk_version_to_generate) {
+ // We assume the caller is trying to generate a version greater than the
+ // current configuration.
+ CHECK(sdk_version_to_generate > config.sdkVersion);
- const auto endIter = entry->values.end();
- auto iter = entry->values.begin();
- for (; iter != endIter; ++iter) {
- if ((*iter)->config == config) {
- break;
- }
+ const auto end_iter = entry->values.end();
+ auto iter = entry->values.begin();
+ for (; iter != end_iter; ++iter) {
+ if ((*iter)->config == config) {
+ break;
}
+ }
- // The source config came from this list, so it should be here.
- assert(iter != entry->values.end());
- ++iter;
+ // The source config came from this list, so it should be here.
+ CHECK(iter != entry->values.end());
+ ++iter;
- // The next configuration either only varies in sdkVersion, or it is completely different
- // and therefore incompatible. If it is incompatible, we must generate the versioned resource.
+ // The next configuration either only varies in sdkVersion, or it is
+ // completely different
+ // and therefore incompatible. If it is incompatible, we must generate the
+ // versioned resource.
- // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
- // qualifiers, so we need to iterate through the entire list to be sure there
- // are no higher sdk level versions of this resource.
- ConfigDescription tempConfig(config);
- for (; iter != endIter; ++iter) {
- tempConfig.sdkVersion = (*iter)->config.sdkVersion;
- if (tempConfig == (*iter)->config) {
- // The two configs are the same, check the sdk version.
- return sdkVersionToGenerate < (*iter)->config.sdkVersion;
- }
+ // NOTE: The ordering of configurations takes sdkVersion as higher precedence
+ // than other
+ // qualifiers, so we need to iterate through the entire list to be sure there
+ // are no higher sdk level versions of this resource.
+ ConfigDescription temp_config(config);
+ for (; iter != end_iter; ++iter) {
+ temp_config.sdkVersion = (*iter)->config.sdkVersion;
+ if (temp_config == (*iter)->config) {
+ // The two configs are the same, check the sdk version.
+ return sdk_version_to_generate < (*iter)->config.sdkVersion;
}
+ }
- // No match was found, so we should generate the versioned resource.
- return true;
+ // No match was found, so we should generate the versioned resource.
+ return true;
}
-bool AutoVersioner::consume(IAaptContext* context, ResourceTable* table) {
- for (auto& package : table->packages) {
- for (auto& type : package->types) {
- if (type->type != ResourceType::kStyle) {
- continue;
- }
+bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ if (type->type != ResourceType::kStyle) {
+ continue;
+ }
- for (auto& entry : type->entries) {
- for (size_t i = 0; i < entry->values.size(); i++) {
- ResourceConfigValue* configValue = entry->values[i].get();
- if (configValue->config.sdkVersion >= SDK_LOLLIPOP_MR1) {
- // If this configuration is only used on L-MR1 then we don't need
- // to do anything since we use private attributes since that version.
- continue;
- }
+ for (auto& entry : type->entries) {
+ for (size_t i = 0; i < entry->values.size(); i++) {
+ ResourceConfigValue* config_value = entry->values[i].get();
+ if (config_value->config.sdkVersion >= SDK_LOLLIPOP_MR1) {
+ // If this configuration is only used on L-MR1 then we don't need
+ // to do anything since we use private attributes since that
+ // version.
+ continue;
+ }
- if (Style* style = valueCast<Style>(configValue->value.get())) {
- Maybe<size_t> minSdkStripped;
- std::vector<Style::Entry> stripped;
+ if (Style* style = ValueCast<Style>(config_value->value.get())) {
+ Maybe<size_t> min_sdk_stripped;
+ std::vector<Style::Entry> stripped;
- auto iter = style->entries.begin();
- while (iter != style->entries.end()) {
- assert(iter->key.id && "IDs must be assigned and linked");
+ auto iter = style->entries.begin();
+ while (iter != style->entries.end()) {
+ CHECK(bool(iter->key.id)) << "IDs must be assigned and linked";
- // Find the SDK level that is higher than the configuration allows.
- const size_t sdkLevel = findAttributeSdkLevel(iter->key.id.value());
- if (sdkLevel > std::max<size_t>(configValue->config.sdkVersion, 1)) {
- // Record that we are about to strip this.
- stripped.emplace_back(std::move(*iter));
+ // Find the SDK level that is higher than the configuration
+ // allows.
+ const size_t sdk_level =
+ FindAttributeSdkLevel(iter->key.id.value());
+ if (sdk_level >
+ std::max<size_t>(config_value->config.sdkVersion, 1)) {
+ // Record that we are about to strip this.
+ stripped.emplace_back(std::move(*iter));
- // We use the smallest SDK level to generate the new style.
- if (minSdkStripped) {
- minSdkStripped = std::min(minSdkStripped.value(), sdkLevel);
- } else {
- minSdkStripped = sdkLevel;
- }
-
- // Erase this from this style.
- iter = style->entries.erase(iter);
- continue;
- }
- ++iter;
- }
-
- if (minSdkStripped && !stripped.empty()) {
- // We found attributes from a higher SDK level. Check that
- // there is no other defined resource for the version we want to
- // generate.
- if (shouldGenerateVersionedResource(entry.get(),
- configValue->config,
- minSdkStripped.value())) {
- // Let's create a new Style for this versioned resource.
- ConfigDescription newConfig(configValue->config);
- newConfig.sdkVersion = minSdkStripped.value();
-
- std::unique_ptr<Style> newStyle(style->clone(&table->stringPool));
- newStyle->setComment(style->getComment());
- newStyle->setSource(style->getSource());
-
- // Move the previously stripped attributes into this style.
- newStyle->entries.insert(newStyle->entries.end(),
- std::make_move_iterator(stripped.begin()),
- std::make_move_iterator(stripped.end()));
-
- // Insert the new Resource into the correct place.
- entry->findOrCreateValue(newConfig, {})->value =
- std::move(newStyle);
- }
- }
- }
+ // We use the smallest SDK level to generate the new style.
+ if (min_sdk_stripped) {
+ min_sdk_stripped =
+ std::min(min_sdk_stripped.value(), sdk_level);
+ } else {
+ min_sdk_stripped = sdk_level;
}
+
+ // Erase this from this style.
+ iter = style->entries.erase(iter);
+ continue;
+ }
+ ++iter;
}
+
+ if (min_sdk_stripped && !stripped.empty()) {
+ // We found attributes from a higher SDK level. Check that
+ // there is no other defined resource for the version we want to
+ // generate.
+ if (ShouldGenerateVersionedResource(entry.get(),
+ config_value->config,
+ min_sdk_stripped.value())) {
+ // Let's create a new Style for this versioned resource.
+ ConfigDescription new_config(config_value->config);
+ new_config.sdkVersion = min_sdk_stripped.value();
+
+ std::unique_ptr<Style> new_style(
+ style->Clone(&table->string_pool));
+ new_style->SetComment(style->GetComment());
+ new_style->SetSource(style->GetSource());
+
+ // Move the previously stripped attributes into this style.
+ new_style->entries.insert(
+ new_style->entries.end(),
+ std::make_move_iterator(stripped.begin()),
+ std::make_move_iterator(stripped.end()));
+
+ // Insert the new Resource into the correct place.
+ entry->FindOrCreateValue(new_config, {})->value =
+ std::move(new_style);
+ }
+ }
+ }
}
+ }
}
- return true;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 3a61da5..755af0a 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -14,109 +14,124 @@
* limitations under the License.
*/
-#include "ConfigDescription.h"
#include "link/Linkers.h"
+
+#include "ConfigDescription.h"
#include "test/Test.h"
namespace aapt {
TEST(AutoVersionerTest, GenerateVersionedResources) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription landConfig = test::parseConfigOrDie("land");
- const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land");
+ const ConfigDescription land_config = test::ParseConfigOrDie("land");
+ const ConfigDescription sw600dp_land_config =
+ test::ParseConfigOrDie("sw600dp-land");
- ResourceEntry entry("foo");
- entry.values.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
- entry.values.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
- entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
+ ResourceEntry entry("foo");
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(
+ ConfigDescription::DefaultConfig(), ""));
+ entry.values.push_back(
+ util::make_unique<ResourceConfigValue>(land_config, ""));
+ entry.values.push_back(
+ util::make_unique<ResourceConfigValue>(sw600dp_land_config, ""));
- EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
- EXPECT_TRUE(shouldGenerateVersionedResource(&entry, landConfig, 17));
+ EXPECT_TRUE(ShouldGenerateVersionedResource(
+ &entry, ConfigDescription::DefaultConfig(), 17));
+ EXPECT_TRUE(ShouldGenerateVersionedResource(&entry, land_config, 17));
}
TEST(AutoVersionerTest, GenerateVersionedResourceWhenHigherVersionExists) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription sw600dpV13Config = test::parseConfigOrDie("sw600dp-v13");
- const ConfigDescription v21Config = test::parseConfigOrDie("v21");
+ const ConfigDescription sw600dp_v13_config =
+ test::ParseConfigOrDie("sw600dp-v13");
+ const ConfigDescription v21_config = test::ParseConfigOrDie("v21");
- ResourceEntry entry("foo");
- entry.values.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
- entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dpV13Config, ""));
- entry.values.push_back(util::make_unique<ResourceConfigValue>(v21Config, ""));
+ ResourceEntry entry("foo");
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(
+ ConfigDescription::DefaultConfig(), ""));
+ entry.values.push_back(
+ util::make_unique<ResourceConfigValue>(sw600dp_v13_config, ""));
+ entry.values.push_back(
+ util::make_unique<ResourceConfigValue>(v21_config, ""));
- EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
- EXPECT_FALSE(shouldGenerateVersionedResource(&entry, defaultConfig, 22));
+ EXPECT_TRUE(ShouldGenerateVersionedResource(
+ &entry, ConfigDescription::DefaultConfig(), 17));
+ EXPECT_FALSE(ShouldGenerateVersionedResource(
+ &entry, ConfigDescription::DefaultConfig(), 22));
}
TEST(AutoVersionerTest, VersionStylesForTable) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("app", 0x7f)
- .addValue("app:style/Foo", test::parseConfigOrDie("v4"), ResourceId(0x7f020000),
- test::StyleBuilder()
- .addItem("android:attr/onClick", ResourceId(0x0101026f),
- util::make_unique<Id>())
- .addItem("android:attr/paddingStart", ResourceId(0x010103b3),
- util::make_unique<Id>())
- .addItem("android:attr/requiresSmallestWidthDp",
- ResourceId(0x01010364), util::make_unique<Id>())
- .addItem("android:attr/colorAccent", ResourceId(0x01010435),
- util::make_unique<Id>())
- .build())
- .addValue("app:style/Foo", test::parseConfigOrDie("v21"), ResourceId(0x7f020000),
- test::StyleBuilder()
- .addItem("android:attr/paddingEnd", ResourceId(0x010103b4),
- util::make_unique<Id>())
- .build())
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("app", 0x7f)
+ .AddValue(
+ "app:style/Foo", test::ParseConfigOrDie("v4"),
+ ResourceId(0x7f020000),
+ test::StyleBuilder()
+ .AddItem("android:attr/onClick", ResourceId(0x0101026f),
+ util::make_unique<Id>())
+ .AddItem("android:attr/paddingStart", ResourceId(0x010103b3),
+ util::make_unique<Id>())
+ .AddItem("android:attr/requiresSmallestWidthDp",
+ ResourceId(0x01010364), util::make_unique<Id>())
+ .AddItem("android:attr/colorAccent", ResourceId(0x01010435),
+ util::make_unique<Id>())
+ .Build())
+ .AddValue(
+ "app:style/Foo", test::ParseConfigOrDie("v21"),
+ ResourceId(0x7f020000),
+ test::StyleBuilder()
+ .AddItem("android:attr/paddingEnd", ResourceId(0x010103b4),
+ util::make_unique<Id>())
+ .Build())
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .setCompilationPackage("app")
- .setPackageId(0x7f)
- .build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .SetCompilationPackage("app")
+ .SetPackageId(0x7f)
+ .Build();
- AutoVersioner versioner;
- ASSERT_TRUE(versioner.consume(context.get(), table.get()));
+ AutoVersioner versioner;
+ ASSERT_TRUE(versioner.Consume(context.get(), table.get()));
- Style* style = test::getValueForConfig<Style>(table.get(), "app:style/Foo",
- test::parseConfigOrDie("v4"));
- ASSERT_NE(style, nullptr);
- ASSERT_EQ(style->entries.size(), 1u);
- AAPT_ASSERT_TRUE(style->entries.front().key.name);
- EXPECT_EQ(style->entries.front().key.name.value(),
- test::parseNameOrDie("android:attr/onClick"));
+ Style* style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo",
+ test::ParseConfigOrDie("v4"));
+ ASSERT_NE(style, nullptr);
+ ASSERT_EQ(style->entries.size(), 1u);
+ AAPT_ASSERT_TRUE(style->entries.front().key.name);
+ EXPECT_EQ(style->entries.front().key.name.value(),
+ test::ParseNameOrDie("android:attr/onClick"));
- style = test::getValueForConfig<Style>(table.get(), "app:style/Foo",
- test::parseConfigOrDie("v13"));
- ASSERT_NE(style, nullptr);
- ASSERT_EQ(style->entries.size(), 2u);
- AAPT_ASSERT_TRUE(style->entries[0].key.name);
- EXPECT_EQ(style->entries[0].key.name.value(),
- test::parseNameOrDie("android:attr/onClick"));
- AAPT_ASSERT_TRUE(style->entries[1].key.name);
- EXPECT_EQ(style->entries[1].key.name.value(),
- test::parseNameOrDie("android:attr/requiresSmallestWidthDp"));
+ style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo",
+ test::ParseConfigOrDie("v13"));
+ ASSERT_NE(style, nullptr);
+ ASSERT_EQ(style->entries.size(), 2u);
+ AAPT_ASSERT_TRUE(style->entries[0].key.name);
+ EXPECT_EQ(style->entries[0].key.name.value(),
+ test::ParseNameOrDie("android:attr/onClick"));
+ AAPT_ASSERT_TRUE(style->entries[1].key.name);
+ EXPECT_EQ(style->entries[1].key.name.value(),
+ test::ParseNameOrDie("android:attr/requiresSmallestWidthDp"));
- style = test::getValueForConfig<Style>(table.get(), "app:style/Foo",
- test::parseConfigOrDie("v17"));
- ASSERT_NE(style, nullptr);
- ASSERT_EQ(style->entries.size(), 3u);
- AAPT_ASSERT_TRUE(style->entries[0].key.name);
- EXPECT_EQ(style->entries[0].key.name.value(),
- test::parseNameOrDie("android:attr/onClick"));
- AAPT_ASSERT_TRUE(style->entries[1].key.name);
- EXPECT_EQ(style->entries[1].key.name.value(),
- test::parseNameOrDie("android:attr/requiresSmallestWidthDp"));
- AAPT_ASSERT_TRUE(style->entries[2].key.name);
- EXPECT_EQ(style->entries[2].key.name.value(),
- test::parseNameOrDie("android:attr/paddingStart"));
+ style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo",
+ test::ParseConfigOrDie("v17"));
+ ASSERT_NE(style, nullptr);
+ ASSERT_EQ(style->entries.size(), 3u);
+ AAPT_ASSERT_TRUE(style->entries[0].key.name);
+ EXPECT_EQ(style->entries[0].key.name.value(),
+ test::ParseNameOrDie("android:attr/onClick"));
+ AAPT_ASSERT_TRUE(style->entries[1].key.name);
+ EXPECT_EQ(style->entries[1].key.name.value(),
+ test::ParseNameOrDie("android:attr/requiresSmallestWidthDp"));
+ AAPT_ASSERT_TRUE(style->entries[2].key.name);
+ EXPECT_EQ(style->entries[2].key.name.value(),
+ test::ParseNameOrDie("android:attr/paddingStart"));
- style = test::getValueForConfig<Style>(table.get(), "app:style/Foo",
- test::parseConfigOrDie("v21"));
- ASSERT_NE(style, nullptr);
- ASSERT_EQ(style->entries.size(), 1u);
- AAPT_ASSERT_TRUE(style->entries.front().key.name);
- EXPECT_EQ(style->entries.front().key.name.value(),
- test::parseNameOrDie("android:attr/paddingEnd"));
+ style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo",
+ test::ParseConfigOrDie("v21"));
+ ASSERT_NE(style, nullptr);
+ ASSERT_EQ(style->entries.size(), 1u);
+ AAPT_ASSERT_TRUE(style->entries.front().key.name);
+ EXPECT_EQ(style->entries.front().key.name.value(),
+ test::ParseNameOrDie("android:attr/paddingEnd"));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index b6b4b473..717978ea 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -14,6 +14,17 @@
* limitations under the License.
*/
+#include <sys/stat.h>
+
+#include <fstream>
+#include <queue>
+#include <unordered_map>
+#include <vector>
+
+#include "android-base/errors.h"
+#include "android-base/file.h"
+#include "google/protobuf/io/coded_stream.h"
+
#include "AppInfo.h"
#include "Debug.h"
#include "Flags.h"
@@ -31,9 +42,8 @@
#include "java/ManifestClassGenerator.h"
#include "java/ProguardRules.h"
#include "link/Linkers.h"
-#include "link/ProductFilter.h"
-#include "link/ReferenceLinker.h"
#include "link/ManifestFixer.h"
+#include "link/ReferenceLinker.h"
#include "link/TableMerger.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
@@ -44,1922 +54,2059 @@
#include "util/StringPiece.h"
#include "xml/XmlDom.h"
-#include <android-base/file.h>
-#include <google/protobuf/io/coded_stream.h>
-
-#include <fstream>
-#include <queue>
-#include <sys/stat.h>
-#include <unordered_map>
-#include <vector>
-
-using google::protobuf::io::CopyingOutputStreamAdaptor;
+using ::google::protobuf::io::CopyingOutputStreamAdaptor;
namespace aapt {
struct LinkOptions {
- std::string outputPath;
- std::string manifestPath;
- std::vector<std::string> includePaths;
- std::vector<std::string> overlayFiles;
+ std::string output_path;
+ std::string manifest_path;
+ std::vector<std::string> include_paths;
+ std::vector<std::string> overlay_files;
+ bool output_to_directory = false;
+ bool auto_add_overlay = false;
- // Java/Proguard options.
- Maybe<std::string> generateJavaClassPath;
- Maybe<std::string> customJavaPackage;
- std::set<std::string> extraJavaPackages;
- Maybe<std::string> generateProguardRulesPath;
- Maybe<std::string> generateMainDexProguardRulesPath;
+ // Java/Proguard options.
+ Maybe<std::string> generate_java_class_path;
+ Maybe<std::string> custom_java_package;
+ std::set<std::string> extra_java_packages;
+ Maybe<std::string> generate_proguard_rules_path;
+ Maybe<std::string> generate_main_dex_proguard_rules_path;
+ bool generate_non_final_ids = false;
+ std::vector<std::string> javadoc_annotations;
+ Maybe<std::string> private_symbols;
- bool noAutoVersion = false;
- bool noVersionVectors = false;
- bool noResourceDeduping = false;
- bool staticLib = false;
- bool noStaticLibPackages = false;
- bool generateNonFinalIds = false;
- std::vector<std::string> javadocAnnotations;
- bool outputToDirectory = false;
- bool noXmlNamespaces = false;
- bool autoAddOverlay = false;
- bool doNotCompressAnything = false;
- std::unordered_set<std::string> extensionsToNotCompress;
- Maybe<std::string> privateSymbols;
- ManifestFixerOptions manifestFixerOptions;
- std::unordered_set<std::string> products;
+ // Optimizations/features.
+ bool no_auto_version = false;
+ bool no_version_vectors = false;
+ bool no_resource_deduping = false;
+ bool no_xml_namespaces = false;
+ bool do_not_compress_anything = false;
+ std::unordered_set<std::string> extensions_to_not_compress;
- // Split APK options.
- TableSplitterOptions tableSplitterOptions;
- std::vector<SplitConstraints> splitConstraints;
- std::vector<std::string> splitPaths;
+ // Static lib options.
+ bool static_lib = false;
+ bool no_static_lib_packages = false;
- // Stable ID options.
- std::unordered_map<ResourceName, ResourceId> stableIdMap;
- Maybe<std::string> resourceIdMapPath;
+ // AndroidManifest.xml massaging options.
+ ManifestFixerOptions manifest_fixer_options;
+
+ // Products to use/filter on.
+ std::unordered_set<std::string> products;
+
+ // Split APK options.
+ TableSplitterOptions table_splitter_options;
+ std::vector<SplitConstraints> split_constraints;
+ std::vector<std::string> split_paths;
+
+ // Stable ID options.
+ std::unordered_map<ResourceName, ResourceId> stable_id_map;
+ Maybe<std::string> resource_id_map_path;
};
class LinkContext : public IAaptContext {
-public:
- LinkContext() : mNameMangler({}) {
- }
+ public:
+ LinkContext() : name_mangler_({}) {}
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ IDiagnostics* GetDiagnostics() override { return &diagnostics_; }
- NameMangler* getNameMangler() override {
- return &mNameMangler;
- }
+ NameMangler* GetNameMangler() override { return &name_mangler_; }
- void setNameManglerPolicy(const NameManglerPolicy& policy) {
- mNameMangler = NameMangler(policy);
- }
+ void SetNameManglerPolicy(const NameManglerPolicy& policy) {
+ name_mangler_ = NameMangler(policy);
+ }
- const std::string& getCompilationPackage() override {
- return mCompilationPackage;
- }
+ const std::string& GetCompilationPackage() override {
+ return compilation_package_;
+ }
- void setCompilationPackage(const StringPiece& packageName) {
- mCompilationPackage = packageName.toString();
- }
+ void SetCompilationPackage(const StringPiece& package_name) {
+ compilation_package_ = package_name.ToString();
+ }
- uint8_t getPackageId() override {
- return mPackageId;
- }
+ uint8_t GetPackageId() override { return package_id_; }
- void setPackageId(uint8_t id) {
- mPackageId = id;
- }
+ void SetPackageId(uint8_t id) { package_id_ = id; }
- SymbolTable* getExternalSymbols() override {
- return &mSymbols;
- }
+ SymbolTable* GetExternalSymbols() override { return &symbols_; }
- bool verbose() override {
- return mVerbose;
- }
+ bool IsVerbose() override { return verbose_; }
- void setVerbose(bool val) {
- mVerbose = val;
- }
+ void SetVerbose(bool val) { verbose_ = val; }
- int getMinSdkVersion() override {
- return mMinSdkVersion;
- }
+ int GetMinSdkVersion() override { return min_sdk_version_; }
- void setMinSdkVersion(int minSdk) {
- mMinSdkVersion = minSdk;
- }
+ void SetMinSdkVersion(int minSdk) { min_sdk_version_ = minSdk; }
-private:
- StdErrDiagnostics mDiagnostics;
- NameMangler mNameMangler;
- std::string mCompilationPackage;
- uint8_t mPackageId = 0x0;
- SymbolTable mSymbols;
- bool mVerbose = false;
- int mMinSdkVersion = 0;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LinkContext);
+
+ StdErrDiagnostics diagnostics_;
+ NameMangler name_mangler_;
+ std::string compilation_package_;
+ uint8_t package_id_ = 0x0;
+ SymbolTable symbols_;
+ bool verbose_ = false;
+ int min_sdk_version_ = 0;
};
-static bool copyFileToArchive(io::IFile* file, const std::string& outPath,
- uint32_t compressionFlags,
+static bool CopyFileToArchive(io::IFile* file, const std::string& out_path,
+ uint32_t compression_flags,
IArchiveWriter* writer, IAaptContext* context) {
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- context->getDiagnostics()->error(DiagMessage(file->getSource())
- << "failed to open file");
- return false;
- }
-
- const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data->data());
- const size_t bufferSize = data->size();
-
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage() << "writing " << outPath << " to archive");
- }
-
- if (writer->startEntry(outPath, compressionFlags)) {
- if (writer->writeEntry(buffer, bufferSize)) {
- if (writer->finishEntry()) {
- return true;
- }
- }
- }
-
- context->getDiagnostics()->error(DiagMessage() << "failed to write file " << outPath);
+ std::unique_ptr<io::IData> data = file->OpenAsData();
+ if (!data) {
+ context->GetDiagnostics()->Error(DiagMessage(file->GetSource())
+ << "failed to open file");
return false;
+ }
+
+ const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data->data());
+ const size_t buffer_size = data->size();
+
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(DiagMessage() << "writing " << out_path
+ << " to archive");
+ }
+
+ if (writer->StartEntry(out_path, compression_flags)) {
+ if (writer->WriteEntry(buffer, buffer_size)) {
+ if (writer->FinishEntry()) {
+ return true;
+ }
+ }
+ }
+
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to write file "
+ << out_path);
+ return false;
}
-static bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
- bool keepRawValues, IArchiveWriter* writer, IAaptContext* context) {
- BigBuffer buffer(1024);
- XmlFlattenerOptions options = {};
- options.keepRawValues = keepRawValues;
- options.maxSdkLevel = maxSdkLevel;
- XmlFlattener flattener(&buffer, options);
- if (!flattener.consume(context, xmlRes)) {
- return false;
- }
-
- if (context->verbose()) {
- DiagMessage msg;
- msg << "writing " << path << " to archive";
- if (maxSdkLevel) {
- msg << " maxSdkLevel=" << maxSdkLevel.value() << " keepRawValues=" << keepRawValues;
- }
- context->getDiagnostics()->note(msg);
- }
-
- if (writer->startEntry(path, ArchiveEntry::kCompress)) {
- if (writer->writeEntry(buffer)) {
- if (writer->finishEntry()) {
- return true;
- }
- }
- }
- context->getDiagnostics()->error(DiagMessage() << "failed to write " << path << " to archive");
+static bool FlattenXml(xml::XmlResource* xml_res, const StringPiece& path,
+ Maybe<size_t> max_sdk_level, bool keep_raw_values,
+ IArchiveWriter* writer, IAaptContext* context) {
+ BigBuffer buffer(1024);
+ XmlFlattenerOptions options = {};
+ options.keep_raw_values = keep_raw_values;
+ options.max_sdk_level = max_sdk_level;
+ XmlFlattener flattener(&buffer, options);
+ if (!flattener.Consume(context, xml_res)) {
return false;
+ }
+
+ if (context->IsVerbose()) {
+ DiagMessage msg;
+ msg << "writing " << path << " to archive";
+ if (max_sdk_level) {
+ msg << " maxSdkLevel=" << max_sdk_level.value()
+ << " keepRawValues=" << keep_raw_values;
+ }
+ context->GetDiagnostics()->Note(msg);
+ }
+
+ if (writer->StartEntry(path, ArchiveEntry::kCompress)) {
+ if (writer->WriteEntry(buffer)) {
+ if (writer->FinishEntry()) {
+ return true;
+ }
+ }
+ }
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to write " << path
+ << " to archive");
+ return false;
}
-static std::unique_ptr<ResourceTable> loadTableFromPb(const Source& source,
- const void* data, size_t len,
+static std::unique_ptr<ResourceTable> LoadTableFromPb(const Source& source,
+ const void* data,
+ size_t len,
IDiagnostics* diag) {
- pb::ResourceTable pbTable;
- if (!pbTable.ParseFromArray(data, len)) {
- diag->error(DiagMessage(source) << "invalid compiled table");
- return {};
- }
+ pb::ResourceTable pb_table;
+ if (!pb_table.ParseFromArray(data, len)) {
+ diag->Error(DiagMessage(source) << "invalid compiled table");
+ return {};
+ }
- std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source, diag);
- if (!table) {
- return {};
- }
- return table;
+ std::unique_ptr<ResourceTable> table =
+ DeserializeTableFromPb(pb_table, source, diag);
+ if (!table) {
+ return {};
+ }
+ return table;
}
/**
* Inflates an XML file from the source path.
*/
-static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
- std::ifstream fin(path, std::ifstream::binary);
- if (!fin) {
- diag->error(DiagMessage(path) << strerror(errno));
- return {};
- }
- return xml::inflate(&fin, diag, Source(path));
+static std::unique_ptr<xml::XmlResource> LoadXml(const std::string& path,
+ IDiagnostics* diag) {
+ std::ifstream fin(path, std::ifstream::binary);
+ if (!fin) {
+ diag->Error(DiagMessage(path) << strerror(errno));
+ return {};
+ }
+ return xml::Inflate(&fin, diag, Source(path));
}
struct ResourceFileFlattenerOptions {
- bool noAutoVersion = false;
- bool noVersionVectors = false;
- bool noXmlNamespaces = false;
- bool keepRawValues = false;
- bool doNotCompressAnything = false;
- bool updateProguardSpec = false;
- std::unordered_set<std::string> extensionsToNotCompress;
+ bool no_auto_version = false;
+ bool no_version_vectors = false;
+ bool no_xml_namespaces = false;
+ bool keep_raw_values = false;
+ bool do_not_compress_anything = false;
+ bool update_proguard_spec = false;
+ std::unordered_set<std::string> extensions_to_not_compress;
};
class ResourceFileFlattener {
-public:
- ResourceFileFlattener(const ResourceFileFlattenerOptions& options,
- IAaptContext* context, proguard::KeepSet* keepSet) :
- mOptions(options), mContext(context), mKeepSet(keepSet) {
- }
+ public:
+ ResourceFileFlattener(const ResourceFileFlattenerOptions& options,
+ IAaptContext* context, proguard::KeepSet* keep_set)
+ : options_(options), context_(context), keep_set_(keep_set) {}
- bool flatten(ResourceTable* table, IArchiveWriter* archiveWriter);
+ bool Flatten(ResourceTable* table, IArchiveWriter* archive_writer);
-private:
- struct FileOperation {
- ConfigDescription config;
+ private:
+ struct FileOperation {
+ ConfigDescription config;
- // The entry this file came from.
- const ResourceEntry* entry;
+ // The entry this file came from.
+ const ResourceEntry* entry;
- // The file to copy as-is.
- io::IFile* fileToCopy;
+ // The file to copy as-is.
+ io::IFile* file_to_copy;
- // The XML to process and flatten.
- std::unique_ptr<xml::XmlResource> xmlToFlatten;
+ // The XML to process and flatten.
+ std::unique_ptr<xml::XmlResource> xml_to_flatten;
- // The destination to write this file to.
- std::string dstPath;
- bool skipVersion = false;
- };
+ // The destination to write this file to.
+ std::string dst_path;
+ bool skip_version = false;
+ };
- uint32_t getCompressionFlags(const StringPiece& str);
+ uint32_t GetCompressionFlags(const StringPiece& str);
- bool linkAndVersionXmlFile(ResourceTable* table, FileOperation* fileOp,
- std::queue<FileOperation>* outFileOpQueue);
+ bool LinkAndVersionXmlFile(ResourceTable* table, FileOperation* file_op,
+ std::queue<FileOperation>* out_file_op_queue);
- ResourceFileFlattenerOptions mOptions;
- IAaptContext* mContext;
- proguard::KeepSet* mKeepSet;
+ ResourceFileFlattenerOptions options_;
+ IAaptContext* context_;
+ proguard::KeepSet* keep_set_;
};
-uint32_t ResourceFileFlattener::getCompressionFlags(const StringPiece& str) {
- if (mOptions.doNotCompressAnything) {
- return 0;
- }
+uint32_t ResourceFileFlattener::GetCompressionFlags(const StringPiece& str) {
+ if (options_.do_not_compress_anything) {
+ return 0;
+ }
- for (const std::string& extension : mOptions.extensionsToNotCompress) {
- if (util::stringEndsWith(str, extension)) {
- return 0;
- }
+ for (const std::string& extension : options_.extensions_to_not_compress) {
+ if (util::EndsWith(str, extension)) {
+ return 0;
}
- return ArchiveEntry::kCompress;
+ }
+ return ArchiveEntry::kCompress;
}
-bool ResourceFileFlattener::linkAndVersionXmlFile(ResourceTable* table,
- FileOperation* fileOp,
- std::queue<FileOperation>* outFileOpQueue) {
- xml::XmlResource* doc = fileOp->xmlToFlatten.get();
- const Source& src = doc->file.source;
+bool ResourceFileFlattener::LinkAndVersionXmlFile(
+ ResourceTable* table, FileOperation* file_op,
+ std::queue<FileOperation>* out_file_op_queue) {
+ xml::XmlResource* doc = file_op->xml_to_flatten.get();
+ const Source& src = doc->file.source;
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage() << "linking " << src.path);
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(DiagMessage() << "linking " << src.path);
+ }
+
+ XmlReferenceLinker xml_linker;
+ if (!xml_linker.Consume(context_, doc)) {
+ return false;
+ }
+
+ if (options_.update_proguard_spec &&
+ !proguard::CollectProguardRules(src, doc, keep_set_)) {
+ return false;
+ }
+
+ if (options_.no_xml_namespaces) {
+ XmlNamespaceRemover namespace_remover;
+ if (!namespace_remover.Consume(context_, doc)) {
+ return false;
}
+ }
- XmlReferenceLinker xmlLinker;
- if (!xmlLinker.consume(mContext, doc)) {
- return false;
- }
-
- if (mOptions.updateProguardSpec && !proguard::collectProguardRules(src, doc, mKeepSet)) {
- return false;
- }
-
- if (mOptions.noXmlNamespaces) {
- XmlNamespaceRemover namespaceRemover;
- if (!namespaceRemover.consume(mContext, doc)) {
- return false;
+ if (!options_.no_auto_version) {
+ if (options_.no_version_vectors) {
+ // Skip this if it is a vector or animated-vector.
+ xml::Element* el = xml::FindRootElement(doc);
+ if (el && el->namespace_uri.empty()) {
+ if (el->name == "vector" || el->name == "animated-vector") {
+ // We are NOT going to version this file.
+ file_op->skip_version = true;
+ return true;
}
+ }
}
- if (!mOptions.noAutoVersion) {
- if (mOptions.noVersionVectors) {
- // Skip this if it is a vector or animated-vector.
- xml::Element* el = xml::findRootElement(doc);
- if (el && el->namespaceUri.empty()) {
- if (el->name == "vector" || el->name == "animated-vector") {
- // We are NOT going to version this file.
- fileOp->skipVersion = true;
- return true;
- }
- }
+ const ConfigDescription& config = file_op->config;
+
+ // Find the first SDK level used that is higher than this defined config and
+ // not superseded by a lower or equal SDK level resource.
+ const int min_sdk_version = context_->GetMinSdkVersion();
+ for (int sdk_level : xml_linker.sdk_levels()) {
+ if (sdk_level > min_sdk_version && sdk_level > config.sdkVersion) {
+ if (!ShouldGenerateVersionedResource(file_op->entry, config,
+ sdk_level)) {
+ // If we shouldn't generate a versioned resource, stop checking.
+ break;
}
- const ConfigDescription& config = fileOp->config;
+ ResourceFile versioned_file_desc = doc->file;
+ versioned_file_desc.config.sdkVersion = (uint16_t)sdk_level;
- // Find the first SDK level used that is higher than this defined config and
- // not superseded by a lower or equal SDK level resource.
- const int minSdkVersion = mContext->getMinSdkVersion();
- for (int sdkLevel : xmlLinker.getSdkLevels()) {
- if (sdkLevel > minSdkVersion && sdkLevel > config.sdkVersion) {
- if (!shouldGenerateVersionedResource(fileOp->entry, config, sdkLevel)) {
- // If we shouldn't generate a versioned resource, stop checking.
- break;
- }
+ FileOperation new_file_op;
+ new_file_op.xml_to_flatten = util::make_unique<xml::XmlResource>(
+ versioned_file_desc, doc->root->Clone());
+ new_file_op.config = versioned_file_desc.config;
+ new_file_op.entry = file_op->entry;
+ new_file_op.dst_path = ResourceUtils::BuildResourceFileName(
+ versioned_file_desc, context_->GetNameMangler());
- ResourceFile versionedFileDesc = doc->file;
- versionedFileDesc.config.sdkVersion = (uint16_t) sdkLevel;
-
- FileOperation newFileOp;
- newFileOp.xmlToFlatten = util::make_unique<xml::XmlResource>(
- versionedFileDesc, doc->root->clone());
- newFileOp.config = versionedFileDesc.config;
- newFileOp.entry = fileOp->entry;
- newFileOp.dstPath = ResourceUtils::buildResourceFileName(
- versionedFileDesc, mContext->getNameMangler());
-
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage(versionedFileDesc.source)
- << "auto-versioning resource from config '"
- << config
- << "' -> '"
- << versionedFileDesc.config << "'");
- }
-
- bool added = table->addFileReferenceAllowMangled(versionedFileDesc.name,
- versionedFileDesc.config,
- versionedFileDesc.source,
- newFileOp.dstPath,
- nullptr,
- mContext->getDiagnostics());
- if (!added) {
- return false;
- }
-
- outFileOpQueue->push(std::move(newFileOp));
- break;
- }
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(
+ DiagMessage(versioned_file_desc.source)
+ << "auto-versioning resource from config '" << config << "' -> '"
+ << versioned_file_desc.config << "'");
}
+
+ bool added = table->AddFileReferenceAllowMangled(
+ versioned_file_desc.name, versioned_file_desc.config,
+ versioned_file_desc.source, new_file_op.dst_path, nullptr,
+ context_->GetDiagnostics());
+ if (!added) {
+ return false;
+ }
+
+ out_file_op_queue->push(std::move(new_file_op));
+ break;
+ }
}
- return true;
+ }
+ return true;
}
/**
- * Do not insert or remove any resources while executing in this function. It will
+ * Do not insert or remove any resources while executing in this function. It
+ * will
* corrupt the iteration order.
*/
-bool ResourceFileFlattener::flatten(ResourceTable* table, IArchiveWriter* archiveWriter) {
- bool error = false;
- std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> configSortedFiles;
+bool ResourceFileFlattener::Flatten(ResourceTable* table,
+ IArchiveWriter* archive_writer) {
+ bool error = false;
+ std::map<std::pair<ConfigDescription, StringPiece>, FileOperation>
+ config_sorted_files;
- for (auto& pkg : table->packages) {
- for (auto& type : pkg->types) {
- // Sort by config and name, so that we get better locality in the zip file.
- configSortedFiles.clear();
- std::queue<FileOperation> fileOperations;
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ // Sort by config and name, so that we get better locality in the zip
+ // file.
+ config_sorted_files.clear();
+ std::queue<FileOperation> file_operations;
- // Populate the queue with all files in the ResourceTable.
- for (auto& entry : type->entries) {
- for (auto& configValue : entry->values) {
- FileReference* fileRef = valueCast<FileReference>(configValue->value.get());
- if (!fileRef) {
- continue;
- }
-
- io::IFile* file = fileRef->file;
- if (!file) {
- mContext->getDiagnostics()->error(DiagMessage(fileRef->getSource())
- << "file not found");
- return false;
- }
-
- FileOperation fileOp;
- fileOp.entry = entry.get();
- fileOp.dstPath = *fileRef->path;
- fileOp.config = configValue->config;
-
- const StringPiece srcPath = file->getSource().path;
- if (type->type != ResourceType::kRaw &&
- (util::stringEndsWith(srcPath, ".xml.flat") ||
- util::stringEndsWith(srcPath, ".xml"))) {
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- mContext->getDiagnostics()->error(DiagMessage(file->getSource())
- << "failed to open file");
- return false;
- }
-
- fileOp.xmlToFlatten = xml::inflate(data->data(), data->size(),
- mContext->getDiagnostics(),
- file->getSource());
-
- if (!fileOp.xmlToFlatten) {
- return false;
- }
-
- fileOp.xmlToFlatten->file.config = configValue->config;
- fileOp.xmlToFlatten->file.source = fileRef->getSource();
- fileOp.xmlToFlatten->file.name =
- ResourceName(pkg->name, type->type, entry->name);
-
- // Enqueue the XML files to be processed.
- fileOperations.push(std::move(fileOp));
- } else {
- fileOp.fileToCopy = file;
-
- // NOTE(adamlesinski): Explicitly construct a StringPiece here, or else
- // we end up copying the string in the std::make_pair() method, then
- // creating a StringPiece from the copy, which would cause us to end up
- // referencing garbage in the map.
- const StringPiece entryName(entry->name);
- configSortedFiles[std::make_pair(configValue->config, entryName)] =
- std::move(fileOp);
- }
- }
- }
-
- // Now process the XML queue
- for (; !fileOperations.empty(); fileOperations.pop()) {
- FileOperation& fileOp = fileOperations.front();
-
- if (!linkAndVersionXmlFile(table, &fileOp, &fileOperations)) {
- error = true;
- continue;
- }
-
- // NOTE(adamlesinski): Explicitly construct a StringPiece here, or else
- // we end up copying the string in the std::make_pair() method, then creating
- // a StringPiece from the copy, which would cause us to end up referencing
- // garbage in the map.
- const StringPiece entryName(fileOp.entry->name);
- configSortedFiles[std::make_pair(fileOp.config, entryName)] = std::move(fileOp);
- }
-
- if (error) {
- return false;
- }
-
- // Now flatten the sorted values.
- for (auto& mapEntry : configSortedFiles) {
- const ConfigDescription& config = mapEntry.first.first;
- const FileOperation& fileOp = mapEntry.second;
-
- if (fileOp.xmlToFlatten) {
- Maybe<size_t> maxSdkLevel;
- if (!mOptions.noAutoVersion && !fileOp.skipVersion) {
- maxSdkLevel = std::max<size_t>(std::max<size_t>(config.sdkVersion, 1u),
- mContext->getMinSdkVersion());
- }
-
- bool result = flattenXml(fileOp.xmlToFlatten.get(), fileOp.dstPath, maxSdkLevel,
- mOptions.keepRawValues,
- archiveWriter, mContext);
- if (!result) {
- error = true;
- }
- } else {
- bool result = copyFileToArchive(fileOp.fileToCopy, fileOp.dstPath,
- getCompressionFlags(fileOp.dstPath),
- archiveWriter, mContext);
- if (!result) {
- error = true;
- }
- }
- }
- }
- }
- return !error;
-}
-
-static bool writeStableIdMapToPath(IDiagnostics* diag,
- const std::unordered_map<ResourceName, ResourceId>& idMap,
- const std::string& idMapPath) {
- std::ofstream fout(idMapPath, std::ofstream::binary);
- if (!fout) {
- diag->error(DiagMessage(idMapPath) << strerror(errno));
- return false;
- }
-
- for (const auto& entry : idMap) {
- const ResourceName& name = entry.first;
- const ResourceId& id = entry.second;
- fout << name << " = " << id << "\n";
- }
-
- if (!fout) {
- diag->error(DiagMessage(idMapPath) << "failed writing to file: " << strerror(errno));
- return false;
- }
-
- return true;
-}
-
-static bool loadStableIdMap(IDiagnostics* diag, const std::string& path,
- std::unordered_map<ResourceName, ResourceId>* outIdMap) {
- std::string content;
- if (!android::base::ReadFileToString(path, &content)) {
- diag->error(DiagMessage(path) << "failed reading stable ID file");
- return false;
- }
-
- outIdMap->clear();
- size_t lineNo = 0;
- for (StringPiece line : util::tokenize(content, '\n')) {
- lineNo++;
- line = util::trimWhitespace(line);
- if (line.empty()) {
+ // Populate the queue with all files in the ResourceTable.
+ for (auto& entry : type->entries) {
+ for (auto& config_value : entry->values) {
+ FileReference* file_ref =
+ ValueCast<FileReference>(config_value->value.get());
+ if (!file_ref) {
continue;
- }
+ }
- auto iter = std::find(line.begin(), line.end(), '=');
- if (iter == line.end()) {
- diag->error(DiagMessage(Source(path, lineNo)) << "missing '='");
+ io::IFile* file = file_ref->file;
+ if (!file) {
+ context_->GetDiagnostics()->Error(DiagMessage(file_ref->GetSource())
+ << "file not found");
return false;
+ }
+
+ FileOperation file_op;
+ file_op.entry = entry.get();
+ file_op.dst_path = *file_ref->path;
+ file_op.config = config_value->config;
+
+ const StringPiece src_path = file->GetSource().path;
+ if (type->type != ResourceType::kRaw &&
+ (util::EndsWith(src_path, ".xml.flat") ||
+ util::EndsWith(src_path, ".xml"))) {
+ std::unique_ptr<io::IData> data = file->OpenAsData();
+ if (!data) {
+ context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
+ << "failed to open file");
+ return false;
+ }
+
+ file_op.xml_to_flatten =
+ xml::Inflate(data->data(), data->size(),
+ context_->GetDiagnostics(), file->GetSource());
+
+ if (!file_op.xml_to_flatten) {
+ return false;
+ }
+
+ file_op.xml_to_flatten->file.config = config_value->config;
+ file_op.xml_to_flatten->file.source = file_ref->GetSource();
+ file_op.xml_to_flatten->file.name =
+ ResourceName(pkg->name, type->type, entry->name);
+
+ // Enqueue the XML files to be processed.
+ file_operations.push(std::move(file_op));
+ } else {
+ file_op.file_to_copy = file;
+
+ // NOTE(adamlesinski): Explicitly construct a StringPiece here, or
+ // else we end up copying the string in the std::make_pair() method,
+ // then creating a StringPiece from the copy, which would cause us
+ // to end up referencing garbage in the map.
+ const StringPiece entry_name(entry->name);
+ config_sorted_files[std::make_pair(
+ config_value->config, entry_name)] = std::move(file_op);
+ }
+ }
+ }
+
+ // Now process the XML queue
+ for (; !file_operations.empty(); file_operations.pop()) {
+ FileOperation& file_op = file_operations.front();
+
+ if (!LinkAndVersionXmlFile(table, &file_op, &file_operations)) {
+ error = true;
+ continue;
}
- ResourceNameRef name;
- StringPiece resNameStr = util::trimWhitespace(
- line.substr(0, std::distance(line.begin(), iter)));
- if (!ResourceUtils::parseResourceName(resNameStr, &name)) {
- diag->error(DiagMessage(Source(path, lineNo))
- << "invalid resource name '" << resNameStr << "'");
- return false;
+ // NOTE(adamlesinski): Explicitly construct a StringPiece here, or else
+ // we end up copying the string in the std::make_pair() method, then
+ // creating a StringPiece from the copy, which would cause us to end up
+ // referencing garbage in the map.
+ const StringPiece entry_name(file_op.entry->name);
+ config_sorted_files[std::make_pair(file_op.config, entry_name)] =
+ std::move(file_op);
+ }
+
+ if (error) {
+ return false;
+ }
+
+ // Now flatten the sorted values.
+ for (auto& map_entry : config_sorted_files) {
+ const ConfigDescription& config = map_entry.first.first;
+ const FileOperation& file_op = map_entry.second;
+
+ if (file_op.xml_to_flatten) {
+ Maybe<size_t> max_sdk_level;
+ if (!options_.no_auto_version && !file_op.skip_version) {
+ max_sdk_level =
+ std::max<size_t>(std::max<size_t>(config.sdkVersion, 1u),
+ context_->GetMinSdkVersion());
+ }
+
+ bool result = FlattenXml(
+ file_op.xml_to_flatten.get(), file_op.dst_path, max_sdk_level,
+ options_.keep_raw_values, archive_writer, context_);
+ if (!result) {
+ error = true;
+ }
+ } else {
+ bool result = CopyFileToArchive(
+ file_op.file_to_copy, file_op.dst_path,
+ GetCompressionFlags(file_op.dst_path), archive_writer, context_);
+ if (!result) {
+ error = true;
+ }
}
-
- const size_t resIdStartIdx = std::distance(line.begin(), iter) + 1;
- const size_t resIdStrLen = line.size() - resIdStartIdx;
- StringPiece resIdStr = util::trimWhitespace(line.substr(resIdStartIdx, resIdStrLen));
-
- Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(resIdStr);
- if (!maybeId) {
- diag->error(DiagMessage(Source(path, lineNo)) << "invalid resource ID '"
- << resIdStr << "'");
- return false;
- }
-
- (*outIdMap)[name.toResourceName()] = maybeId.value();
+ }
}
- return true;
+ }
+ return !error;
}
-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;
+static bool WriteStableIdMapToPath(
+ IDiagnostics* diag,
+ const std::unordered_map<ResourceName, ResourceId>& id_map,
+ const std::string& id_map_path) {
+ std::ofstream fout(id_map_path, std::ofstream::binary);
+ if (!fout) {
+ diag->Error(DiagMessage(id_map_path) << strerror(errno));
+ return false;
+ }
+
+ for (const auto& entry : id_map) {
+ const ResourceName& name = entry.first;
+ const ResourceId& id = entry.second;
+ fout << name << " = " << id << "\n";
+ }
+
+ if (!fout) {
+ diag->Error(DiagMessage(id_map_path)
+ << "failed writing to file: "
+ << android::base::SystemErrorCodeToString(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static bool LoadStableIdMap(
+ IDiagnostics* diag, const std::string& path,
+ std::unordered_map<ResourceName, ResourceId>* out_id_map) {
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content)) {
+ diag->Error(DiagMessage(path) << "failed reading stable ID file");
+ return false;
+ }
+
+ out_id_map->clear();
+ size_t line_no = 0;
+ for (StringPiece line : util::Tokenize(content, '\n')) {
+ line_no++;
+ line = util::TrimWhitespace(line);
+ if (line.empty()) {
+ continue;
}
- *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;
- }
+
+ auto iter = std::find(line.begin(), line.end(), '=');
+ if (iter == line.end()) {
+ diag->Error(DiagMessage(Source(path, line_no)) << "missing '='");
+ return false;
}
- outSplit->configs.insert(configs.begin(), configs.end());
- return true;
+
+ ResourceNameRef name;
+ StringPiece res_name_str =
+ util::TrimWhitespace(line.substr(0, std::distance(line.begin(), iter)));
+ if (!ResourceUtils::ParseResourceName(res_name_str, &name)) {
+ diag->Error(DiagMessage(Source(path, line_no))
+ << "invalid resource name '" << res_name_str << "'");
+ return false;
+ }
+
+ const size_t res_id_start_idx = std::distance(line.begin(), iter) + 1;
+ const size_t res_id_str_len = line.size() - res_id_start_idx;
+ StringPiece res_id_str =
+ util::TrimWhitespace(line.substr(res_id_start_idx, res_id_str_len));
+
+ Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(res_id_str);
+ if (!maybe_id) {
+ diag->Error(DiagMessage(Source(path, line_no)) << "invalid resource ID '"
+ << res_id_str << "'");
+ return false;
+ }
+
+ (*out_id_map)[name.ToResourceName()] = maybe_id.value();
+ }
+ return true;
+}
+
+static bool ParseSplitParameter(const StringPiece& arg, IDiagnostics* diag,
+ std::string* out_path,
+ SplitConstraints* out_split) {
+ 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;
+ }
+ *out_path = parts[0];
+ std::vector<ConfigDescription> configs;
+ for (const StringPiece& config_str : util::Tokenize(parts[1], ',')) {
+ configs.push_back({});
+ if (!ConfigDescription::Parse(config_str, &configs.back())) {
+ diag->Error(DiagMessage() << "invalid config '" << config_str
+ << "' in split parameter '" << arg << "'");
+ return false;
+ }
+ }
+ out_split->configs.insert(configs.begin(), configs.end());
+ return true;
}
class LinkCommand {
-public:
- LinkCommand(LinkContext* context, const LinkOptions& options) :
- mOptions(options), mContext(context), mFinalTable(),
- mFileCollection(util::make_unique<io::FileCollection>()) {
- }
+ public:
+ LinkCommand(LinkContext* context, const LinkOptions& options)
+ : options_(options),
+ context_(context),
+ final_table_(),
+ file_collection_(util::make_unique<io::FileCollection>()) {}
- /**
- * Creates a SymbolTable that loads symbols from the various APKs and caches the
- * results for faster lookup.
- */
- bool loadSymbolsFromIncludePaths() {
- std::unique_ptr<AssetManagerSymbolSource> assetSource =
- util::make_unique<AssetManagerSymbolSource>();
- for (const std::string& path : mOptions.includePaths) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path");
- }
+ /**
+ * Creates a SymbolTable that loads symbols from the various APKs and caches
+ * the results for faster lookup.
+ */
+ bool LoadSymbolsFromIncludePaths() {
+ std::unique_ptr<AssetManagerSymbolSource> asset_source =
+ util::make_unique<AssetManagerSymbolSource>();
+ for (const std::string& path : options_.include_paths) {
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(DiagMessage(path)
+ << "loading include path");
+ }
- // First try to load the file as a static lib.
- std::string errorStr;
- std::unique_ptr<ResourceTable> staticInclude = loadStaticLibrary(path, &errorStr);
- if (staticInclude) {
- if (!mOptions.staticLib) {
- // Can't include static libraries when not building a static library.
- mContext->getDiagnostics()->error(
- DiagMessage(path) << "can't include static library when building app");
- return false;
- }
-
- // If we are using --no-static-lib-packages, we need to rename the package of this
- // table to our compilation package.
- if (mOptions.noStaticLibPackages) {
- if (ResourceTablePackage* pkg = staticInclude->findPackageById(0x7f)) {
- pkg->name = mContext->getCompilationPackage();
- }
- }
-
- mContext->getExternalSymbols()->appendSource(
- util::make_unique<ResourceTableSymbolSource>(staticInclude.get()));
-
- mStaticTableIncludes.push_back(std::move(staticInclude));
-
- } else if (!errorStr.empty()) {
- // We had an error with reading, so fail.
- mContext->getDiagnostics()->error(DiagMessage(path) << errorStr);
- return false;
- }
-
- if (!assetSource->addAssetPath(path)) {
- mContext->getDiagnostics()->error(
- DiagMessage(path) << "failed to load include path");
- return false;
- }
+ // First try to load the file as a static lib.
+ std::string error_str;
+ std::unique_ptr<ResourceTable> static_include =
+ LoadStaticLibrary(path, &error_str);
+ if (static_include) {
+ if (!options_.static_lib) {
+ // Can't include static libraries when not building a static library.
+ context_->GetDiagnostics()->Error(
+ DiagMessage(path)
+ << "can't include static library when building app");
+ return false;
}
- mContext->getExternalSymbols()->appendSource(std::move(assetSource));
- return true;
- }
-
- Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes, IDiagnostics* diag) {
- // Make sure the first element is <manifest> with package attribute.
- if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
- AppInfo appInfo;
-
- if (!manifestEl->namespaceUri.empty() || manifestEl->name != "manifest") {
- diag->error(DiagMessage(xmlRes->file.source) << "root tag must be <manifest>");
- return {};
- }
-
- xml::Attribute* packageAttr = manifestEl->findAttribute({}, "package");
- if (!packageAttr) {
- diag->error(DiagMessage(xmlRes->file.source)
- << "<manifest> must have a 'package' attribute");
- return {};
- }
-
- 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")) {
- appInfo.minSdkVersion = minSdk->value;
- }
- }
-
- return appInfo;
- }
- return {};
- }
-
- /**
- * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
- * Postcondition: ResourceTable has only one package left. All others are stripped, or there
- * is an error and false is returned.
- */
- bool verifyNoExternalPackages() {
- auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
- return mContext->getCompilationPackage() != pkg->name ||
- !pkg->id ||
- pkg->id.value() != mContext->getPackageId();
- };
-
- bool error = false;
- for (const auto& package : mFinalTable.packages) {
- if (isExtPackageFunc(package)) {
- // We have a package that is not related to the one we're building!
- for (const auto& type : package->types) {
- for (const auto& entry : type->entries) {
- ResourceNameRef resName(package->name, type->type, entry->name);
-
- for (const auto& configValue : entry->values) {
- // Special case the occurrence of an ID that is being generated for the
- // 'android' package. This is due to legacy reasons.
- if (valueCast<Id>(configValue->value.get()) &&
- package->name == "android") {
- mContext->getDiagnostics()->warn(
- DiagMessage(configValue->value->getSource())
- << "generated id '" << resName
- << "' for external package '" << package->name
- << "'");
- } else {
- mContext->getDiagnostics()->error(
- DiagMessage(configValue->value->getSource())
- << "defined resource '" << resName
- << "' for external package '" << package->name
- << "'");
- error = true;
- }
- }
- }
- }
- }
+ // If we are using --no-static-lib-packages, we need to rename the
+ // package of this
+ // table to our compilation package.
+ if (options_.no_static_lib_packages) {
+ if (ResourceTablePackage* pkg =
+ static_include->FindPackageById(0x7f)) {
+ pkg->name = context_->GetCompilationPackage();
+ }
}
- auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
- isExtPackageFunc);
- mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
- return !error;
- }
+ context_->GetExternalSymbols()->AppendSource(
+ util::make_unique<ResourceTableSymbolSource>(static_include.get()));
- /**
- * Returns true if no IDs have been set, false otherwise.
- */
- bool verifyNoIdsSet() {
- for (const auto& package : mFinalTable.packages) {
- for (const auto& type : package->types) {
- if (type->id) {
- mContext->getDiagnostics()->error(DiagMessage() << "type " << type->type
- << " has ID " << std::hex
- << (int) type->id.value()
- << std::dec << " assigned");
- return false;
- }
+ static_table_includes_.push_back(std::move(static_include));
- for (const auto& entry : type->entries) {
- if (entry->id) {
- ResourceNameRef resName(package->name, type->type, entry->name);
- mContext->getDiagnostics()->error(DiagMessage() << "entry " << resName
- << " has ID " << std::hex
- << (int) entry->id.value()
- << std::dec << " assigned");
- return false;
- }
- }
- }
- }
- return true;
- }
-
- std::unique_ptr<IArchiveWriter> makeArchiveWriter(const StringPiece& out) {
- if (mOptions.outputToDirectory) {
- return createDirectoryArchiveWriter(mContext->getDiagnostics(), out);
- } else {
- return createZipFileArchiveWriter(mContext->getDiagnostics(), out);
- }
- }
-
- bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
- BigBuffer buffer(1024);
- TableFlattener flattener(&buffer);
- if (!flattener.consume(mContext, table)) {
- return false;
- }
-
- if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
- if (writer->writeEntry(buffer)) {
- if (writer->finishEntry()) {
- return true;
- }
- }
- }
-
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed to write resources.arsc to archive");
+ } else if (!error_str.empty()) {
+ // We had an error with reading, so fail.
+ context_->GetDiagnostics()->Error(DiagMessage(path) << error_str);
return false;
+ }
+
+ if (!asset_source->AddAssetPath(path)) {
+ context_->GetDiagnostics()->Error(DiagMessage(path)
+ << "failed to load include path");
+ return false;
+ }
}
- bool flattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
- // Create the file/zip entry.
- if (!writer->startEntry("resources.arsc.flat", 0)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed to open");
- return false;
- }
+ context_->GetExternalSymbols()->AppendSource(std::move(asset_source));
+ return true;
+ }
- // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->finishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
- // interface.
- CopyingOutputStreamAdaptor adaptor(writer);
+ Maybe<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res,
+ IDiagnostics* diag) {
+ // Make sure the first element is <manifest> with package attribute.
+ if (xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get())) {
+ AppInfo app_info;
- std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table);
- if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed to write");
- return false;
- }
- }
+ if (!manifest_el->namespace_uri.empty() ||
+ manifest_el->name != "manifest") {
+ diag->Error(DiagMessage(xml_res->file.source)
+ << "root tag must be <manifest>");
+ return {};
+ }
- if (!writer->finishEntry()) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed to finish entry");
- return false;
+ xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package");
+ if (!package_attr) {
+ diag->Error(DiagMessage(xml_res->file.source)
+ << "<manifest> must have a 'package' attribute");
+ return {};
+ }
+
+ app_info.package = package_attr->value;
+
+ if (xml::Attribute* version_code_attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
+ Maybe<uint32_t> maybe_code =
+ ResourceUtils::ParseInt(version_code_attr->value);
+ if (!maybe_code) {
+ diag->Error(DiagMessage(xml_res->file.source.WithLine(
+ manifest_el->line_number))
+ << "invalid android:versionCode '"
+ << version_code_attr->value << "'");
+ return {};
}
- return true;
+ app_info.version_code = maybe_code.value();
+ }
+
+ if (xml::Attribute* revision_code_attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
+ Maybe<uint32_t> maybe_code =
+ ResourceUtils::ParseInt(revision_code_attr->value);
+ if (!maybe_code) {
+ diag->Error(DiagMessage(xml_res->file.source.WithLine(
+ manifest_el->line_number))
+ << "invalid android:revisionCode '"
+ << revision_code_attr->value << "'");
+ return {};
+ }
+ app_info.revision_code = maybe_code.value();
+ }
+
+ if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
+ if (xml::Attribute* min_sdk = uses_sdk_el->FindAttribute(
+ xml::kSchemaAndroid, "minSdkVersion")) {
+ app_info.min_sdk_version = min_sdk->value;
+ }
+ }
+ return app_info;
}
+ return {};
+ }
- bool writeJavaFile(ResourceTable* table, const StringPiece& packageNameToGenerate,
- const StringPiece& outPackage, const JavaClassGeneratorOptions& javaOptions) {
- if (!mOptions.generateJavaClassPath) {
- return true;
- }
+ /**
+ * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it
+ * linked.
+ * Postcondition: ResourceTable has only one package left. All others are
+ * stripped, or there is an error and false is returned.
+ */
+ bool VerifyNoExternalPackages() {
+ auto is_ext_package_func =
+ [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
+ return context_->GetCompilationPackage() != pkg->name || !pkg->id ||
+ pkg->id.value() != context_->GetPackageId();
+ };
- std::string outPath = mOptions.generateJavaClassPath.value();
- file::appendPath(&outPath, file::packageToPath(outPackage));
- if (!file::mkdirs(outPath)) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed to create directory '" << outPath << "'");
- return false;
- }
+ bool error = false;
+ for (const auto& package : final_table_.packages) {
+ if (is_ext_package_func(package)) {
+ // We have a package that is not related to the one we're building!
+ for (const auto& type : package->types) {
+ for (const auto& entry : type->entries) {
+ ResourceNameRef res_name(package->name, type->type, entry->name);
- file::appendPath(&outPath, "R.java");
-
- std::ofstream fout(outPath, std::ofstream::binary);
- if (!fout) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
- return false;
- }
-
- JavaClassGenerator generator(mContext, table, javaOptions);
- if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
- mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
- return false;
- }
-
- if (!fout) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
- }
- return true;
- }
-
- bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
- if (!mOptions.generateJavaClassPath) {
- return true;
- }
-
- std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
- mContext->getDiagnostics(), manifestXml);
-
- if (!manifestClass) {
- // Something bad happened, but we already logged it, so exit.
- return false;
- }
-
- if (manifestClass->empty()) {
- // Empty Manifest class, no need to generate it.
- return true;
- }
-
- // Add any JavaDoc annotations to the generated class.
- for (const std::string& annotation : mOptions.javadocAnnotations) {
- std::string properAnnotation = "@";
- properAnnotation += annotation;
- manifestClass->getCommentBuilder()->appendComment(properAnnotation);
- }
-
- const std::string& packageUtf8 = mContext->getCompilationPackage();
-
- std::string outPath = mOptions.generateJavaClassPath.value();
- file::appendPath(&outPath, file::packageToPath(packageUtf8));
-
- if (!file::mkdirs(outPath)) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed to create directory '" << outPath << "'");
- return false;
- }
-
- file::appendPath(&outPath, "Manifest.java");
-
- std::ofstream fout(outPath, std::ofstream::binary);
- if (!fout) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
- return false;
- }
-
- if (!ClassDefinition::writeJavaFile(manifestClass.get(), packageUtf8, true, &fout)) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
- return false;
- }
- return true;
- }
-
- bool writeProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keepSet) {
- if (!out) {
- return true;
- }
-
- const std::string& outPath = out.value();
- std::ofstream fout(outPath, std::ofstream::binary);
- if (!fout) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed to open '" << outPath << "': " << strerror(errno));
- return false;
- }
-
- proguard::writeKeepSet(&fout, keepSet);
- if (!fout) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
- return false;
- }
- return true;
- }
-
- std::unique_ptr<ResourceTable> loadStaticLibrary(const std::string& input,
- std::string* outError) {
- std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
- input, outError);
- if (!collection) {
- return {};
- }
- return loadTablePbFromCollection(collection.get());
- }
-
- std::unique_ptr<ResourceTable> loadTablePbFromCollection(io::IFileCollection* collection) {
- io::IFile* file = collection->findFile("resources.arsc.flat");
- if (!file) {
- return {};
- }
-
- std::unique_ptr<io::IData> data = file->openAsData();
- return loadTableFromPb(file->getSource(), data->data(), data->size(),
- mContext->getDiagnostics());
- }
-
- bool mergeStaticLibrary(const std::string& input, bool override) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage() << "merging static library " << input);
- }
-
- std::string errorStr;
- std::unique_ptr<io::ZipFileCollection> collection =
- io::ZipFileCollection::create(input, &errorStr);
- if (!collection) {
- mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
- return false;
- }
-
- std::unique_ptr<ResourceTable> table = loadTablePbFromCollection(collection.get());
- if (!table) {
- mContext->getDiagnostics()->error(DiagMessage(input) << "invalid static library");
- return false;
- }
-
- ResourceTablePackage* pkg = table->findPackageById(0x7f);
- if (!pkg) {
- mContext->getDiagnostics()->error(DiagMessage(input)
- << "static library has no package");
- return false;
- }
-
- bool result;
- if (mOptions.noStaticLibPackages) {
- // Merge all resources as if they were in the compilation package. This is the old
- // behaviour of aapt.
-
- // Add the package to the set of --extra-packages so we emit an R.java for each
- // library package.
- if (!pkg->name.empty()) {
- mOptions.extraJavaPackages.insert(pkg->name);
- }
-
- pkg->name = "";
- if (override) {
- result = mTableMerger->mergeOverlay(Source(input), table.get(), collection.get());
- } else {
- result = mTableMerger->merge(Source(input), table.get(), collection.get());
- }
-
- } else {
- // This is the proper way to merge libraries, where the package name is preserved
- // and resource names are mangled.
- result = mTableMerger->mergeAndMangle(Source(input), pkg->name, table.get(),
- collection.get());
- }
-
- if (!result) {
- return false;
- }
-
- // Make sure to move the collection into the set of IFileCollections.
- mCollections.push_back(std::move(collection));
- return true;
- }
-
- bool mergeResourceTable(io::IFile* file, bool override) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage() << "merging resource table "
- << file->getSource());
- }
-
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- mContext->getDiagnostics()->error(DiagMessage(file->getSource())
- << "failed to open file");
- return false;
- }
-
- std::unique_ptr<ResourceTable> table = loadTableFromPb(file->getSource(),
- data->data(), data->size(),
- mContext->getDiagnostics());
- if (!table) {
- return false;
- }
-
- bool result = false;
- if (override) {
- result = mTableMerger->mergeOverlay(file->getSource(), table.get());
- } else {
- result = mTableMerger->merge(file->getSource(), table.get());
- }
- return result;
- }
-
- bool mergeCompiledFile(io::IFile* file, ResourceFile* fileDesc, bool override) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage()
- << "merging '" << fileDesc->name
- << "' from compiled file "
- << file->getSource());
- }
-
- bool result = false;
- if (override) {
- result = mTableMerger->mergeFileOverlay(*fileDesc, file);
- } else {
- result = mTableMerger->mergeFile(*fileDesc, file);
- }
-
- if (!result) {
- return false;
- }
-
- // Add the exports of this file to the table.
- for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
- if (exportedSymbol.name.package.empty()) {
- exportedSymbol.name.package = mContext->getCompilationPackage();
- }
-
- ResourceNameRef resName = exportedSymbol.name;
-
- Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
- exportedSymbol.name);
- if (mangledName) {
- resName = mangledName.value();
- }
-
- std::unique_ptr<Id> id = util::make_unique<Id>();
- id->setSource(fileDesc->source.withLine(exportedSymbol.line));
- bool result = mFinalTable.addResourceAllowMangled(
- resName, ConfigDescription::defaultConfig(), std::string(), std::move(id),
- mContext->getDiagnostics());
- if (!result) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
- * If override is true, conflicting resources are allowed to override each other, in order of
- * last seen.
- *
- * An io::IFileCollection is created from the ZIP file and added to the set of
- * io::IFileCollections that are open.
- */
- bool mergeArchive(const std::string& input, bool override) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage() << "merging archive " << input);
- }
-
- std::string errorStr;
- std::unique_ptr<io::ZipFileCollection> collection =
- io::ZipFileCollection::create(input, &errorStr);
- if (!collection) {
- mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
- return false;
- }
-
- bool error = false;
- for (auto iter = collection->iterator(); iter->hasNext(); ) {
- if (!mergeFile(iter->next(), override)) {
+ for (const auto& config_value : entry->values) {
+ // Special case the occurrence of an ID that is being generated
+ // for the 'android' package. This is due to legacy reasons.
+ if (ValueCast<Id>(config_value->value.get()) &&
+ package->name == "android") {
+ context_->GetDiagnostics()->Warn(
+ DiagMessage(config_value->value->GetSource())
+ << "generated id '" << res_name
+ << "' for external package '" << package->name << "'");
+ } else {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(config_value->value->GetSource())
+ << "defined resource '" << res_name
+ << "' for external package '" << package->name << "'");
error = true;
+ }
}
+ }
}
-
- // Make sure to move the collection into the set of IFileCollections.
- mCollections.push_back(std::move(collection));
- return !error;
+ }
}
- /**
- * Takes a path to load and merge into the master ResourceTable. If override is true,
- * conflicting resources are allowed to override each other, in order of last seen.
- *
- * If the file path ends with .flata, .jar, .jack, or .zip the file is treated as ZIP archive
- * and the files within are merged individually.
- *
- * Otherwise the files is processed on its own.
- */
- bool mergePath(const std::string& path, bool override) {
- if (util::stringEndsWith(path, ".flata") ||
- util::stringEndsWith(path, ".jar") ||
- util::stringEndsWith(path, ".jack") ||
- util::stringEndsWith(path, ".zip")) {
- return mergeArchive(path, override);
- } else if (util::stringEndsWith(path, ".apk")) {
- return mergeStaticLibrary(path, override);
+ auto new_end_iter =
+ std::remove_if(final_table_.packages.begin(),
+ final_table_.packages.end(), is_ext_package_func);
+ final_table_.packages.erase(new_end_iter, final_table_.packages.end());
+ return !error;
+ }
+
+ /**
+ * Returns true if no IDs have been set, false otherwise.
+ */
+ bool VerifyNoIdsSet() {
+ for (const auto& package : final_table_.packages) {
+ for (const auto& type : package->types) {
+ if (type->id) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "type " << type->type << " has ID " << std::hex
+ << (int)type->id.value() << std::dec
+ << " assigned");
+ return false;
}
- io::IFile* file = mFileCollection->insertFile(path);
- return mergeFile(file, override);
- }
-
- /**
- * Takes a file to load and merge into the master ResourceTable. If override is true,
- * conflicting resources are allowed to override each other, in order of last seen.
- *
- * If the file ends with .arsc.flat, then it is loaded as a ResourceTable and merged into the
- * master ResourceTable. If the file ends with .flat, then it is treated like a compiled file
- * and the header data is read and merged into the final ResourceTable.
- *
- * All other file types are ignored. This is because these files could be coming from a zip,
- * where we could have other files like classes.dex.
- */
- bool mergeFile(io::IFile* file, bool override) {
- const Source& src = file->getSource();
- if (util::stringEndsWith(src.path, ".arsc.flat")) {
- return mergeResourceTable(file, override);
-
- } else if (util::stringEndsWith(src.path, ".flat")){
- // Try opening the file and looking for an Export header.
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "failed to open");
- return false;
- }
-
- CompiledFileInputStream inputStream(data->data(), data->size());
- uint32_t numFiles = 0;
- if (!inputStream.ReadLittleEndian32(&numFiles)) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "failed read num files");
- return false;
- }
-
- for (uint32_t i = 0; i < numFiles; i++) {
- pb::CompiledFile compiledFile;
- if (!inputStream.ReadCompiledFile(&compiledFile)) {
- mContext->getDiagnostics()->error(DiagMessage(src)
- << "failed to read compiled file header");
- return false;
- }
-
- uint64_t offset, len;
- if (!inputStream.ReadDataMetaData(&offset, &len)) {
- mContext->getDiagnostics()->error(DiagMessage(src)
- << "failed to read data meta data");
- return false;
- }
-
- std::unique_ptr<ResourceFile> resourceFile = deserializeCompiledFileFromPb(
- compiledFile, file->getSource(), mContext->getDiagnostics());
- if (!resourceFile) {
- return false;
- }
-
- if (!mergeCompiledFile(file->createFileSegment(offset, len), resourceFile.get(),
- override)) {
- return false;
- }
- }
- return true;
- }
-
- // Ignore non .flat files. This could be classes.dex or something else that happens
- // to be in an archive.
- 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) {
+ for (const auto& entry : type->entries) {
+ if (entry->id) {
+ ResourceNameRef res_name(package->name, type->type, entry->name);
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "entry " << res_name << " has ID " << std::hex
+ << (int)entry->id.value() << std::dec
+ << " assigned");
return false;
+ }
}
+ }
+ }
+ return true;
+ }
- ResourceFileFlattenerOptions fileFlattenerOptions;
- fileFlattenerOptions.keepRawValues = keepRawValues;
- fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything;
- fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress;
- fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion;
- fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors;
- fileFlattenerOptions.noXmlNamespaces = mOptions.noXmlNamespaces;
- fileFlattenerOptions.updateProguardSpec =
- static_cast<bool>(mOptions.generateProguardRulesPath);
+ std::unique_ptr<IArchiveWriter> MakeArchiveWriter(const StringPiece& out) {
+ if (options_.output_to_directory) {
+ return CreateDirectoryArchiveWriter(context_->GetDiagnostics(), out);
+ } else {
+ return CreateZipFileArchiveWriter(context_->GetDiagnostics(), out);
+ }
+ }
- 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;
+ bool FlattenTable(ResourceTable* table, IArchiveWriter* writer) {
+ BigBuffer buffer(1024);
+ TableFlattener flattener(&buffer);
+ if (!flattener.Consume(context_, table)) {
+ return false;
}
- int run(const std::vector<std::string>& inputFiles) {
- // Load the AndroidManifest.xml
- std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
- mContext->getDiagnostics());
- if (!manifestXml) {
- return 1;
+ if (writer->StartEntry("resources.arsc", ArchiveEntry::kAlign)) {
+ if (writer->WriteEntry(buffer)) {
+ if (writer->FinishEntry()) {
+ return true;
}
-
- // First extract the Package name without modifying it (via --rename-manifest-package).
- if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(),
- mContext->getDiagnostics())) {
- const AppInfo& appInfo = maybeAppInfo.value();
- mContext->setCompilationPackage(appInfo.package);
- }
-
- ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
- if (!manifestFixer.consume(mContext, manifestXml.get())) {
- return 1;
- }
-
- Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(),
- mContext->getDiagnostics());
- if (!maybeAppInfo) {
- return 1;
- }
-
- 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 {
- mContext->setPackageId(0x7f);
- }
-
- if (!loadSymbolsFromIncludePaths()) {
- return 1;
- }
-
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
- mTableMerger = util::make_unique<TableMerger>(mContext, &mFinalTable, tableMergerOptions);
-
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(
- DiagMessage() << "linking package '" << mContext->getCompilationPackage()
- << "' with package ID " << std::hex
- << (int) mContext->getPackageId());
- }
-
-
- for (const std::string& input : inputFiles) {
- if (!mergePath(input, false)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input");
- return 1;
- }
- }
-
- for (const std::string& input : mOptions.overlayFiles) {
- if (!mergePath(input, true)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
- return 1;
- }
- }
-
- if (!verifyNoExternalPackages()) {
- return 1;
- }
-
- if (!mOptions.staticLib) {
- PrivateAttributeMover mover;
- if (!mover.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed moving private attributes");
- return 1;
- }
-
- // Assign IDs if we are building a regular app.
- IdAssigner idAssigner(&mOptions.stableIdMap);
- if (!idAssigner.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
- return 1;
- }
-
- // Now grab each ID and emit it as a file.
- if (mOptions.resourceIdMapPath) {
- for (auto& package : mFinalTable.packages) {
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- ResourceName name(package->name, type->type, entry->name);
- // The IDs are guaranteed to exist.
- mOptions.stableIdMap[std::move(name)] = ResourceId(package->id.value(),
- type->id.value(),
- entry->id.value());
- }
- }
- }
-
- if (!writeStableIdMapToPath(mContext->getDiagnostics(),
- mOptions.stableIdMap,
- mOptions.resourceIdMapPath.value())) {
- return 1;
- }
- }
- } else {
- // Static libs are merged with other apps, and ID collisions are bad, so verify that
- // no IDs have been set.
- if (!verifyNoIdsSet()) {
- return 1;
- }
- }
-
- // Add the names to mangle based on our source merge earlier.
- mContext->setNameManglerPolicy(NameManglerPolicy{
- mContext->getCompilationPackage(), mTableMerger->getMergedPackages() });
-
- // Add our table to the symbol table.
- mContext->getExternalSymbols()->prependSource(
- util::make_unique<ResourceTableSymbolSource>(&mFinalTable));
-
- ReferenceLinker linker;
- if (!linker.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
- return 1;
- }
-
- if (mOptions.staticLib) {
- if (!mOptions.products.empty()) {
- mContext->getDiagnostics()->warn(
- DiagMessage() << "can't select products when building static library");
- }
- } else {
- ProductFilter productFilter(mOptions.products);
- if (!productFilter.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
- return 1;
- }
- }
-
- if (!mOptions.noAutoVersion) {
- AutoVersioner versioner;
- if (!versioner.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles");
- return 1;
- }
- }
-
- if (!mOptions.staticLib && mContext->getMinSdkVersion() > 0) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(
- DiagMessage() << "collapsing resource versions for minimum SDK "
- << mContext->getMinSdkVersion());
- }
-
- VersionCollapser collapser;
- if (!collapser.consume(mContext, &mFinalTable)) {
- return 1;
- }
- }
-
- if (!mOptions.noResourceDeduping) {
- ResourceDeduper deduper;
- if (!deduper.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed deduping resources");
- return 1;
- }
- }
-
- proguard::KeepSet proguardKeepSet;
- proguard::KeepSet proguardMainDexKeepSet;
-
- if (mOptions.staticLib) {
- if (mOptions.tableSplitterOptions.configFilter != nullptr ||
- mOptions.tableSplitterOptions.preferredDensity) {
- mContext->getDiagnostics()->warn(
- DiagMessage() << "can't strip resources when building static library");
- }
- } else {
- // Adjust the SplitConstraints so that their SDK version is stripped if it is less
- // than or equal to the minSdk. Otherwise the resources that have had their SDK version
- // stripped due to minSdk won't ever match.
- std::vector<SplitConstraints> adjustedConstraintsList;
- adjustedConstraintsList.reserve(mOptions.splitConstraints.size());
- for (const SplitConstraints& constraints : mOptions.splitConstraints) {
- SplitConstraints adjustedConstraints;
- for (const ConfigDescription& config : constraints.configs) {
- if (config.sdkVersion <= mContext->getMinSdkVersion()) {
- adjustedConstraints.configs.insert(config.copyWithoutSdkVersion());
- } else {
- adjustedConstraints.configs.insert(config);
- }
- }
- adjustedConstraintsList.push_back(std::move(adjustedConstraints));
- }
-
- TableSplitter tableSplitter(adjustedConstraintsList, mOptions.tableSplitterOptions);
- if (!tableSplitter.verifySplitConstraints(mContext)) {
- return 1;
- }
- tableSplitter.splitTable(&mFinalTable);
-
- // 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, ", ") << "'");
- }
-
- 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;
- }
-
- if (!writeApk(archiveWriter.get(), &proguardKeepSet, splitManifest.get(),
- splitTable.get())) {
- return 1;
- }
-
- ++pathIter;
- ++splitConstraintsIter;
- }
- }
-
- // 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;
- }
-
- bool error = false;
- {
- // 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.
- manifestXml->file.name.package = mContext->getCompilationPackage();
-
- XmlReferenceLinker manifestLinker;
- if (manifestLinker.consume(mContext, manifestXml.get())) {
- if (mOptions.generateProguardRulesPath &&
- !proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
- manifestXml.get(),
- &proguardKeepSet)) {
- error = true;
- }
-
- if (mOptions.generateMainDexProguardRulesPath &&
- !proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
- manifestXml.get(),
- &proguardMainDexKeepSet,
- true)) {
- error = true;
- }
-
- if (mOptions.generateJavaClassPath) {
- if (!writeManifestJavaFile(manifestXml.get())) {
- error = true;
- }
- }
-
- if (mOptions.noXmlNamespaces) {
- // PackageParser will fail if URIs are removed from AndroidManifest.xml.
- XmlNamespaceRemover namespaceRemover(true /* keepUris */);
- if (!namespaceRemover.consume(mContext, manifestXml.get())) {
- error = true;
- }
- }
- } else {
- error = true;
- }
- }
-
- if (error) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed processing manifest");
- return 1;
- }
-
- if (!writeApk(archiveWriter.get(), &proguardKeepSet, manifestXml.get(), &mFinalTable)) {
- return 1;
- }
-
- if (mOptions.generateJavaClassPath) {
- JavaClassGeneratorOptions options;
- options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
- options.javadocAnnotations = mOptions.javadocAnnotations;
-
- if (mOptions.staticLib || mOptions.generateNonFinalIds) {
- options.useFinal = false;
- }
-
- const StringPiece actualPackage = mContext->getCompilationPackage();
- StringPiece outputPackage = mContext->getCompilationPackage();
- if (mOptions.customJavaPackage) {
- // Override the output java package to the custom one.
- outputPackage = mOptions.customJavaPackage.value();
- }
-
- if (mOptions.privateSymbols) {
- // If we defined a private symbols package, we only emit Public symbols
- // to the original package, and private and public symbols to the private package.
-
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
- if (!writeJavaFile(&mFinalTable, mContext->getCompilationPackage(),
- outputPackage, options)) {
- return 1;
- }
-
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
- outputPackage = mOptions.privateSymbols.value();
- }
-
- if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
- return 1;
- }
-
- for (const std::string& extraPackage : mOptions.extraJavaPackages) {
- if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
- return 1;
- }
- }
- }
-
- if (!writeProguardFile(mOptions.generateProguardRulesPath, proguardKeepSet)) {
- return 1;
- }
-
- if (!writeProguardFile(mOptions.generateMainDexProguardRulesPath, proguardMainDexKeepSet)) {
- return 1;
- }
-
- if (mContext->verbose()) {
- DebugPrintTableOptions debugPrintTableOptions;
- debugPrintTableOptions.showSources = true;
- Debug::printTable(&mFinalTable, debugPrintTableOptions);
- }
- return 0;
+ }
}
-private:
- LinkOptions mOptions;
- LinkContext* mContext;
- ResourceTable mFinalTable;
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed to write resources.arsc to archive");
+ return false;
+ }
- std::unique_ptr<TableMerger> mTableMerger;
+ bool FlattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
+ // Create the file/zip entry.
+ if (!writer->StartEntry("resources.arsc.flat", 0)) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to open");
+ return false;
+ }
- // A pointer to the FileCollection representing the filesystem (not archives).
- std::unique_ptr<io::FileCollection> mFileCollection;
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call
+ // writer->FinishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the
+ // ZeroCopyOutputStream interface.
+ CopyingOutputStreamAdaptor adaptor(writer);
- // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
- std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
+ std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table);
+ if (!pb_table->SerializeToZeroCopyStream(&adaptor)) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to write");
+ return false;
+ }
+ }
- // A vector of ResourceTables. This is here to retain ownership, so that the SymbolTable
- // can use these.
- std::vector<std::unique_ptr<ResourceTable>> mStaticTableIncludes;
+ if (!writer->FinishEntry()) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed to finish entry");
+ return false;
+ }
+ return true;
+ }
+
+ bool WriteJavaFile(ResourceTable* table,
+ const StringPiece& package_name_to_generate,
+ const StringPiece& out_package,
+ const JavaClassGeneratorOptions& java_options) {
+ if (!options_.generate_java_class_path) {
+ return true;
+ }
+
+ std::string out_path = options_.generate_java_class_path.value();
+ file::AppendPath(&out_path, file::PackageToPath(out_package));
+ if (!file::mkdirs(out_path)) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed to create directory '" << out_path << "'");
+ return false;
+ }
+
+ file::AppendPath(&out_path, "R.java");
+
+ std::ofstream fout(out_path, std::ofstream::binary);
+ if (!fout) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed writing to '" << out_path << "': "
+ << android::base::SystemErrorCodeToString(errno));
+ return false;
+ }
+
+ JavaClassGenerator generator(context_, table, java_options);
+ if (!generator.Generate(package_name_to_generate, out_package, &fout)) {
+ context_->GetDiagnostics()->Error(DiagMessage(out_path)
+ << generator.getError());
+ return false;
+ }
+
+ if (!fout) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed writing to '" << out_path << "': "
+ << android::base::SystemErrorCodeToString(errno));
+ }
+ return true;
+ }
+
+ bool WriteManifestJavaFile(xml::XmlResource* manifest_xml) {
+ if (!options_.generate_java_class_path) {
+ return true;
+ }
+
+ std::unique_ptr<ClassDefinition> manifest_class =
+ GenerateManifestClass(context_->GetDiagnostics(), manifest_xml);
+
+ if (!manifest_class) {
+ // Something bad happened, but we already logged it, so exit.
+ return false;
+ }
+
+ if (manifest_class->empty()) {
+ // Empty Manifest class, no need to generate it.
+ return true;
+ }
+
+ // Add any JavaDoc annotations to the generated class.
+ for (const std::string& annotation : options_.javadoc_annotations) {
+ std::string proper_annotation = "@";
+ proper_annotation += annotation;
+ manifest_class->GetCommentBuilder()->AppendComment(proper_annotation);
+ }
+
+ const std::string& package_utf8 = context_->GetCompilationPackage();
+
+ std::string out_path = options_.generate_java_class_path.value();
+ file::AppendPath(&out_path, file::PackageToPath(package_utf8));
+
+ if (!file::mkdirs(out_path)) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed to create directory '" << out_path << "'");
+ return false;
+ }
+
+ file::AppendPath(&out_path, "Manifest.java");
+
+ std::ofstream fout(out_path, std::ofstream::binary);
+ if (!fout) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed writing to '" << out_path << "': "
+ << android::base::SystemErrorCodeToString(errno));
+ return false;
+ }
+
+ if (!ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8,
+ true, &fout)) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed writing to '" << out_path << "': "
+ << android::base::SystemErrorCodeToString(errno));
+ return false;
+ }
+ return true;
+ }
+
+ bool WriteProguardFile(const Maybe<std::string>& out,
+ const proguard::KeepSet& keep_set) {
+ if (!out) {
+ return true;
+ }
+
+ const std::string& out_path = out.value();
+ std::ofstream fout(out_path, std::ofstream::binary);
+ if (!fout) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed to open '" << out_path << "': "
+ << android::base::SystemErrorCodeToString(errno));
+ return false;
+ }
+
+ proguard::WriteKeepSet(&fout, keep_set);
+ if (!fout) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed writing to '" << out_path << "': "
+ << android::base::SystemErrorCodeToString(errno));
+ return false;
+ }
+ return true;
+ }
+
+ std::unique_ptr<ResourceTable> LoadStaticLibrary(const std::string& input,
+ std::string* out_error) {
+ std::unique_ptr<io::ZipFileCollection> collection =
+ io::ZipFileCollection::Create(input, out_error);
+ if (!collection) {
+ return {};
+ }
+ return LoadTablePbFromCollection(collection.get());
+ }
+
+ std::unique_ptr<ResourceTable> LoadTablePbFromCollection(
+ io::IFileCollection* collection) {
+ io::IFile* file = collection->FindFile("resources.arsc.flat");
+ if (!file) {
+ return {};
+ }
+
+ std::unique_ptr<io::IData> data = file->OpenAsData();
+ return LoadTableFromPb(file->GetSource(), data->data(), data->size(),
+ context_->GetDiagnostics());
+ }
+
+ bool MergeStaticLibrary(const std::string& input, bool override) {
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(DiagMessage()
+ << "merging static library " << input);
+ }
+
+ std::string error_str;
+ std::unique_ptr<io::ZipFileCollection> collection =
+ io::ZipFileCollection::Create(input, &error_str);
+ if (!collection) {
+ context_->GetDiagnostics()->Error(DiagMessage(input) << error_str);
+ return false;
+ }
+
+ std::unique_ptr<ResourceTable> table =
+ LoadTablePbFromCollection(collection.get());
+ if (!table) {
+ context_->GetDiagnostics()->Error(DiagMessage(input)
+ << "invalid static library");
+ return false;
+ }
+
+ ResourceTablePackage* pkg = table->FindPackageById(0x7f);
+ if (!pkg) {
+ context_->GetDiagnostics()->Error(DiagMessage(input)
+ << "static library has no package");
+ return false;
+ }
+
+ bool result;
+ if (options_.no_static_lib_packages) {
+ // Merge all resources as if they were in the compilation package. This is
+ // the old behavior of aapt.
+
+ // Add the package to the set of --extra-packages so we emit an R.java for
+ // each library package.
+ if (!pkg->name.empty()) {
+ options_.extra_java_packages.insert(pkg->name);
+ }
+
+ pkg->name = "";
+ if (override) {
+ result = table_merger_->MergeOverlay(Source(input), table.get(),
+ collection.get());
+ } else {
+ result =
+ table_merger_->Merge(Source(input), table.get(), collection.get());
+ }
+
+ } else {
+ // This is the proper way to merge libraries, where the package name is
+ // preserved and resource names are mangled.
+ result = table_merger_->MergeAndMangle(Source(input), pkg->name,
+ table.get(), collection.get());
+ }
+
+ if (!result) {
+ return false;
+ }
+
+ // Make sure to move the collection into the set of IFileCollections.
+ collections_.push_back(std::move(collection));
+ return true;
+ }
+
+ bool MergeResourceTable(io::IFile* file, bool override) {
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(
+ DiagMessage() << "merging resource table " << file->GetSource());
+ }
+
+ std::unique_ptr<io::IData> data = file->OpenAsData();
+ if (!data) {
+ context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
+ << "failed to open file");
+ return false;
+ }
+
+ std::unique_ptr<ResourceTable> table =
+ LoadTableFromPb(file->GetSource(), data->data(), data->size(),
+ context_->GetDiagnostics());
+ if (!table) {
+ return false;
+ }
+
+ bool result = false;
+ if (override) {
+ result = table_merger_->MergeOverlay(file->GetSource(), table.get());
+ } else {
+ result = table_merger_->Merge(file->GetSource(), table.get());
+ }
+ return result;
+ }
+
+ bool MergeCompiledFile(io::IFile* file, ResourceFile* file_desc,
+ bool override) {
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(
+ DiagMessage() << "merging '" << file_desc->name
+ << "' from compiled file " << file->GetSource());
+ }
+
+ bool result = false;
+ if (override) {
+ result = table_merger_->MergeFileOverlay(*file_desc, file);
+ } else {
+ result = table_merger_->MergeFile(*file_desc, file);
+ }
+
+ if (!result) {
+ return false;
+ }
+
+ // Add the exports of this file to the table.
+ for (SourcedResourceName& exported_symbol : file_desc->exported_symbols) {
+ if (exported_symbol.name.package.empty()) {
+ exported_symbol.name.package = context_->GetCompilationPackage();
+ }
+
+ ResourceNameRef res_name = exported_symbol.name;
+
+ Maybe<ResourceName> mangled_name =
+ context_->GetNameMangler()->MangleName(exported_symbol.name);
+ if (mangled_name) {
+ res_name = mangled_name.value();
+ }
+
+ std::unique_ptr<Id> id = util::make_unique<Id>();
+ id->SetSource(file_desc->source.WithLine(exported_symbol.line));
+ bool result = final_table_.AddResourceAllowMangled(
+ res_name, ConfigDescription::DefaultConfig(), std::string(),
+ std::move(id), context_->GetDiagnostics());
+ if (!result) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Takes a path to load as a ZIP file and merges the files within into the
+ * master ResourceTable.
+ * If override is true, conflicting resources are allowed to override each
+ * other, in order of last seen.
+ *
+ * An io::IFileCollection is created from the ZIP file and added to the set of
+ * io::IFileCollections that are open.
+ */
+ bool MergeArchive(const std::string& input, bool override) {
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(DiagMessage() << "merging archive "
+ << input);
+ }
+
+ std::string error_str;
+ std::unique_ptr<io::ZipFileCollection> collection =
+ io::ZipFileCollection::Create(input, &error_str);
+ if (!collection) {
+ context_->GetDiagnostics()->Error(DiagMessage(input) << error_str);
+ return false;
+ }
+
+ bool error = false;
+ for (auto iter = collection->Iterator(); iter->HasNext();) {
+ if (!MergeFile(iter->Next(), override)) {
+ error = true;
+ }
+ }
+
+ // Make sure to move the collection into the set of IFileCollections.
+ collections_.push_back(std::move(collection));
+ return !error;
+ }
+
+ /**
+ * Takes a path to load and merge into the master ResourceTable. If override
+ * is true,
+ * conflicting resources are allowed to override each other, in order of last
+ * seen.
+ *
+ * If the file path ends with .flata, .jar, .jack, or .zip the file is treated
+ * as ZIP archive
+ * and the files within are merged individually.
+ *
+ * Otherwise the files is processed on its own.
+ */
+ bool MergePath(const std::string& path, bool override) {
+ if (util::EndsWith(path, ".flata") || util::EndsWith(path, ".jar") ||
+ util::EndsWith(path, ".jack") || util::EndsWith(path, ".zip")) {
+ return MergeArchive(path, override);
+ } else if (util::EndsWith(path, ".apk")) {
+ return MergeStaticLibrary(path, override);
+ }
+
+ io::IFile* file = file_collection_->InsertFile(path);
+ return MergeFile(file, override);
+ }
+
+ /**
+ * Takes a file to load and merge into the master ResourceTable. If override
+ * is true,
+ * conflicting resources are allowed to override each other, in order of last
+ * seen.
+ *
+ * If the file ends with .arsc.flat, then it is loaded as a ResourceTable and
+ * merged into the
+ * master ResourceTable. If the file ends with .flat, then it is treated like
+ * a compiled file
+ * and the header data is read and merged into the final ResourceTable.
+ *
+ * All other file types are ignored. This is because these files could be
+ * coming from a zip,
+ * where we could have other files like classes.dex.
+ */
+ bool MergeFile(io::IFile* file, bool override) {
+ const Source& src = file->GetSource();
+ if (util::EndsWith(src.path, ".arsc.flat")) {
+ return MergeResourceTable(file, override);
+
+ } else if (util::EndsWith(src.path, ".flat")) {
+ // Try opening the file and looking for an Export header.
+ std::unique_ptr<io::IData> data = file->OpenAsData();
+ if (!data) {
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to open");
+ return false;
+ }
+
+ CompiledFileInputStream input_stream(data->data(), data->size());
+ uint32_t num_files = 0;
+ if (!input_stream.ReadLittleEndian32(&num_files)) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "failed read num files");
+ return false;
+ }
+
+ for (uint32_t i = 0; i < num_files; i++) {
+ pb::CompiledFile compiled_file;
+ if (!input_stream.ReadCompiledFile(&compiled_file)) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(src) << "failed to read compiled file header");
+ return false;
+ }
+
+ uint64_t offset, len;
+ if (!input_stream.ReadDataMetaData(&offset, &len)) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "failed to read data meta data");
+ return false;
+ }
+
+ std::unique_ptr<ResourceFile> resource_file =
+ DeserializeCompiledFileFromPb(compiled_file, file->GetSource(),
+ context_->GetDiagnostics());
+ if (!resource_file) {
+ return false;
+ }
+
+ if (!MergeCompiledFile(file->CreateFileSegment(offset, len),
+ resource_file.get(), override)) {
+ return false;
+ }
+ }
+ return true;
+ } else if (util::EndsWith(src.path, ".xml") ||
+ util::EndsWith(src.path, ".png")) {
+ // Since AAPT compiles these file types and appends .flat to them, seeing
+ // their raw extensions is a sign that they weren't compiled.
+ const StringPiece file_type =
+ util::EndsWith(src.path, ".xml") ? "XML" : "PNG";
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "uncompiled " << file_type
+ << " file passed as argument. Must be "
+ "compiled first into .flat file.");
+ return false;
+ }
+
+ // Ignore non .flat files. This could be classes.dex or something else that
+ // happens
+ // to be in an archive.
+ return true;
+ }
+
+ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(
+ const AppInfo& app_info, const SplitConstraints& constraints) {
+ std::unique_ptr<xml::XmlResource> doc =
+ util::make_unique<xml::XmlResource>();
+
+ std::unique_ptr<xml::Namespace> namespace_android =
+ util::make_unique<xml::Namespace>();
+ namespace_android->namespace_uri = xml::kSchemaAndroid;
+ namespace_android->namespace_prefix = "android";
+
+ std::unique_ptr<xml::Element> manifest_el =
+ util::make_unique<xml::Element>();
+ manifest_el->name = "manifest";
+ manifest_el->attributes.push_back(
+ xml::Attribute{"", "package", app_info.package});
+
+ if (app_info.version_code) {
+ manifest_el->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "versionCode",
+ std::to_string(app_info.version_code.value())});
+ }
+
+ if (app_info.revision_code) {
+ manifest_el->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "revisionCode",
+ std::to_string(app_info.revision_code.value())});
+ }
+
+ std::stringstream split_name;
+ split_name << "config." << util::Joiner(constraints.configs, "_");
+
+ manifest_el->attributes.push_back(
+ xml::Attribute{"", "split", split_name.str()});
+
+ std::unique_ptr<xml::Element> application_el =
+ util::make_unique<xml::Element>();
+ application_el->name = "application";
+ application_el->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"});
+
+ manifest_el->AddChild(std::move(application_el));
+ namespace_android->AddChild(std::move(manifest_el));
+ doc->root = std::move(namespace_android);
+ return doc;
+ }
+
+ /**
+ * Writes the AndroidManifest, ResourceTable, and all XML files referenced by
+ * the ResourceTable to the IArchiveWriter.
+ */
+ bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set,
+ xml::XmlResource* manifest, ResourceTable* table) {
+ const bool keep_raw_values = options_.static_lib;
+ bool result = FlattenXml(manifest, "AndroidManifest.xml", {},
+ keep_raw_values, writer, context_);
+ if (!result) {
+ return false;
+ }
+
+ ResourceFileFlattenerOptions file_flattener_options;
+ file_flattener_options.keep_raw_values = keep_raw_values;
+ file_flattener_options.do_not_compress_anything =
+ options_.do_not_compress_anything;
+ file_flattener_options.extensions_to_not_compress =
+ options_.extensions_to_not_compress;
+ file_flattener_options.no_auto_version = options_.no_auto_version;
+ file_flattener_options.no_version_vectors = options_.no_version_vectors;
+ file_flattener_options.no_xml_namespaces = options_.no_xml_namespaces;
+ file_flattener_options.update_proguard_spec =
+ static_cast<bool>(options_.generate_proguard_rules_path);
+
+ ResourceFileFlattener file_flattener(file_flattener_options, context_,
+ keep_set);
+
+ if (!file_flattener.Flatten(table, writer)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed linking file resources");
+ return false;
+ }
+
+ if (options_.static_lib) {
+ if (!FlattenTableToPb(table, writer)) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed to write resources.arsc.flat");
+ return false;
+ }
+ } else {
+ if (!FlattenTable(table, writer)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed to write resources.arsc");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ int Run(const std::vector<std::string>& input_files) {
+ // Load the AndroidManifest.xml
+ std::unique_ptr<xml::XmlResource> manifest_xml =
+ LoadXml(options_.manifest_path, context_->GetDiagnostics());
+ if (!manifest_xml) {
+ return 1;
+ }
+
+ // First extract the Package name without modifying it (via
+ // --rename-manifest-package).
+ if (Maybe<AppInfo> maybe_app_info = ExtractAppInfoFromManifest(
+ manifest_xml.get(), context_->GetDiagnostics())) {
+ const AppInfo& app_info = maybe_app_info.value();
+ context_->SetCompilationPackage(app_info.package);
+ }
+
+ ManifestFixer manifest_fixer(options_.manifest_fixer_options);
+ if (!manifest_fixer.Consume(context_, manifest_xml.get())) {
+ return 1;
+ }
+
+ Maybe<AppInfo> maybe_app_info = ExtractAppInfoFromManifest(
+ manifest_xml.get(), context_->GetDiagnostics());
+ if (!maybe_app_info) {
+ return 1;
+ }
+
+ const AppInfo& app_info = maybe_app_info.value();
+ if (app_info.min_sdk_version) {
+ if (Maybe<int> maybe_min_sdk_version = ResourceUtils::ParseSdkVersion(
+ app_info.min_sdk_version.value())) {
+ context_->SetMinSdkVersion(maybe_min_sdk_version.value());
+ }
+ }
+
+ context_->SetNameManglerPolicy(
+ NameManglerPolicy{context_->GetCompilationPackage()});
+ if (context_->GetCompilationPackage() == "android") {
+ context_->SetPackageId(0x01);
+ } else {
+ context_->SetPackageId(0x7f);
+ }
+
+ if (!LoadSymbolsFromIncludePaths()) {
+ return 1;
+ }
+
+ TableMergerOptions table_merger_options;
+ table_merger_options.auto_add_overlay = options_.auto_add_overlay;
+ table_merger_ = util::make_unique<TableMerger>(context_, &final_table_,
+ table_merger_options);
+
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(DiagMessage()
+ << "linking package '"
+ << context_->GetCompilationPackage()
+ << "' with package ID " << std::hex
+ << (int)context_->GetPackageId());
+ }
+
+ for (const std::string& input : input_files) {
+ if (!MergePath(input, false)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed parsing input");
+ return 1;
+ }
+ }
+
+ for (const std::string& input : options_.overlay_files) {
+ if (!MergePath(input, true)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed parsing overlays");
+ return 1;
+ }
+ }
+
+ if (!VerifyNoExternalPackages()) {
+ return 1;
+ }
+
+ if (!options_.static_lib) {
+ PrivateAttributeMover mover;
+ if (!mover.Consume(context_, &final_table_)) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed moving private attributes");
+ return 1;
+ }
+
+ // Assign IDs if we are building a regular app.
+ IdAssigner id_assigner(&options_.stable_id_map);
+ if (!id_assigner.Consume(context_, &final_table_)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed assigning IDs");
+ return 1;
+ }
+
+ // Now grab each ID and emit it as a file.
+ if (options_.resource_id_map_path) {
+ for (auto& package : final_table_.packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ ResourceName name(package->name, type->type, entry->name);
+ // The IDs are guaranteed to exist.
+ options_.stable_id_map[std::move(name)] = ResourceId(
+ package->id.value(), type->id.value(), entry->id.value());
+ }
+ }
+ }
+
+ if (!WriteStableIdMapToPath(context_->GetDiagnostics(),
+ options_.stable_id_map,
+ options_.resource_id_map_path.value())) {
+ return 1;
+ }
+ }
+ } else {
+ // Static libs are merged with other apps, and ID collisions are bad, so
+ // verify that
+ // no IDs have been set.
+ if (!VerifyNoIdsSet()) {
+ return 1;
+ }
+ }
+
+ // Add the names to mangle based on our source merge earlier.
+ context_->SetNameManglerPolicy(NameManglerPolicy{
+ context_->GetCompilationPackage(), table_merger_->merged_packages()});
+
+ // Add our table to the symbol table.
+ context_->GetExternalSymbols()->PrependSource(
+ util::make_unique<ResourceTableSymbolSource>(&final_table_));
+
+ ReferenceLinker linker;
+ if (!linker.Consume(context_, &final_table_)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed linking references");
+ return 1;
+ }
+
+ if (options_.static_lib) {
+ if (!options_.products.empty()) {
+ context_->GetDiagnostics()
+ ->Warn(DiagMessage()
+ << "can't select products when building static library");
+ }
+ } else {
+ ProductFilter product_filter(options_.products);
+ if (!product_filter.Consume(context_, &final_table_)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed stripping products");
+ return 1;
+ }
+ }
+
+ if (!options_.no_auto_version) {
+ AutoVersioner versioner;
+ if (!versioner.Consume(context_, &final_table_)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed versioning styles");
+ return 1;
+ }
+ }
+
+ if (!options_.static_lib && context_->GetMinSdkVersion() > 0) {
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(
+ DiagMessage() << "collapsing resource versions for minimum SDK "
+ << context_->GetMinSdkVersion());
+ }
+
+ VersionCollapser collapser;
+ if (!collapser.Consume(context_, &final_table_)) {
+ return 1;
+ }
+ }
+
+ if (!options_.no_resource_deduping) {
+ ResourceDeduper deduper;
+ if (!deduper.Consume(context_, &final_table_)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed deduping resources");
+ return 1;
+ }
+ }
+
+ proguard::KeepSet proguard_keep_set;
+ proguard::KeepSet proguard_main_dex_keep_set;
+
+ if (options_.static_lib) {
+ if (options_.table_splitter_options.config_filter != nullptr ||
+ options_.table_splitter_options.preferred_density) {
+ context_->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> adjusted_constraints_list;
+ adjusted_constraints_list.reserve(options_.split_constraints.size());
+ for (const SplitConstraints& constraints : options_.split_constraints) {
+ SplitConstraints adjusted_constraints;
+ for (const ConfigDescription& config : constraints.configs) {
+ if (config.sdkVersion <= context_->GetMinSdkVersion()) {
+ adjusted_constraints.configs.insert(config.CopyWithoutSdkVersion());
+ } else {
+ adjusted_constraints.configs.insert(config);
+ }
+ }
+ adjusted_constraints_list.push_back(std::move(adjusted_constraints));
+ }
+
+ TableSplitter table_splitter(adjusted_constraints_list,
+ options_.table_splitter_options);
+ if (!table_splitter.VerifySplitConstraints(context_)) {
+ return 1;
+ }
+ table_splitter.SplitTable(&final_table_);
+
+ // Now we need to write out the Split APKs.
+ auto path_iter = options_.split_paths.begin();
+ auto split_constraints_iter = adjusted_constraints_list.begin();
+ for (std::unique_ptr<ResourceTable>& split_table :
+ table_splitter.splits()) {
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(
+ DiagMessage(*path_iter)
+ << "generating split with configurations '"
+ << util::Joiner(split_constraints_iter->configs, ", ") << "'");
+ }
+
+ std::unique_ptr<IArchiveWriter> archive_writer =
+ MakeArchiveWriter(*path_iter);
+ if (!archive_writer) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed to create archive");
+ return 1;
+ }
+
+ // Generate an AndroidManifest.xml for each split.
+ std::unique_ptr<xml::XmlResource> split_manifest =
+ GenerateSplitManifest(app_info, *split_constraints_iter);
+
+ XmlReferenceLinker linker;
+ if (!linker.Consume(context_, split_manifest.get())) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "failed to create Split AndroidManifest.xml");
+ return 1;
+ }
+
+ if (!WriteApk(archive_writer.get(), &proguard_keep_set,
+ split_manifest.get(), split_table.get())) {
+ return 1;
+ }
+
+ ++path_iter;
+ ++split_constraints_iter;
+ }
+ }
+
+ // Start writing the base APK.
+ std::unique_ptr<IArchiveWriter> archive_writer =
+ MakeArchiveWriter(options_.output_path);
+ if (!archive_writer) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed to create archive");
+ return 1;
+ }
+
+ bool error = false;
+ {
+ // 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.
+ manifest_xml->file.name.package = context_->GetCompilationPackage();
+
+ XmlReferenceLinker manifest_linker;
+ if (manifest_linker.Consume(context_, manifest_xml.get())) {
+ if (options_.generate_proguard_rules_path &&
+ !proguard::CollectProguardRulesForManifest(
+ Source(options_.manifest_path), manifest_xml.get(),
+ &proguard_keep_set)) {
+ error = true;
+ }
+
+ if (options_.generate_main_dex_proguard_rules_path &&
+ !proguard::CollectProguardRulesForManifest(
+ Source(options_.manifest_path), manifest_xml.get(),
+ &proguard_main_dex_keep_set, true)) {
+ error = true;
+ }
+
+ if (options_.generate_java_class_path) {
+ if (!WriteManifestJavaFile(manifest_xml.get())) {
+ error = true;
+ }
+ }
+
+ if (options_.no_xml_namespaces) {
+ // PackageParser will fail if URIs are removed from
+ // AndroidManifest.xml.
+ XmlNamespaceRemover namespace_remover(true /* keepUris */);
+ if (!namespace_remover.Consume(context_, manifest_xml.get())) {
+ error = true;
+ }
+ }
+ } else {
+ error = true;
+ }
+ }
+
+ if (error) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed processing manifest");
+ return 1;
+ }
+
+ if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(),
+ &final_table_)) {
+ return 1;
+ }
+
+ if (options_.generate_java_class_path) {
+ JavaClassGeneratorOptions options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ options.javadoc_annotations = options_.javadoc_annotations;
+
+ if (options_.static_lib || options_.generate_non_final_ids) {
+ options.use_final = false;
+ }
+
+ const StringPiece actual_package = context_->GetCompilationPackage();
+ StringPiece output_package = context_->GetCompilationPackage();
+ if (options_.custom_java_package) {
+ // Override the output java package to the custom one.
+ output_package = options_.custom_java_package.value();
+ }
+
+ if (options_.private_symbols) {
+ // If we defined a private symbols package, we only emit Public symbols
+ // to the original package, and private and public symbols to the
+ // private package.
+
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
+ if (!WriteJavaFile(&final_table_, context_->GetCompilationPackage(),
+ output_package, options)) {
+ return 1;
+ }
+
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
+ output_package = options_.private_symbols.value();
+ }
+
+ if (!WriteJavaFile(&final_table_, actual_package, output_package,
+ options)) {
+ return 1;
+ }
+
+ for (const std::string& extra_package : options_.extra_java_packages) {
+ if (!WriteJavaFile(&final_table_, actual_package, extra_package,
+ options)) {
+ return 1;
+ }
+ }
+ }
+
+ if (!WriteProguardFile(options_.generate_proguard_rules_path,
+ proguard_keep_set)) {
+ return 1;
+ }
+
+ if (!WriteProguardFile(options_.generate_main_dex_proguard_rules_path,
+ proguard_main_dex_keep_set)) {
+ return 1;
+ }
+
+ if (context_->IsVerbose()) {
+ DebugPrintTableOptions debug_print_table_options;
+ debug_print_table_options.show_sources = true;
+ Debug::PrintTable(&final_table_, debug_print_table_options);
+ }
+ return 0;
+ }
+
+ private:
+ LinkOptions options_;
+ LinkContext* context_;
+ ResourceTable final_table_;
+
+ std::unique_ptr<TableMerger> table_merger_;
+
+ // A pointer to the FileCollection representing the filesystem (not archives).
+ std::unique_ptr<io::FileCollection> file_collection_;
+
+ // A vector of IFileCollections. This is mainly here to keep ownership of the
+ // collections.
+ std::vector<std::unique_ptr<io::IFileCollection>> collections_;
+
+ // A vector of ResourceTables. This is here to retain ownership, so that the
+ // SymbolTable
+ // can use these.
+ std::vector<std::unique_ptr<ResourceTable>> static_table_includes_;
};
-int link(const std::vector<StringPiece>& args) {
- LinkContext context;
- LinkOptions options;
- std::vector<std::string> overlayArgList;
- std::vector<std::string> extraJavaPackages;
- Maybe<std::string> configs;
- Maybe<std::string> preferredDensity;
- Maybe<std::string> productList;
- bool legacyXFlag = false;
- 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",
- &options.manifestPath)
- .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
- .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
- "The last conflicting resource given takes precedence.",
- &overlayArgList)
- .optionalFlag("--java", "Directory in which to generate R.java",
- &options.generateJavaClassPath)
- .optionalFlag("--proguard", "Output file for generated Proguard rules",
- &options.generateProguardRulesPath)
- .optionalFlag("--proguard-main-dex",
- "Output file for generated Proguard rules for the main dex",
- &options.generateMainDexProguardRulesPath)
- .optionalSwitch("--no-auto-version",
- "Disables automatic style and layout SDK versioning",
- &options.noAutoVersion)
- .optionalSwitch("--no-version-vectors",
- "Disables automatic versioning of vector drawables. Use this only\n"
- "when building with vector drawable support library",
- &options.noVersionVectors)
- .optionalSwitch("--no-resource-deduping", "Disables automatic deduping of resources with\n"
- "identical values across compatible configurations.",
- &options.noResourceDeduping)
- .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
- &legacyXFlag)
- .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
- &requireLocalization)
- .optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
- "is all configurations", &configs)
- .optionalFlag("--preferred-density",
- "Selects the closest matching density and strips out all others.",
- &preferredDensity)
- .optionalFlag("--product", "Comma separated list of product names to keep",
- &productList)
- .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
- "by -o",
- &options.outputToDirectory)
- .optionalSwitch("--no-xml-namespaces", "Removes XML namespace prefix and URI "
- "information from AndroidManifest.xml\nand XML binaries in res/*.",
- &options.noXmlNamespaces)
- .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
- "AndroidManifest.xml",
- &options.manifestFixerOptions.minSdkVersionDefault)
- .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
- "AndroidManifest.xml",
- &options.manifestFixerOptions.targetSdkVersionDefault)
- .optionalFlag("--version-code", "Version code (integer) to inject into the "
- "AndroidManifest.xml if none is present",
- &options.manifestFixerOptions.versionCodeDefault)
- .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
- "if none is present",
- &options.manifestFixerOptions.versionNameDefault)
- .optionalSwitch("--static-lib", "Generate a static Android library",
- &options.staticLib)
- .optionalSwitch("--no-static-lib-packages",
- "Merge all library resources under the app's package",
- &options.noStaticLibPackages)
- .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
- "This is implied when --static-lib is specified.",
- &options.generateNonFinalIds)
- .optionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
- &stableIdFilePath)
- .optionalFlag("--emit-ids", "Emit a file at the given path with a list of name to ID\n"
- "mappings, suitable for use with --stable-ids.",
- &options.resourceIdMapPath)
- .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
- "private symbols.\n"
- "If not specified, public and private symbols will use the application's "
- "package name",
- &options.privateSymbols)
- .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
- &options.customJavaPackage)
- .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
- "package names",
- &extraJavaPackages)
- .optionalFlagList("--add-javadoc-annotation", "Adds a JavaDoc annotation to all "
+int Link(const std::vector<StringPiece>& args) {
+ LinkContext context;
+ LinkOptions options;
+ std::vector<std::string> overlay_arg_list;
+ std::vector<std::string> extra_java_packages;
+ Maybe<std::string> configs;
+ Maybe<std::string> preferred_density;
+ Maybe<std::string> product_list;
+ bool legacy_x_flag = false;
+ bool require_localization = false;
+ bool verbose = false;
+ Maybe<std::string> stable_id_file_path;
+ std::vector<std::string> split_args;
+ Flags flags =
+ Flags()
+ .RequiredFlag("-o", "Output path", &options.output_path)
+ .RequiredFlag("--manifest", "Path to the Android manifest to build",
+ &options.manifest_path)
+ .OptionalFlagList("-I", "Adds an Android APK to link against",
+ &options.include_paths)
+ .OptionalFlagList(
+ "-R",
+ "Compilation unit to link, using `overlay` semantics.\n"
+ "The last conflicting resource given takes precedence.",
+ &overlay_arg_list)
+ .OptionalFlag("--java", "Directory in which to generate R.java",
+ &options.generate_java_class_path)
+ .OptionalFlag("--proguard",
+ "Output file for generated Proguard rules",
+ &options.generate_proguard_rules_path)
+ .OptionalFlag(
+ "--proguard-main-dex",
+ "Output file for generated Proguard rules for the main dex",
+ &options.generate_main_dex_proguard_rules_path)
+ .OptionalSwitch("--no-auto-version",
+ "Disables automatic style and layout SDK versioning",
+ &options.no_auto_version)
+ .OptionalSwitch("--no-version-vectors",
+ "Disables automatic versioning of vector drawables. "
+ "Use this only\n"
+ "when building with vector drawable support library",
+ &options.no_version_vectors)
+ .OptionalSwitch("--no-resource-deduping",
+ "Disables automatic deduping of resources with\n"
+ "identical values across compatible configurations.",
+ &options.no_resource_deduping)
+ .OptionalSwitch(
+ "-x",
+ "Legacy flag that specifies to use the package identifier 0x01",
+ &legacy_x_flag)
+ .OptionalSwitch("-z",
+ "Require localization of strings marked 'suggested'",
+ &require_localization)
+ .OptionalFlag(
+ "-c",
+ "Comma separated list of configurations to include. The default\n"
+ "is all configurations",
+ &configs)
+ .OptionalFlag(
+ "--preferred-density",
+ "Selects the closest matching density and strips out all others.",
+ &preferred_density)
+ .OptionalFlag("--product",
+ "Comma separated list of product names to keep",
+ &product_list)
+ .OptionalSwitch("--output-to-dir",
+ "Outputs the APK contents to a directory specified "
+ "by -o",
+ &options.output_to_directory)
+ .OptionalSwitch("--no-xml-namespaces",
+ "Removes XML namespace prefix and URI "
+ "information from AndroidManifest.xml\nand XML "
+ "binaries in res/*.",
+ &options.no_xml_namespaces)
+ .OptionalFlag("--min-sdk-version",
+ "Default minimum SDK version to use for "
+ "AndroidManifest.xml",
+ &options.manifest_fixer_options.min_sdk_version_default)
+ .OptionalFlag(
+ "--target-sdk-version",
+ "Default target SDK version to use for "
+ "AndroidManifest.xml",
+ &options.manifest_fixer_options.target_sdk_version_default)
+ .OptionalFlag("--version-code",
+ "Version code (integer) to inject into the "
+ "AndroidManifest.xml if none is present",
+ &options.manifest_fixer_options.version_code_default)
+ .OptionalFlag("--version-name",
+ "Version name to inject into the AndroidManifest.xml "
+ "if none is present",
+ &options.manifest_fixer_options.version_name_default)
+ .OptionalSwitch("--static-lib", "Generate a static Android library",
+ &options.static_lib)
+ .OptionalSwitch("--no-static-lib-packages",
+ "Merge all library resources under the app's package",
+ &options.no_static_lib_packages)
+ .OptionalSwitch("--non-final-ids",
+ "Generates R.java without the final modifier.\n"
+ "This is implied when --static-lib is specified.",
+ &options.generate_non_final_ids)
+ .OptionalFlag("--stable-ids",
+ "File containing a list of name to ID mapping.",
+ &stable_id_file_path)
+ .OptionalFlag(
+ "--emit-ids",
+ "Emit a file at the given path with a list of name to ID\n"
+ "mappings, suitable for use with --stable-ids.",
+ &options.resource_id_map_path)
+ .OptionalFlag("--private-symbols",
+ "Package name to use when generating R.java for "
+ "private symbols.\n"
+ "If not specified, public and private symbols will use "
+ "the application's "
+ "package name",
+ &options.private_symbols)
+ .OptionalFlag("--custom-package",
+ "Custom Java package under which to generate R.java",
+ &options.custom_java_package)
+ .OptionalFlagList("--extra-packages",
+ "Generate the same R.java but with different "
+ "package names",
+ &extra_java_packages)
+ .OptionalFlagList("--add-javadoc-annotation",
+ "Adds a JavaDoc annotation to all "
"generated Java classes",
- &options.javadocAnnotations)
- .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
- "overlays without <add-resource> tags",
- &options.autoAddOverlay)
- .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
- &options.manifestFixerOptions.renameManifestPackage)
- .optionalFlag("--rename-instrumentation-target-package",
- "Changes the name of the target package for instrumentation. Most useful "
- "when used\nin conjunction with --rename-manifest-package",
- &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);
+ &options.javadoc_annotations)
+ .OptionalSwitch("--auto-add-overlay",
+ "Allows the addition of new resources in "
+ "overlays without <add-resource> tags",
+ &options.auto_add_overlay)
+ .OptionalFlag("--rename-manifest-package",
+ "Renames the package in AndroidManifest.xml",
+ &options.manifest_fixer_options.rename_manifest_package)
+ .OptionalFlag(
+ "--rename-instrumentation-target-package",
+ "Changes the name of the target package for instrumentation. "
+ "Most useful "
+ "when used\nin conjunction with --rename-manifest-package",
+ &options.manifest_fixer_options
+ .rename_instrumentation_target_package)
+ .OptionalFlagList("-0", "File extensions not to compress",
+ &options.extensions_to_not_compress)
+ .OptionalFlagList(
+ "--split",
+ "Split resources matching a set of configs out to a "
+ "Split APK.\nSyntax: path/to/output.apk:<config>[,<config>[...]]",
+ &split_args)
+ .OptionalSwitch("-v", "Enables verbose logging", &verbose);
- if (!flags.parse("aapt2 link", args, &std::cerr)) {
+ if (!flags.Parse("aapt2 link", args, &std::cerr)) {
+ return 1;
+ }
+
+ // Expand all argument-files passed into the command line. These start with
+ // '@'.
+ std::vector<std::string> arg_list;
+ for (const std::string& arg : flags.GetArgs()) {
+ if (util::StartsWith(arg, "@")) {
+ const std::string path = arg.substr(1, arg.size() - 1);
+ std::string error;
+ if (!file::AppendArgsFromFile(path, &arg_list, &error)) {
+ context.GetDiagnostics()->Error(DiagMessage(path) << error);
return 1;
+ }
+ } else {
+ arg_list.push_back(arg);
+ }
+ }
+
+ // Expand all argument-files passed to -R.
+ for (const std::string& arg : overlay_arg_list) {
+ if (util::StartsWith(arg, "@")) {
+ const std::string path = arg.substr(1, arg.size() - 1);
+ std::string error;
+ if (!file::AppendArgsFromFile(path, &options.overlay_files, &error)) {
+ context.GetDiagnostics()->Error(DiagMessage(path) << error);
+ return 1;
+ }
+ } else {
+ options.overlay_files.push_back(arg);
+ }
+ }
+
+ if (verbose) {
+ context.SetVerbose(verbose);
+ }
+
+ // Populate the set of extra packages for which to generate R.java.
+ for (std::string& extra_package : extra_java_packages) {
+ // A given package can actually be a colon separated list of packages.
+ for (StringPiece package : util::Split(extra_package, ':')) {
+ options.extra_java_packages.insert(package.ToString());
+ }
+ }
+
+ if (product_list) {
+ for (StringPiece product : util::Tokenize(product_list.value(), ',')) {
+ if (product != "" && product != "default") {
+ options.products.insert(product.ToString());
+ }
+ }
+ }
+
+ AxisConfigFilter filter;
+ if (configs) {
+ for (const StringPiece& config_str : util::Tokenize(configs.value(), ',')) {
+ ConfigDescription config;
+ LocaleValue lv;
+ if (lv.InitFromFilterString(config_str)) {
+ lv.WriteTo(&config);
+ } else if (!ConfigDescription::Parse(config_str, &config)) {
+ context.GetDiagnostics()->Error(DiagMessage() << "invalid config '"
+ << config_str
+ << "' for -c option");
+ return 1;
+ }
+
+ if (config.density != 0) {
+ context.GetDiagnostics()->Warn(DiagMessage() << "ignoring density '"
+ << config
+ << "' for -c option");
+ } else {
+ filter.AddConfig(config);
+ }
}
- // Expand all argument-files passed into the command line. These start with '@'.
- std::vector<std::string> argList;
- for (const std::string& arg : flags.getArgs()) {
- if (util::stringStartsWith(arg, "@")) {
- const std::string path = arg.substr(1, arg.size() - 1);
- std::string error;
- if (!file::appendArgsFromFile(path, &argList, &error)) {
- context.getDiagnostics()->error(DiagMessage(path) << error);
- return 1;
- }
- } else {
- argList.push_back(arg);
- }
+ options.table_splitter_options.config_filter = &filter;
+ }
+
+ if (preferred_density) {
+ ConfigDescription preferred_density_config;
+ if (!ConfigDescription::Parse(preferred_density.value(),
+ &preferred_density_config)) {
+ context.GetDiagnostics()->Error(
+ DiagMessage() << "invalid density '" << preferred_density.value()
+ << "' for --preferred-density option");
+ return 1;
}
- // Expand all argument-files passed to -R.
- for (const std::string& arg : overlayArgList) {
- if (util::stringStartsWith(arg, "@")) {
- const std::string path = arg.substr(1, arg.size() - 1);
- std::string error;
- if (!file::appendArgsFromFile(path, &options.overlayFiles, &error)) {
- context.getDiagnostics()->error(DiagMessage(path) << error);
- return 1;
- }
- } else {
- options.overlayFiles.push_back(arg);
- }
+ // Clear the version that can be automatically added.
+ preferred_density_config.sdkVersion = 0;
+
+ if (preferred_density_config.diff(ConfigDescription::DefaultConfig()) !=
+ ConfigDescription::CONFIG_DENSITY) {
+ context.GetDiagnostics()->Error(
+ DiagMessage() << "invalid preferred density '"
+ << preferred_density.value() << "'. "
+ << "Preferred density must only be a density value");
+ return 1;
}
+ options.table_splitter_options.preferred_density =
+ preferred_density_config.density;
+ }
- if (verbose) {
- context.setVerbose(verbose);
+ if (!options.static_lib && stable_id_file_path) {
+ if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path.value(),
+ &options.stable_id_map)) {
+ return 1;
}
+ }
- // Populate the set of extra packages for which to generate R.java.
- for (std::string& extraPackage : extraJavaPackages) {
- // A given package can actually be a colon separated list of packages.
- for (StringPiece package : util::split(extraPackage, ':')) {
- options.extraJavaPackages.insert(package.toString());
- }
+ // Populate some default no-compress extensions that are already compressed.
+ options.extensions_to_not_compress.insert(
+ {".jpg", ".jpeg", ".png", ".gif", ".wav", ".mp2", ".mp3", ".ogg",
+ ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl",
+ ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2",
+ ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"});
+
+ // Parse the split parameters.
+ for (const std::string& split_arg : split_args) {
+ options.split_paths.push_back({});
+ options.split_constraints.push_back({});
+ if (!ParseSplitParameter(split_arg, context.GetDiagnostics(),
+ &options.split_paths.back(),
+ &options.split_constraints.back())) {
+ return 1;
}
+ }
- if (productList) {
- for (StringPiece product : util::tokenize(productList.value(), ',')) {
- if (product != "" && product != "default") {
- options.products.insert(product.toString());
- }
- }
- }
+ // Turn off auto versioning for static-libs.
+ if (options.static_lib) {
+ options.no_auto_version = true;
+ options.no_version_vectors = true;
+ }
- AxisConfigFilter filter;
- if (configs) {
- for (const StringPiece& configStr : util::tokenize(configs.value(), ',')) {
- ConfigDescription config;
- LocaleValue lv;
- if (lv.initFromFilterString(configStr)) {
- lv.writeTo(&config);
- } else if (!ConfigDescription::parse(configStr, &config)) {
- context.getDiagnostics()->error(
- DiagMessage() << "invalid config '" << configStr << "' for -c option");
- return 1;
- }
-
- if (config.density != 0) {
- context.getDiagnostics()->warn(
- DiagMessage() << "ignoring density '" << config << "' for -c option");
- } else {
- filter.addConfig(config);
- }
- }
-
- options.tableSplitterOptions.configFilter = &filter;
- }
-
- if (preferredDensity) {
- ConfigDescription preferredDensityConfig;
- if (!ConfigDescription::parse(preferredDensity.value(), &preferredDensityConfig)) {
- context.getDiagnostics()->error(DiagMessage() << "invalid density '"
- << preferredDensity.value()
- << "' for --preferred-density option");
- return 1;
- }
-
- // Clear the version that can be automatically added.
- preferredDensityConfig.sdkVersion = 0;
-
- if (preferredDensityConfig.diff(ConfigDescription::defaultConfig())
- != ConfigDescription::CONFIG_DENSITY) {
- context.getDiagnostics()->error(DiagMessage() << "invalid preferred density '"
- << preferredDensity.value() << "'. "
- << "Preferred density must only be a density value");
- return 1;
- }
- options.tableSplitterOptions.preferredDensity = preferredDensityConfig.density;
- }
-
- if (!options.staticLib && stableIdFilePath) {
- if (!loadStableIdMap(context.getDiagnostics(), stableIdFilePath.value(),
- &options.stableIdMap)) {
- return 1;
- }
- }
-
- // Populate some default no-compress extensions that are already compressed.
- options.extensionsToNotCompress.insert({
- ".jpg", ".jpeg", ".png", ".gif",
- ".wav", ".mp2", ".mp3", ".ogg", ".aac",
- ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
- ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
- ".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;
- options.noVersionVectors = true;
- }
-
- LinkCommand cmd(&context, options);
- return cmd.run(argList);
+ LinkCommand cmd(&context, options);
+ return cmd.Run(arg_list);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index ce455da..4687d2c 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -17,12 +17,15 @@
#ifndef AAPT_LINKER_LINKERS_H
#define AAPT_LINKER_LINKERS_H
+#include <set>
+#include <unordered_set>
+
+#include "android-base/macros.h"
+
#include "Resource.h"
#include "process/IResourceTableConsumer.h"
#include "xml/XmlDom.h"
-#include <set>
-
namespace aapt {
class ResourceTable;
@@ -30,106 +33,174 @@
struct ConfigDescription;
/**
- * Defines the location in which a value exists. This determines visibility of other
- * package's private symbols.
+ * Defines the location in which a value exists. This determines visibility of
+ * other package's private symbols.
*/
struct CallSite {
- ResourceNameRef resource;
+ ResourceNameRef resource;
};
/**
- * Determines whether a versioned resource should be created. If a versioned resource already
- * exists, it takes precedence.
+ * Determines whether a versioned resource should be created. If a versioned
+ * resource already exists, it takes precedence.
*/
-bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
- const int sdkVersionToGenerate);
+bool ShouldGenerateVersionedResource(const ResourceEntry* entry,
+ const ConfigDescription& config,
+ const int sdk_version_to_generate);
class AutoVersioner : public IResourceTableConsumer {
-public:
- bool consume(IAaptContext* context, ResourceTable* table) override;
-};
+ public:
+ AutoVersioner() = default;
-class XmlAutoVersioner : public IXmlResourceConsumer {
-public:
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutoVersioner);
};
class VersionCollapser : public IResourceTableConsumer {
-public:
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ public:
+ VersionCollapser() = default;
+
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(VersionCollapser);
};
/**
* Removes duplicated key-value entries from dominated resources.
*/
class ResourceDeduper : public IResourceTableConsumer {
-public:
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ public:
+ ResourceDeduper() = default;
+
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceDeduper);
};
/**
- * If any attribute resource values are defined as public, this consumer will move all private
- * attribute resource values to a private ^private-attr type, avoiding backwards compatibility
+ * If any attribute resource values are defined as public, this consumer will
+ * move all private
+ * attribute resource values to a private ^private-attr type, avoiding backwards
+ * compatibility
* issues with new apps running on old platforms.
*
- * The Android platform ignores resource attributes it doesn't recognize, so an app developer can
- * use new attributes in their layout XML files without worrying about versioning. This assumption
- * actually breaks on older platforms. OEMs may add private attributes that are used internally.
- * AAPT originally assigned all private attributes IDs immediately proceeding the public attributes'
+ * The Android platform ignores resource attributes it doesn't recognize, so an
+ * app developer can
+ * use new attributes in their layout XML files without worrying about
+ * versioning. This assumption
+ * actually breaks on older platforms. OEMs may add private attributes that are
+ * used internally.
+ * AAPT originally assigned all private attributes IDs immediately proceeding
+ * the public attributes'
* IDs.
*
- * This means that on a newer Android platform, an ID previously assigned to a private attribute
+ * This means that on a newer Android platform, an ID previously assigned to a
+ * private attribute
* may end up assigned to a public attribute.
*
- * App developers assume using the newer attribute is safe on older platforms because it will
- * be ignored. Instead, the platform thinks the new attribute is an older, private attribute and
- * will interpret it as such. This leads to unintended styling and exceptions thrown due to
+ * App developers assume using the newer attribute is safe on older platforms
+ * because it will
+ * be ignored. Instead, the platform thinks the new attribute is an older,
+ * private attribute and
+ * will interpret it as such. This leads to unintended styling and exceptions
+ * thrown due to
* unexpected types.
*
- * By moving the private attributes to a completely different type, this ID conflict will never
+ * By moving the private attributes to a completely different type, this ID
+ * conflict will never
* occur.
*/
-struct PrivateAttributeMover : public IResourceTableConsumer {
- bool consume(IAaptContext* context, ResourceTable* table) override;
+class PrivateAttributeMover : public IResourceTableConsumer {
+ public:
+ PrivateAttributeMover() = default;
+
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrivateAttributeMover);
+};
+
+class ResourceConfigValue;
+
+class ProductFilter : public IResourceTableConsumer {
+ public:
+ using ResourceConfigValueIter =
+ std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
+
+ explicit ProductFilter(std::unordered_set<std::string> products)
+ : products_(products) {}
+
+ ResourceConfigValueIter SelectProductToKeep(
+ const ResourceNameRef& name, const ResourceConfigValueIter begin,
+ const ResourceConfigValueIter end, IDiagnostics* diag);
+
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+ std::unordered_set<std::string> products_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProductFilter);
+};
+
+class XmlAutoVersioner : public IXmlResourceConsumer {
+ public:
+ XmlAutoVersioner() = default;
+
+ bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XmlAutoVersioner);
};
/**
* Removes namespace nodes and URI information from the XmlResource.
*
- * Once an XmlResource is processed by this consumer, it is no longer able to have its attributes
- * parsed. As such, this XmlResource must have already been processed by XmlReferenceLinker.
+ * Once an XmlResource is processed by this consumer, it is no longer able to
+ * have its attributes
+ * parsed. As such, this XmlResource must have already been processed by
+ * XmlReferenceLinker.
*/
class XmlNamespaceRemover : public IXmlResourceConsumer {
-private:
- bool mKeepUris;
+ public:
+ explicit XmlNamespaceRemover(bool keep_uris = false)
+ : keep_uris_(keep_uris){};
-public:
- XmlNamespaceRemover(bool keepUris = false) : mKeepUris(keepUris) {
- };
+ bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XmlNamespaceRemover);
+
+ bool keep_uris_;
};
/**
- * Resolves attributes in the XmlResource and compiles string values to resource values.
+ * Resolves attributes in the XmlResource and compiles string values to resource
+ * values.
* Once an XmlResource is processed by this linker, it is ready to be flattened.
*/
class XmlReferenceLinker : public IXmlResourceConsumer {
-private:
- std::set<int> mSdkLevelsFound;
+ public:
+ XmlReferenceLinker() = default;
-public:
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
- /**
- * Once the XmlResource has been consumed, this returns the various SDK levels in which
- * framework attributes used within the XML document were defined.
- */
- inline const std::set<int>& getSdkLevels() const {
- return mSdkLevelsFound;
- }
+ /**
+ * Once the XmlResource has been consumed, this returns the various SDK levels
+ * in which
+ * framework attributes used within the XML document were defined.
+ */
+ inline const std::set<int>& sdk_levels() const { return sdk_levels_found_; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XmlReferenceLinker);
+
+ std::set<int> sdk_levels_found_;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_LINKER_LINKERS_H */
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 45f5acd..a418fc8 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -14,306 +14,326 @@
* limitations under the License.
*/
-#include "ResourceUtils.h"
#include "link/ManifestFixer.h"
+
+#include <unordered_set>
+
+#include "android-base/logging.h"
+
+#include "ResourceUtils.h"
#include "util/Util.h"
#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"
-#include <unordered_set>
-
namespace aapt {
/**
- * This is how PackageManager builds class names from AndroidManifest.xml entries.
+ * This is how PackageManager builds class names from AndroidManifest.xml
+ * entries.
*/
-static bool nameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
+static bool NameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
SourcePathDiagnostics* diag) {
- // We allow unqualified class names (ie: .HelloActivity)
- // Since we don't know the package name, we can just make a fake one here and
- // the test will be identical as long as the real package name is valid too.
- Maybe<std::string> fullyQualifiedClassName =
- util::getFullyQualifiedClassName("a", attr->value);
+ // We allow unqualified class names (ie: .HelloActivity)
+ // Since we don't know the package name, we can just make a fake one here and
+ // the test will be identical as long as the real package name is valid too.
+ Maybe<std::string> fully_qualified_class_name =
+ util::GetFullyQualifiedClassName("a", attr->value);
- StringPiece qualifiedClassName = fullyQualifiedClassName
- ? fullyQualifiedClassName.value() : attr->value;
+ StringPiece qualified_class_name = fully_qualified_class_name
+ ? fully_qualified_class_name.value()
+ : attr->value;
- if (!util::isJavaClassName(qualifiedClassName)) {
- diag->error(DiagMessage(el->lineNumber)
- << "attribute 'android:name' in <"
- << el->name << "> tag must be a valid Java class name");
- return false;
- }
- return true;
-}
-
-static bool optionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
- if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "name")) {
- return nameIsJavaClassName(el, attr, diag);
- }
- return true;
-}
-
-static bool requiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
- if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "name")) {
- return nameIsJavaClassName(el, attr, diag);
- }
- diag->error(DiagMessage(el->lineNumber)
- << "<" << el->name << "> is missing attribute 'android:name'");
+ if (!util::IsJavaClassName(qualified_class_name)) {
+ diag->Error(DiagMessage(el->line_number)
+ << "attribute 'android:name' in <" << el->name
+ << "> tag must be a valid Java class name");
return false;
+ }
+ return true;
}
-static bool verifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
- xml::Attribute* attr = el->findAttribute({}, "package");
- if (!attr) {
- diag->error(DiagMessage(el->lineNumber) << "<manifest> tag is missing 'package' attribute");
- return false;
- } else if (ResourceUtils::isReference(attr->value)) {
- diag->error(DiagMessage(el->lineNumber)
- << "attribute 'package' in <manifest> tag must not be a reference");
- return false;
- } else if (!util::isJavaPackageName(attr->value)) {
- diag->error(DiagMessage(el->lineNumber)
- << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
- << attr->value << "'");
- return false;
- }
- return true;
+static bool OptionalNameIsJavaClassName(xml::Element* el,
+ SourcePathDiagnostics* diag) {
+ if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) {
+ return NameIsJavaClassName(el, attr, diag);
+ }
+ return true;
+}
+
+static bool RequiredNameIsJavaClassName(xml::Element* el,
+ SourcePathDiagnostics* diag) {
+ if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) {
+ return NameIsJavaClassName(el, attr, diag);
+ }
+ diag->Error(DiagMessage(el->line_number)
+ << "<" << el->name << "> is missing attribute 'android:name'");
+ return false;
+}
+
+static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
+ xml::Attribute* attr = el->FindAttribute({}, "package");
+ if (!attr) {
+ diag->Error(DiagMessage(el->line_number)
+ << "<manifest> tag is missing 'package' attribute");
+ return false;
+ } else if (ResourceUtils::IsReference(attr->value)) {
+ diag->Error(
+ DiagMessage(el->line_number)
+ << "attribute 'package' in <manifest> tag must not be a reference");
+ return false;
+ } else if (!util::IsJavaPackageName(attr->value)) {
+ diag->Error(DiagMessage(el->line_number)
+ << "attribute 'package' in <manifest> tag is not a valid Java "
+ "package name: '"
+ << attr->value << "'");
+ return false;
+ }
+ return true;
}
/**
- * The coreApp attribute in <manifest> is not a regular AAPT attribute, so type checking on it
- * is manual.
+ * The coreApp attribute in <manifest> is not a regular AAPT attribute, so type
+ * checking on it is manual.
*/
-static bool fixCoreAppAttribute(xml::Element* el, SourcePathDiagnostics* diag) {
- if (xml::Attribute* attr = el->findAttribute("", "coreApp")) {
- std::unique_ptr<BinaryPrimitive> result = ResourceUtils::tryParseBool(attr->value);
- if (!result) {
- diag->error(DiagMessage(el->lineNumber) << "attribute coreApp must be a boolean");
- return false;
- }
- attr->compiledValue = std::move(result);
+static bool FixCoreAppAttribute(xml::Element* el, SourcePathDiagnostics* diag) {
+ if (xml::Attribute* attr = el->FindAttribute("", "coreApp")) {
+ std::unique_ptr<BinaryPrimitive> result =
+ ResourceUtils::TryParseBool(attr->value);
+ if (!result) {
+ diag->Error(DiagMessage(el->line_number)
+ << "attribute coreApp must be a boolean");
+ return false;
}
- return true;
+ attr->compiled_value = std::move(result);
+ }
+ return true;
}
-bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) {
- // First verify some options.
- if (mOptions.renameManifestPackage) {
- if (!util::isJavaPackageName(mOptions.renameManifestPackage.value())) {
- diag->error(DiagMessage() << "invalid manifest package override '"
- << mOptions.renameManifestPackage.value() << "'");
- return false;
- }
+bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
+ IDiagnostics* diag) {
+ // First verify some options.
+ if (options_.rename_manifest_package) {
+ if (!util::IsJavaPackageName(options_.rename_manifest_package.value())) {
+ diag->Error(DiagMessage() << "invalid manifest package override '"
+ << options_.rename_manifest_package.value()
+ << "'");
+ return false;
+ }
+ }
+
+ if (options_.rename_instrumentation_target_package) {
+ if (!util::IsJavaPackageName(
+ options_.rename_instrumentation_target_package.value())) {
+ diag->Error(DiagMessage()
+ << "invalid instrumentation target package override '"
+ << options_.rename_instrumentation_target_package.value()
+ << "'");
+ return false;
+ }
+ }
+
+ // Common intent-filter actions.
+ xml::XmlNodeAction intent_filter_action;
+ intent_filter_action["action"];
+ intent_filter_action["category"];
+ intent_filter_action["data"];
+
+ // Common meta-data actions.
+ xml::XmlNodeAction meta_data_action;
+
+ // Manifest actions.
+ xml::XmlNodeAction& manifest_action = (*executor)["manifest"];
+ manifest_action.Action(VerifyManifest);
+ manifest_action.Action(FixCoreAppAttribute);
+ manifest_action.Action([&](xml::Element* el) -> bool {
+ if (options_.version_name_default) {
+ if (el->FindAttribute(xml::kSchemaAndroid, "versionName") == nullptr) {
+ el->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "versionName",
+ options_.version_name_default.value()});
+ }
}
- if (mOptions.renameInstrumentationTargetPackage) {
- if (!util::isJavaPackageName(mOptions.renameInstrumentationTargetPackage.value())) {
- diag->error(DiagMessage() << "invalid instrumentation target package override '"
- << mOptions.renameInstrumentationTargetPackage.value() << "'");
- return false;
- }
+ if (options_.version_code_default) {
+ if (el->FindAttribute(xml::kSchemaAndroid, "versionCode") == nullptr) {
+ el->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "versionCode",
+ options_.version_code_default.value()});
+ }
}
-
- // Common intent-filter actions.
- xml::XmlNodeAction intentFilterAction;
- intentFilterAction["action"];
- intentFilterAction["category"];
- intentFilterAction["data"];
-
- // Common meta-data actions.
- xml::XmlNodeAction metaDataAction;
-
- // Manifest actions.
- xml::XmlNodeAction& manifestAction = (*executor)["manifest"];
- manifestAction.action(verifyManifest);
- manifestAction.action(fixCoreAppAttribute);
- manifestAction.action([&](xml::Element* el) -> bool {
- if (mOptions.versionNameDefault) {
- if (el->findAttribute(xml::kSchemaAndroid, "versionName") == nullptr) {
- el->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid,
- "versionName",
- mOptions.versionNameDefault.value() });
- }
- }
-
- if (mOptions.versionCodeDefault) {
- if (el->findAttribute(xml::kSchemaAndroid, "versionCode") == nullptr) {
- el->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid,
- "versionCode",
- mOptions.versionCodeDefault.value() });
- }
- }
- return true;
- });
-
- // Meta tags.
- manifestAction["eat-comment"];
-
- // Uses-sdk actions.
- manifestAction["uses-sdk"].action([&](xml::Element* el) -> bool {
- if (mOptions.minSdkVersionDefault &&
- el->findAttribute(xml::kSchemaAndroid, "minSdkVersion") == nullptr) {
- // There was no minSdkVersion defined and we have a default to assign.
- el->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid, "minSdkVersion",
- mOptions.minSdkVersionDefault.value() });
- }
-
- if (mOptions.targetSdkVersionDefault &&
- el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion") == nullptr) {
- // There was no targetSdkVersion defined and we have a default to assign.
- el->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid, "targetSdkVersion",
- mOptions.targetSdkVersionDefault.value() });
- }
- return true;
- });
-
- // Instrumentation actions.
- manifestAction["instrumentation"].action([&](xml::Element* el) -> bool {
- if (!mOptions.renameInstrumentationTargetPackage) {
- return true;
- }
-
- if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "targetPackage")) {
- attr->value = mOptions.renameInstrumentationTargetPackage.value();
- }
- return true;
- });
-
- manifestAction["original-package"];
- manifestAction["protected-broadcast"];
- manifestAction["uses-permission"];
- manifestAction["permission"];
- manifestAction["permission-tree"];
- manifestAction["permission-group"];
-
- manifestAction["uses-configuration"];
- manifestAction["uses-feature"];
- manifestAction["supports-screens"];
-
- manifestAction["compatible-screens"];
- manifestAction["compatible-screens"]["screen"];
-
- manifestAction["supports-gl-texture"];
-
- // Application actions.
- xml::XmlNodeAction& applicationAction = manifestAction["application"];
- applicationAction.action(optionalNameIsJavaClassName);
-
- // Uses library actions.
- applicationAction["uses-library"];
-
- // Meta-data.
- applicationAction["meta-data"] = metaDataAction;
-
- // Activity actions.
- applicationAction["activity"].action(requiredNameIsJavaClassName);
- applicationAction["activity"]["intent-filter"] = intentFilterAction;
- applicationAction["activity"]["meta-data"] = metaDataAction;
-
- // Activity alias actions.
- applicationAction["activity-alias"]["intent-filter"] = intentFilterAction;
- applicationAction["activity-alias"]["meta-data"] = metaDataAction;
-
- // Service actions.
- applicationAction["service"].action(requiredNameIsJavaClassName);
- applicationAction["service"]["intent-filter"] = intentFilterAction;
- applicationAction["service"]["meta-data"] = metaDataAction;
-
- // Receiver actions.
- applicationAction["receiver"].action(requiredNameIsJavaClassName);
- applicationAction["receiver"]["intent-filter"] = intentFilterAction;
- applicationAction["receiver"]["meta-data"] = metaDataAction;
-
- // Provider actions.
- applicationAction["provider"].action(requiredNameIsJavaClassName);
- applicationAction["provider"]["intent-filter"] = intentFilterAction;
- applicationAction["provider"]["meta-data"] = metaDataAction;
- applicationAction["provider"]["grant-uri-permissions"];
- applicationAction["provider"]["path-permissions"];
-
return true;
+ });
+
+ // Meta tags.
+ manifest_action["eat-comment"];
+
+ // Uses-sdk actions.
+ manifest_action["uses-sdk"].Action([&](xml::Element* el) -> bool {
+ if (options_.min_sdk_version_default &&
+ el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion") == nullptr) {
+ // There was no minSdkVersion defined and we have a default to assign.
+ el->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "minSdkVersion",
+ options_.min_sdk_version_default.value()});
+ }
+
+ if (options_.target_sdk_version_default &&
+ el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion") == nullptr) {
+ // There was no targetSdkVersion defined and we have a default to assign.
+ el->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "targetSdkVersion",
+ options_.target_sdk_version_default.value()});
+ }
+ return true;
+ });
+
+ // Instrumentation actions.
+ manifest_action["instrumentation"].Action([&](xml::Element* el) -> bool {
+ if (!options_.rename_instrumentation_target_package) {
+ return true;
+ }
+
+ if (xml::Attribute* attr =
+ el->FindAttribute(xml::kSchemaAndroid, "targetPackage")) {
+ attr->value = options_.rename_instrumentation_target_package.value();
+ }
+ return true;
+ });
+
+ manifest_action["original-package"];
+ manifest_action["protected-broadcast"];
+ manifest_action["uses-permission"];
+ manifest_action["permission"];
+ manifest_action["permission-tree"];
+ manifest_action["permission-group"];
+
+ manifest_action["uses-configuration"];
+ manifest_action["uses-feature"];
+ manifest_action["supports-screens"];
+
+ manifest_action["compatible-screens"];
+ manifest_action["compatible-screens"]["screen"];
+
+ manifest_action["supports-gl-texture"];
+
+ // Application actions.
+ xml::XmlNodeAction& application_action = manifest_action["application"];
+ application_action.Action(OptionalNameIsJavaClassName);
+
+ // Uses library actions.
+ application_action["uses-library"];
+
+ // Meta-data.
+ application_action["meta-data"] = meta_data_action;
+
+ // Activity actions.
+ application_action["activity"].Action(RequiredNameIsJavaClassName);
+ application_action["activity"]["intent-filter"] = intent_filter_action;
+ application_action["activity"]["meta-data"] = meta_data_action;
+
+ // Activity alias actions.
+ application_action["activity-alias"]["intent-filter"] = intent_filter_action;
+ application_action["activity-alias"]["meta-data"] = meta_data_action;
+
+ // Service actions.
+ application_action["service"].Action(RequiredNameIsJavaClassName);
+ application_action["service"]["intent-filter"] = intent_filter_action;
+ application_action["service"]["meta-data"] = meta_data_action;
+
+ // Receiver actions.
+ application_action["receiver"].Action(RequiredNameIsJavaClassName);
+ application_action["receiver"]["intent-filter"] = intent_filter_action;
+ application_action["receiver"]["meta-data"] = meta_data_action;
+
+ // Provider actions.
+ application_action["provider"].Action(RequiredNameIsJavaClassName);
+ application_action["provider"]["intent-filter"] = intent_filter_action;
+ application_action["provider"]["meta-data"] = meta_data_action;
+ application_action["provider"]["grant-uri-permissions"];
+ application_action["provider"]["path-permissions"];
+
+ return true;
}
class FullyQualifiedClassNameVisitor : public xml::Visitor {
-public:
- using xml::Visitor::visit;
+ public:
+ using xml::Visitor::Visit;
- explicit FullyQualifiedClassNameVisitor(const StringPiece& package) : mPackage(package) {
- }
+ explicit FullyQualifiedClassNameVisitor(const StringPiece& package)
+ : package_(package) {}
- void visit(xml::Element* el) override {
- for (xml::Attribute& attr : el->attributes) {
- if (attr.namespaceUri == xml::kSchemaAndroid
- && mClassAttributes.find(attr.name) != mClassAttributes.end()) {
- if (Maybe<std::string> newValue =
- util::getFullyQualifiedClassName(mPackage, attr.value)) {
- attr.value = std::move(newValue.value());
- }
- }
+ void Visit(xml::Element* el) override {
+ for (xml::Attribute& attr : el->attributes) {
+ if (attr.namespace_uri == xml::kSchemaAndroid &&
+ class_attributes_.find(attr.name) != class_attributes_.end()) {
+ if (Maybe<std::string> new_value =
+ util::GetFullyQualifiedClassName(package_, attr.value)) {
+ attr.value = std::move(new_value.value());
}
-
- // Super implementation to iterate over the children.
- xml::Visitor::visit(el);
+ }
}
-private:
- StringPiece mPackage;
- std::unordered_set<StringPiece> mClassAttributes = { "name" };
+ // Super implementation to iterate over the children.
+ xml::Visitor::Visit(el);
+ }
+
+ private:
+ StringPiece package_;
+ std::unordered_set<StringPiece> class_attributes_ = {"name"};
};
-static bool renameManifestPackage(const StringPiece& packageOverride, xml::Element* manifestEl) {
- xml::Attribute* attr = manifestEl->findAttribute({}, "package");
+static bool RenameManifestPackage(const StringPiece& package_override,
+ xml::Element* manifest_el) {
+ xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
- // We've already verified that the manifest element is present, with a package name specified.
- assert(attr);
+ // We've already verified that the manifest element is present, with a package
+ // name specified.
+ CHECK(attr != nullptr);
- std::string originalPackage = std::move(attr->value);
- attr->value = packageOverride.toString();
+ std::string original_package = std::move(attr->value);
+ attr->value = package_override.ToString();
- FullyQualifiedClassNameVisitor visitor(originalPackage);
- manifestEl->accept(&visitor);
- return true;
+ FullyQualifiedClassNameVisitor visitor(original_package);
+ manifest_el->Accept(&visitor);
+ return true;
}
-bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
- xml::Element* root = xml::findRootElement(doc->root.get());
- if (!root || !root->namespaceUri.empty() || root->name != "manifest") {
- context->getDiagnostics()->error(DiagMessage(doc->file.source)
- << "root tag must be <manifest>");
- return false;
- }
+bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) {
+ xml::Element* root = xml::FindRootElement(doc->root.get());
+ if (!root || !root->namespace_uri.empty() || root->name != "manifest") {
+ context->GetDiagnostics()->Error(DiagMessage(doc->file.source)
+ << "root tag must be <manifest>");
+ return false;
+ }
- if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)
- && root->findChild({}, "uses-sdk") == nullptr) {
- // Auto insert a <uses-sdk> element.
- std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
- usesSdk->name = "uses-sdk";
- root->addChild(std::move(usesSdk));
- }
+ if ((options_.min_sdk_version_default ||
+ options_.target_sdk_version_default) &&
+ root->FindChild({}, "uses-sdk") == nullptr) {
+ // Auto insert a <uses-sdk> element.
+ std::unique_ptr<xml::Element> uses_sdk = util::make_unique<xml::Element>();
+ uses_sdk->name = "uses-sdk";
+ root->AddChild(std::move(uses_sdk));
+ }
- xml::XmlActionExecutor executor;
- if (!buildRules(&executor, context->getDiagnostics())) {
- return false;
- }
+ xml::XmlActionExecutor executor;
+ if (!BuildRules(&executor, context->GetDiagnostics())) {
+ return false;
+ }
- if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(),
- doc)) {
- return false;
- }
+ if (!executor.Execute(xml::XmlActionExecutorPolicy::kWhitelist,
+ context->GetDiagnostics(), doc)) {
+ return false;
+ }
- if (mOptions.renameManifestPackage) {
- // Rename manifest package outside of the XmlActionExecutor.
- // We need to extract the old package name and FullyQualify all class names.
- if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) {
- return false;
- }
+ if (options_.rename_manifest_package) {
+ // Rename manifest package outside of the XmlActionExecutor.
+ // We need to extract the old package name and FullyQualify all class names.
+ if (!RenameManifestPackage(options_.rename_manifest_package.value(),
+ root)) {
+ return false;
}
- return true;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 2e81266..470f65e 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -17,22 +17,24 @@
#ifndef AAPT_LINK_MANIFESTFIXER_H
#define AAPT_LINK_MANIFESTFIXER_H
+#include <string>
+
+#include "android-base/macros.h"
+
#include "process/IResourceTableConsumer.h"
#include "util/Maybe.h"
#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"
-#include <string>
-
namespace aapt {
struct ManifestFixerOptions {
- Maybe<std::string> minSdkVersionDefault;
- Maybe<std::string> targetSdkVersionDefault;
- Maybe<std::string> renameManifestPackage;
- Maybe<std::string> renameInstrumentationTargetPackage;
- Maybe<std::string> versionNameDefault;
- Maybe<std::string> versionCodeDefault;
+ Maybe<std::string> min_sdk_version_default;
+ Maybe<std::string> target_sdk_version_default;
+ Maybe<std::string> rename_manifest_package;
+ Maybe<std::string> rename_instrumentation_target_package;
+ Maybe<std::string> version_name_default;
+ Maybe<std::string> version_code_default;
};
/**
@@ -40,18 +42,20 @@
* where specified with ManifestFixerOptions.
*/
class ManifestFixer : public IXmlResourceConsumer {
-public:
- explicit ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) {
- }
+ public:
+ explicit ManifestFixer(const ManifestFixerOptions& options)
+ : options_(options) {}
- bool consume(IAaptContext* context, xml::XmlResource* doc) override;
+ bool Consume(IAaptContext* context, xml::XmlResource* doc) override;
-private:
- bool buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ManifestFixer);
- ManifestFixerOptions mOptions;
+ bool BuildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag);
+
+ ManifestFixerOptions options_;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_LINK_MANIFESTFIXER_H */
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 16ab9ab..0e29ba6 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -15,262 +15,280 @@
*/
#include "link/ManifestFixer.h"
-#include "test/Builders.h"
-#include "test/Context.h"
-#include <gtest/gtest.h>
+#include "test/Test.h"
namespace aapt {
struct ManifestFixerTest : public ::testing::Test {
- std::unique_ptr<IAaptContext> mContext;
+ std::unique_ptr<IAaptContext> mContext;
- void SetUp() override {
- mContext = test::ContextBuilder()
- .setCompilationPackage("android")
- .setPackageId(0x01)
- .setNameManglerPolicy(NameManglerPolicy{ "android" })
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addSymbol("android:attr/package", ResourceId(0x01010000),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_STRING)
- .build())
- .addSymbol("android:attr/minSdkVersion", ResourceId(0x01010001),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_STRING |
- android::ResTable_map::TYPE_INTEGER)
- .build())
- .addSymbol("android:attr/targetSdkVersion", ResourceId(0x01010002),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_STRING |
- android::ResTable_map::TYPE_INTEGER)
- .build())
- .addSymbol("android:string/str", ResourceId(0x01060000))
- .build())
- .build();
- }
+ void SetUp() override {
+ mContext =
+ test::ContextBuilder()
+ .SetCompilationPackage("android")
+ .SetPackageId(0x01)
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .AddSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .AddSymbol(
+ "android:attr/package", ResourceId(0x01010000),
+ test::AttributeBuilder()
+ .SetTypeMask(android::ResTable_map::TYPE_STRING)
+ .Build())
+ .AddSymbol(
+ "android:attr/minSdkVersion", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .SetTypeMask(android::ResTable_map::TYPE_STRING |
+ android::ResTable_map::TYPE_INTEGER)
+ .Build())
+ .AddSymbol(
+ "android:attr/targetSdkVersion", ResourceId(0x01010002),
+ test::AttributeBuilder()
+ .SetTypeMask(android::ResTable_map::TYPE_STRING |
+ android::ResTable_map::TYPE_INTEGER)
+ .Build())
+ .AddSymbol("android:string/str", ResourceId(0x01060000))
+ .Build())
+ .Build();
+ }
- std::unique_ptr<xml::XmlResource> verify(const StringPiece& str) {
- return verifyWithOptions(str, {});
- }
+ std::unique_ptr<xml::XmlResource> Verify(const StringPiece& str) {
+ return VerifyWithOptions(str, {});
+ }
- std::unique_ptr<xml::XmlResource> verifyWithOptions(const StringPiece& str,
- const ManifestFixerOptions& options) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(str);
- ManifestFixer fixer(options);
- if (fixer.consume(mContext.get(), doc.get())) {
- return doc;
- }
- return {};
+ std::unique_ptr<xml::XmlResource> VerifyWithOptions(
+ const StringPiece& str, const ManifestFixerOptions& options) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(str);
+ ManifestFixer fixer(options);
+ if (fixer.Consume(mContext.get(), doc.get())) {
+ return doc;
}
+ return {};
+ }
};
TEST_F(ManifestFixerTest, EnsureManifestIsRootTag) {
- EXPECT_EQ(nullptr, verify("<other-tag />"));
- EXPECT_EQ(nullptr, verify("<ns:manifest xmlns:ns=\"com\" />"));
- EXPECT_NE(nullptr, verify("<manifest package=\"android\"></manifest>"));
+ EXPECT_EQ(nullptr, Verify("<other-tag />"));
+ EXPECT_EQ(nullptr, Verify("<ns:manifest xmlns:ns=\"com\" />"));
+ EXPECT_NE(nullptr, Verify("<manifest package=\"android\"></manifest>"));
}
TEST_F(ManifestFixerTest, EnsureManifestHasPackage) {
- EXPECT_NE(nullptr, verify("<manifest package=\"android\" />"));
- EXPECT_NE(nullptr, verify("<manifest package=\"com.android\" />"));
- EXPECT_NE(nullptr, verify("<manifest package=\"com.android.google\" />"));
- EXPECT_EQ(nullptr, verify("<manifest package=\"com.android.google.Class$1\" />"));
- EXPECT_EQ(nullptr,
- verify("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" "
- "android:package=\"com.android\" />"));
- EXPECT_EQ(nullptr, verify("<manifest package=\"@string/str\" />"));
+ EXPECT_NE(nullptr, Verify("<manifest package=\"android\" />"));
+ EXPECT_NE(nullptr, Verify("<manifest package=\"com.android\" />"));
+ EXPECT_NE(nullptr, Verify("<manifest package=\"com.android.google\" />"));
+ EXPECT_EQ(nullptr,
+ Verify("<manifest package=\"com.android.google.Class$1\" />"));
+ EXPECT_EQ(nullptr, Verify("<manifest "
+ "xmlns:android=\"http://schemas.android.com/apk/"
+ "res/android\" "
+ "android:package=\"com.android\" />"));
+ EXPECT_EQ(nullptr, Verify("<manifest package=\"@string/str\" />"));
}
TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
- ManifestFixerOptions options = { std::string("8"), std::string("22") };
+ ManifestFixerOptions options = {std::string("8"), std::string("22")};
- std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
- </manifest>)EOF", options);
- ASSERT_NE(nullptr, doc);
+ </manifest>)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- xml::Element* el;
- xml::Attribute* attr;
+ xml::Element* el;
+ xml::Attribute* attr;
- el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
- el = el->findChild({}, "uses-sdk");
- ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("7", attr->value);
- attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("21", attr->value);
+ el = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
+ el = el->FindChild({}, "uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("7", attr->value);
+ attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("21", attr->value);
- doc = verifyWithOptions(R"EOF(
+ doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-sdk android:targetSdkVersion="21" />
- </manifest>)EOF", options);
- ASSERT_NE(nullptr, doc);
+ </manifest>)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
- el = el->findChild({}, "uses-sdk");
- ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("8", attr->value);
- attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("21", attr->value);
+ el = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
+ el = el->FindChild({}, "uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("8", attr->value);
+ attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("21", attr->value);
- doc = verifyWithOptions(R"EOF(
+ doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-sdk />
- </manifest>)EOF", options);
- ASSERT_NE(nullptr, doc);
+ </manifest>)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
- el = el->findChild({}, "uses-sdk");
- ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("8", attr->value);
- attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("22", attr->value);
+ el = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
+ el = el->FindChild({}, "uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("8", attr->value);
+ attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("22", attr->value);
- doc = verifyWithOptions(R"EOF(
+ doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android" />)EOF", options);
- ASSERT_NE(nullptr, doc);
+ package="android" />)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
- el = el->findChild({}, "uses-sdk");
- ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("8", attr->value);
- attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("22", attr->value);
+ el = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
+ el = el->FindChild({}, "uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("8", attr->value);
+ attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("22", attr->value);
}
TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
- ManifestFixerOptions options;
- options.renameManifestPackage = std::string("com.android");
+ ManifestFixerOptions options;
+ options.rename_manifest_package = std::string("com.android");
- std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<application android:name=".MainApplication" text="hello">
<activity android:name=".activity.Start" />
<receiver android:name="com.google.android.Receiver" />
</application>
- </manifest>)EOF", options);
- ASSERT_NE(nullptr, doc);
+ </manifest>)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- xml::Element* manifestEl = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, manifestEl);
+ xml::Element* manifestEl = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
- xml::Attribute* attr = nullptr;
+ xml::Attribute* attr = nullptr;
- attr = manifestEl->findAttribute({},"package");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("com.android"), attr->value);
+ attr = manifestEl->FindAttribute({}, "package");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("com.android"), attr->value);
- xml::Element* applicationEl = manifestEl->findChild({}, "application");
- ASSERT_NE(nullptr, applicationEl);
+ xml::Element* applicationEl = manifestEl->FindChild({}, "application");
+ ASSERT_NE(nullptr, applicationEl);
- attr = applicationEl->findAttribute(xml::kSchemaAndroid, "name");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("android.MainApplication"), attr->value);
+ attr = applicationEl->FindAttribute(xml::kSchemaAndroid, "name");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("android.MainApplication"), attr->value);
- attr = applicationEl->findAttribute({}, "text");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("hello"), attr->value);
+ attr = applicationEl->FindAttribute({}, "text");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("hello"), attr->value);
- xml::Element* el;
- el = applicationEl->findChild({}, "activity");
- ASSERT_NE(nullptr, el);
+ xml::Element* el;
+ el = applicationEl->FindChild({}, "activity");
+ ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "name");
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(std::string("android.activity.Start"), attr->value);
+ attr = el->FindAttribute(xml::kSchemaAndroid, "name");
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ(std::string("android.activity.Start"), attr->value);
- el = applicationEl->findChild({}, "receiver");
- ASSERT_NE(nullptr, el);
+ el = applicationEl->FindChild({}, "receiver");
+ ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "name");
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(std::string("com.google.android.Receiver"), attr->value);
+ attr = el->FindAttribute(xml::kSchemaAndroid, "name");
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ(std::string("com.google.android.Receiver"), attr->value);
}
-TEST_F(ManifestFixerTest, RenameManifestInstrumentationPackageAndFullyQualifyTarget) {
- ManifestFixerOptions options;
- options.renameInstrumentationTargetPackage = std::string("com.android");
+TEST_F(ManifestFixerTest,
+ RenameManifestInstrumentationPackageAndFullyQualifyTarget) {
+ ManifestFixerOptions options;
+ options.rename_instrumentation_target_package = std::string("com.android");
- std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<instrumentation android:targetPackage="android" />
- </manifest>)EOF", options);
- ASSERT_NE(nullptr, doc);
+ </manifest>)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- xml::Element* manifestEl = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, manifestEl);
+ xml::Element* manifest_el = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, manifest_el);
- xml::Element* instrumentationEl = manifestEl->findChild({}, "instrumentation");
- ASSERT_NE(nullptr, instrumentationEl);
+ xml::Element* instrumentation_el =
+ manifest_el->FindChild({}, "instrumentation");
+ ASSERT_NE(nullptr, instrumentation_el);
- xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, "targetPackage");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("com.android"), attr->value);
+ xml::Attribute* attr =
+ instrumentation_el->FindAttribute(xml::kSchemaAndroid, "targetPackage");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("com.android"), attr->value);
}
TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
- ManifestFixerOptions options;
- options.versionNameDefault = std::string("Beta");
- options.versionCodeDefault = std::string("0x10000000");
+ ManifestFixerOptions options;
+ options.version_name_default = std::string("Beta");
+ options.version_code_default = std::string("0x10000000");
- std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android" />)EOF", options);
- ASSERT_NE(nullptr, doc);
+ package="android" />)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- xml::Element* manifestEl = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, manifestEl);
+ xml::Element* manifest_el = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, manifest_el);
- xml::Attribute* attr = manifestEl->findAttribute(xml::kSchemaAndroid, "versionName");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("Beta"), attr->value);
+ xml::Attribute* attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("Beta"), attr->value);
- attr = manifestEl->findAttribute(xml::kSchemaAndroid, "versionCode");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("0x10000000"), attr->value);
+ attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("0x10000000"), attr->value);
}
TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) {
- EXPECT_EQ(nullptr, verify("<manifest package=\"android\" coreApp=\"hello\" />"));
- EXPECT_EQ(nullptr, verify("<manifest package=\"android\" coreApp=\"1dp\" />"));
+ EXPECT_EQ(nullptr,
+ Verify("<manifest package=\"android\" coreApp=\"hello\" />"));
+ EXPECT_EQ(nullptr,
+ Verify("<manifest package=\"android\" coreApp=\"1dp\" />"));
- std::unique_ptr<xml::XmlResource> doc =
- verify("<manifest package=\"android\" coreApp=\"true\" />");
- ASSERT_NE(nullptr, doc);
+ std::unique_ptr<xml::XmlResource> doc =
+ Verify("<manifest package=\"android\" coreApp=\"true\" />");
+ ASSERT_NE(nullptr, doc);
- xml::Element* el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
+ xml::Element* el = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
- EXPECT_EQ("manifest", el->name);
+ EXPECT_EQ("manifest", el->name);
- xml::Attribute* attr = el->findAttribute("", "coreApp");
- ASSERT_NE(nullptr, attr);
+ xml::Attribute* attr = el->FindAttribute("", "coreApp");
+ ASSERT_NE(nullptr, attr);
- EXPECT_NE(nullptr, attr->compiledValue);
- EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(attr->compiledValue.get()));
+ EXPECT_NE(nullptr, attr->compiled_value);
+ EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(attr->compiled_value.get()));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/PrivateAttributeMover.cpp b/tools/aapt2/link/PrivateAttributeMover.cpp
index 3c8af4f..cc07a6e 100644
--- a/tools/aapt2/link/PrivateAttributeMover.cpp
+++ b/tools/aapt2/link/PrivateAttributeMover.cpp
@@ -14,67 +14,74 @@
* limitations under the License.
*/
-#include "ResourceTable.h"
#include "link/Linkers.h"
#include <algorithm>
#include <iterator>
+#include "android-base/logging.h"
+
+#include "ResourceTable.h"
+
namespace aapt {
template <typename InputContainer, typename OutputIterator, typename Predicate>
-OutputIterator moveIf(InputContainer& inputContainer, OutputIterator result,
- Predicate pred) {
- const auto last = inputContainer.end();
- auto newEnd = std::find_if(inputContainer.begin(), inputContainer.end(), pred);
- if (newEnd == last) {
- return result;
- }
-
- *result = std::move(*newEnd);
-
- auto first = newEnd;
- ++first;
-
- for (; first != last; ++first) {
- if (bool(pred(*first))) {
- // We want to move this guy
- *result = std::move(*first);
- ++result;
- } else {
- // We want to keep this guy, but we will need to move it up the list to replace
- // missing items.
- *newEnd = std::move(*first);
- ++newEnd;
- }
- }
-
- inputContainer.erase(newEnd, last);
+OutputIterator move_if(InputContainer& input_container, OutputIterator result,
+ Predicate pred) {
+ const auto last = input_container.end();
+ auto new_end =
+ std::find_if(input_container.begin(), input_container.end(), pred);
+ if (new_end == last) {
return result;
-}
+ }
-bool PrivateAttributeMover::consume(IAaptContext* context, ResourceTable* table) {
- for (auto& package : table->packages) {
- ResourceTableType* type = package->findType(ResourceType::kAttr);
- if (!type) {
- continue;
- }
+ *result = std::move(*new_end);
- if (type->symbolStatus.state != SymbolState::kPublic) {
- // No public attributes, so we can safely leave these private attributes where they are.
- return true;
- }
+ auto first = new_end;
+ ++first;
- ResourceTableType* privAttrType = package->findOrCreateType(ResourceType::kAttrPrivate);
- assert(privAttrType->entries.empty());
-
- moveIf(type->entries, std::back_inserter(privAttrType->entries),
- [](const std::unique_ptr<ResourceEntry>& entry) -> bool {
- return entry->symbolStatus.state != SymbolState::kPublic;
- });
- break;
+ for (; first != last; ++first) {
+ if (bool(pred(*first))) {
+ // We want to move this guy
+ *result = std::move(*first);
+ ++result;
+ } else {
+ // We want to keep this guy, but we will need to move it up the list to
+ // replace missing items.
+ *new_end = std::move(*first);
+ ++new_end;
}
- return true;
+ }
+
+ input_container.erase(new_end, last);
+ return result;
}
-} // namespace aapt
+bool PrivateAttributeMover::Consume(IAaptContext* context,
+ ResourceTable* table) {
+ for (auto& package : table->packages) {
+ ResourceTableType* type = package->FindType(ResourceType::kAttr);
+ if (!type) {
+ continue;
+ }
+
+ if (type->symbol_status.state != SymbolState::kPublic) {
+ // No public attributes, so we can safely leave these private attributes
+ // where they are.
+ return true;
+ }
+
+ ResourceTableType* priv_attr_type =
+ package->FindOrCreateType(ResourceType::kAttrPrivate);
+ CHECK(priv_attr_type->entries.empty());
+
+ move_if(type->entries, std::back_inserter(priv_attr_type->entries),
+ [](const std::unique_ptr<ResourceEntry>& entry) -> bool {
+ return entry->symbol_status.state != SymbolState::kPublic;
+ });
+ break;
+ }
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/PrivateAttributeMover_test.cpp b/tools/aapt2/link/PrivateAttributeMover_test.cpp
index c9d1a08..90c4922 100644
--- a/tools/aapt2/link/PrivateAttributeMover_test.cpp
+++ b/tools/aapt2/link/PrivateAttributeMover_test.cpp
@@ -15,61 +15,66 @@
*/
#include "link/Linkers.h"
+
#include "test/Test.h"
namespace aapt {
TEST(PrivateAttributeMoverTest, MovePrivateAttributes) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:attr/publicA")
- .addSimple("android:attr/privateA")
- .addSimple("android:attr/publicB")
- .addSimple("android:attr/privateB")
- .setSymbolState("android:attr/publicA", ResourceId(0x01010000), SymbolState::kPublic)
- .setSymbolState("android:attr/publicB", ResourceId(0x01010000), SymbolState::kPublic)
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddSimple("android:attr/publicA")
+ .AddSimple("android:attr/privateA")
+ .AddSimple("android:attr/publicB")
+ .AddSimple("android:attr/privateB")
+ .SetSymbolState("android:attr/publicA", ResourceId(0x01010000),
+ SymbolState::kPublic)
+ .SetSymbolState("android:attr/publicB", ResourceId(0x01010000),
+ SymbolState::kPublic)
+ .Build();
- PrivateAttributeMover mover;
- ASSERT_TRUE(mover.consume(context.get(), table.get()));
+ PrivateAttributeMover mover;
+ ASSERT_TRUE(mover.Consume(context.get(), table.get()));
- ResourceTablePackage* package = table->findPackage("android");
- ASSERT_NE(package, nullptr);
+ ResourceTablePackage* package = table->FindPackage("android");
+ ASSERT_NE(package, nullptr);
- ResourceTableType* type = package->findType(ResourceType::kAttr);
- ASSERT_NE(type, nullptr);
- ASSERT_EQ(type->entries.size(), 2u);
- EXPECT_NE(type->findEntry("publicA"), nullptr);
- EXPECT_NE(type->findEntry("publicB"), nullptr);
+ ResourceTableType* type = package->FindType(ResourceType::kAttr);
+ ASSERT_NE(type, nullptr);
+ ASSERT_EQ(type->entries.size(), 2u);
+ EXPECT_NE(type->FindEntry("publicA"), nullptr);
+ EXPECT_NE(type->FindEntry("publicB"), nullptr);
- type = package->findType(ResourceType::kAttrPrivate);
- ASSERT_NE(type, nullptr);
- ASSERT_EQ(type->entries.size(), 2u);
- EXPECT_NE(type->findEntry("privateA"), nullptr);
- EXPECT_NE(type->findEntry("privateB"), nullptr);
+ type = package->FindType(ResourceType::kAttrPrivate);
+ ASSERT_NE(type, nullptr);
+ ASSERT_EQ(type->entries.size(), 2u);
+ EXPECT_NE(type->FindEntry("privateA"), nullptr);
+ EXPECT_NE(type->FindEntry("privateB"), nullptr);
}
-TEST(PrivateAttributeMoverTest, LeavePrivateAttributesWhenNoPublicAttributesDefined) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+TEST(PrivateAttributeMoverTest,
+ LeavePrivateAttributesWhenNoPublicAttributesDefined) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:attr/privateA")
- .addSimple("android:attr/privateB")
- .build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/privateA")
+ .AddSimple("android:attr/privateB")
+ .Build();
- PrivateAttributeMover mover;
- ASSERT_TRUE(mover.consume(context.get(), table.get()));
+ PrivateAttributeMover mover;
+ ASSERT_TRUE(mover.Consume(context.get(), table.get()));
- ResourceTablePackage* package = table->findPackage("android");
- ASSERT_NE(package, nullptr);
+ ResourceTablePackage* package = table->FindPackage("android");
+ ASSERT_NE(package, nullptr);
- ResourceTableType* type = package->findType(ResourceType::kAttr);
- ASSERT_NE(type, nullptr);
- ASSERT_EQ(type->entries.size(), 2u);
+ ResourceTableType* type = package->FindType(ResourceType::kAttr);
+ ASSERT_NE(type, nullptr);
+ ASSERT_EQ(type->entries.size(), 2u);
- type = package->findType(ResourceType::kAttrPrivate);
- ASSERT_EQ(type, nullptr);
+ type = package->FindType(ResourceType::kAttrPrivate);
+ ASSERT_EQ(type, nullptr);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter.cpp b/tools/aapt2/link/ProductFilter.cpp
index 8784e89..c1a95ee 100644
--- a/tools/aapt2/link/ProductFilter.cpp
+++ b/tools/aapt2/link/ProductFilter.cpp
@@ -14,105 +14,110 @@
* limitations under the License.
*/
-#include "link/ProductFilter.h"
+#include "link/Linkers.h"
+
+#include "ResourceTable.h"
namespace aapt {
-ProductFilter::ResourceConfigValueIter
-ProductFilter::selectProductToKeep(const ResourceNameRef& name,
- const ResourceConfigValueIter begin,
- const ResourceConfigValueIter end,
- IDiagnostics* diag) {
- ResourceConfigValueIter defaultProductIter = end;
- ResourceConfigValueIter selectedProductIter = end;
+ProductFilter::ResourceConfigValueIter ProductFilter::SelectProductToKeep(
+ const ResourceNameRef& name, const ResourceConfigValueIter begin,
+ const ResourceConfigValueIter end, IDiagnostics* diag) {
+ ResourceConfigValueIter default_product_iter = end;
+ ResourceConfigValueIter selected_product_iter = end;
- for (ResourceConfigValueIter iter = begin; iter != end; ++iter) {
- ResourceConfigValue* configValue = iter->get();
- if (mProducts.find(configValue->product) != mProducts.end()) {
- if (selectedProductIter != end) {
- // We have two possible values for this product!
- diag->error(DiagMessage(configValue->value->getSource())
- << "selection of product '" << configValue->product
- << "' for resource " << name << " is ambiguous");
+ for (ResourceConfigValueIter iter = begin; iter != end; ++iter) {
+ ResourceConfigValue* config_value = iter->get();
+ if (products_.find(config_value->product) != products_.end()) {
+ if (selected_product_iter != end) {
+ // We have two possible values for this product!
+ diag->Error(DiagMessage(config_value->value->GetSource())
+ << "selection of product '" << config_value->product
+ << "' for resource " << name << " is ambiguous");
- ResourceConfigValue* previouslySelectedConfigValue = selectedProductIter->get();
- diag->note(DiagMessage(previouslySelectedConfigValue->value->getSource())
- << "product '" << previouslySelectedConfigValue->product
- << "' is also a candidate");
- return end;
- }
-
- // Select this product.
- selectedProductIter = iter;
- }
-
- if (configValue->product.empty() || configValue->product == "default") {
- if (defaultProductIter != end) {
- // We have two possible default values.
- diag->error(DiagMessage(configValue->value->getSource())
- << "multiple default products defined for resource " << name);
-
- ResourceConfigValue* previouslyDefaultConfigValue = defaultProductIter->get();
- diag->note(DiagMessage(previouslyDefaultConfigValue->value->getSource())
- << "default product also defined here");
- return end;
- }
-
- // Mark the default.
- defaultProductIter = iter;
- }
- }
-
- if (defaultProductIter == end) {
- diag->error(DiagMessage() << "no default product defined for resource " << name);
+ ResourceConfigValue* previously_selected_config_value =
+ selected_product_iter->get();
+ diag->Note(
+ DiagMessage(previously_selected_config_value->value->GetSource())
+ << "product '" << previously_selected_config_value->product
+ << "' is also a candidate");
return end;
+ }
+
+ // Select this product.
+ selected_product_iter = iter;
}
- if (selectedProductIter == end) {
- selectedProductIter = defaultProductIter;
+ if (config_value->product.empty() || config_value->product == "default") {
+ if (default_product_iter != end) {
+ // We have two possible default values.
+ diag->Error(DiagMessage(config_value->value->GetSource())
+ << "multiple default products defined for resource "
+ << name);
+
+ ResourceConfigValue* previously_default_config_value =
+ default_product_iter->get();
+ diag->Note(
+ DiagMessage(previously_default_config_value->value->GetSource())
+ << "default product also defined here");
+ return end;
+ }
+
+ // Mark the default.
+ default_product_iter = iter;
}
- return selectedProductIter;
+ }
+
+ if (default_product_iter == end) {
+ diag->Error(DiagMessage() << "no default product defined for resource "
+ << name);
+ return end;
+ }
+
+ if (selected_product_iter == end) {
+ selected_product_iter = default_product_iter;
+ }
+ return selected_product_iter;
}
-bool ProductFilter::consume(IAaptContext* context, ResourceTable* table) {
- bool error = false;
- for (auto& pkg : table->packages) {
- for (auto& type : pkg->types) {
- for (auto& entry : type->entries) {
- std::vector<std::unique_ptr<ResourceConfigValue>> newValues;
+bool ProductFilter::Consume(IAaptContext* context, ResourceTable* table) {
+ bool error = false;
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ for (auto& entry : type->entries) {
+ std::vector<std::unique_ptr<ResourceConfigValue>> new_values;
- ResourceConfigValueIter iter = entry->values.begin();
- ResourceConfigValueIter startRangeIter = iter;
- while (iter != entry->values.end()) {
- ++iter;
- if (iter == entry->values.end() ||
- (*iter)->config != (*startRangeIter)->config) {
-
- // End of the array, or we saw a different config,
- // so this must be the end of a range of products.
- // Select the product to keep from the set of products defined.
- ResourceNameRef name(pkg->name, type->type, entry->name);
- auto valueToKeep = selectProductToKeep(name, startRangeIter, iter,
- context->getDiagnostics());
- if (valueToKeep == iter) {
- // An error occurred, we could not pick a product.
- error = true;
- } else {
- // We selected a product to keep. Move it to the new array.
- newValues.push_back(std::move(*valueToKeep));
- }
-
- // Start the next range of products.
- startRangeIter = iter;
- }
- }
-
- // Now move the new values in to place.
- entry->values = std::move(newValues);
+ ResourceConfigValueIter iter = entry->values.begin();
+ ResourceConfigValueIter start_range_iter = iter;
+ while (iter != entry->values.end()) {
+ ++iter;
+ if (iter == entry->values.end() ||
+ (*iter)->config != (*start_range_iter)->config) {
+ // End of the array, or we saw a different config,
+ // so this must be the end of a range of products.
+ // Select the product to keep from the set of products defined.
+ ResourceNameRef name(pkg->name, type->type, entry->name);
+ auto value_to_keep = SelectProductToKeep(
+ name, start_range_iter, iter, context->GetDiagnostics());
+ if (value_to_keep == iter) {
+ // An error occurred, we could not pick a product.
+ error = true;
+ } else {
+ // We selected a product to keep. Move it to the new array.
+ new_values.push_back(std::move(*value_to_keep));
}
+
+ // Start the next range of products.
+ start_range_iter = iter;
+ }
}
+
+ // Now move the new values in to place.
+ entry->values = std::move(new_values);
+ }
}
- return !error;
+ }
+ return !error;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter.h b/tools/aapt2/link/ProductFilter.h
deleted file mode 100644
index 7724e14..0000000
--- a/tools/aapt2/link/ProductFilter.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 AAPT_LINK_PRODUCTFILTER_H
-#define AAPT_LINK_PRODUCTFILTER_H
-
-#include "ResourceTable.h"
-#include "process/IResourceTableConsumer.h"
-
-#include <android-base/macros.h>
-#include <unordered_set>
-
-namespace aapt {
-
-class ProductFilter {
-public:
- using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
-
- explicit ProductFilter(std::unordered_set<std::string> products) : mProducts(products) { }
-
- ResourceConfigValueIter selectProductToKeep(const ResourceNameRef& name,
- const ResourceConfigValueIter begin,
- const ResourceConfigValueIter end,
- IDiagnostics* diag);
-
- bool consume(IAaptContext* context, ResourceTable* table);
-
-private:
- std::unordered_set<std::string> mProducts;
-
- DISALLOW_COPY_AND_ASSIGN(ProductFilter);
-};
-
-} // namespace aapt
-
-#endif /* AAPT_LINK_PRODUCTFILTER_H */
diff --git a/tools/aapt2/link/ProductFilter_test.cpp b/tools/aapt2/link/ProductFilter_test.cpp
index a3376ac..379ad26 100644
--- a/tools/aapt2/link/ProductFilter_test.cpp
+++ b/tools/aapt2/link/ProductFilter_test.cpp
@@ -14,120 +14,117 @@
* limitations under the License.
*/
-#include "link/ProductFilter.h"
+#include "link/Linkers.h"
+
#include "test/Test.h"
namespace aapt {
TEST(ProductFilterTest, SelectTwoProducts) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- const ConfigDescription land = test::parseConfigOrDie("land");
- const ConfigDescription port = test::parseConfigOrDie("port");
+ const ConfigDescription land = test::ParseConfigOrDie("land");
+ const ConfigDescription port = test::ParseConfigOrDie("port");
- ResourceTable table;
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- land, "",
- test::ValueBuilder<Id>()
- .setSource(Source("land/default.xml")).build(),
- context->getDiagnostics()));
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- land, "tablet",
- test::ValueBuilder<Id>()
- .setSource(Source("land/tablet.xml")).build(),
- context->getDiagnostics()));
+ ResourceTable table;
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/one"), land, "",
+ test::ValueBuilder<Id>().SetSource(Source("land/default.xml")).Build(),
+ context->GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/one"), land, "tablet",
+ test::ValueBuilder<Id>().SetSource(Source("land/tablet.xml")).Build(),
+ context->GetDiagnostics()));
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- port, "",
- test::ValueBuilder<Id>()
- .setSource(Source("port/default.xml")).build(),
- context->getDiagnostics()));
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- port, "tablet",
- test::ValueBuilder<Id>()
- .setSource(Source("port/tablet.xml")).build(),
- context->getDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/one"), port, "",
+ test::ValueBuilder<Id>().SetSource(Source("port/default.xml")).Build(),
+ context->GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/one"), port, "tablet",
+ test::ValueBuilder<Id>().SetSource(Source("port/tablet.xml")).Build(),
+ context->GetDiagnostics()));
- ProductFilter filter({ "tablet" });
- ASSERT_TRUE(filter.consume(context.get(), &table));
+ ProductFilter filter({"tablet"});
+ ASSERT_TRUE(filter.Consume(context.get(), &table));
- EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- land, ""));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- land, "tablet"));
- EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- port, ""));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- port, "tablet"));
+ EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
+ &table, "android:string/one", land, ""));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
+ &table, "android:string/one", land, "tablet"));
+ EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
+ &table, "android:string/one", port, ""));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
+ &table, "android:string/one", port, "tablet"));
}
TEST(ProductFilterTest, SelectDefaultProduct) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- ResourceTable table;
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- ConfigDescription::defaultConfig(), "",
- test::ValueBuilder<Id>()
- .setSource(Source("default.xml")).build(),
- context->getDiagnostics()));
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- ConfigDescription::defaultConfig(), "tablet",
- test::ValueBuilder<Id>()
- .setSource(Source("tablet.xml")).build(),
- context->getDiagnostics()));
+ ResourceTable table;
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/one"),
+ ConfigDescription::DefaultConfig(), "",
+ test::ValueBuilder<Id>().SetSource(Source("default.xml")).Build(),
+ context->GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/one"),
+ ConfigDescription::DefaultConfig(), "tablet",
+ test::ValueBuilder<Id>().SetSource(Source("tablet.xml")).Build(),
+ context->GetDiagnostics()));
- ProductFilter filter({});
- ASSERT_TRUE(filter.consume(context.get(), &table));
+ ProductFilter filter({});
+ ASSERT_TRUE(filter.Consume(context.get(), &table));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- ConfigDescription::defaultConfig(),
- ""));
- EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- ConfigDescription::defaultConfig(),
- "tablet"));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
+ &table, "android:string/one",
+ ConfigDescription::DefaultConfig(), ""));
+ EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
+ &table, "android:string/one",
+ ConfigDescription::DefaultConfig(), "tablet"));
}
TEST(ProductFilterTest, FailOnAmbiguousProduct) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- ResourceTable table;
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- ConfigDescription::defaultConfig(), "",
- test::ValueBuilder<Id>()
- .setSource(Source("default.xml")).build(),
- context->getDiagnostics()));
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- ConfigDescription::defaultConfig(), "tablet",
- test::ValueBuilder<Id>()
- .setSource(Source("tablet.xml")).build(),
- context->getDiagnostics()));
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- ConfigDescription::defaultConfig(), "no-sdcard",
- test::ValueBuilder<Id>()
- .setSource(Source("no-sdcard.xml")).build(),
- context->getDiagnostics()));
+ ResourceTable table;
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/one"),
+ ConfigDescription::DefaultConfig(), "",
+ test::ValueBuilder<Id>().SetSource(Source("default.xml")).Build(),
+ context->GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/one"),
+ ConfigDescription::DefaultConfig(), "tablet",
+ test::ValueBuilder<Id>().SetSource(Source("tablet.xml")).Build(),
+ context->GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/one"),
+ ConfigDescription::DefaultConfig(), "no-sdcard",
+ test::ValueBuilder<Id>().SetSource(Source("no-sdcard.xml")).Build(),
+ context->GetDiagnostics()));
- ProductFilter filter({ "tablet", "no-sdcard" });
- ASSERT_FALSE(filter.consume(context.get(), &table));
+ ProductFilter filter({"tablet", "no-sdcard"});
+ ASSERT_FALSE(filter.Consume(context.get(), &table));
}
TEST(ProductFilterTest, FailOnMultipleDefaults) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- ResourceTable table;
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- ConfigDescription::defaultConfig(), "",
- test::ValueBuilder<Id>()
- .setSource(Source(".xml")).build(),
- context->getDiagnostics()));
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- ConfigDescription::defaultConfig(), "default",
- test::ValueBuilder<Id>()
- .setSource(Source("default.xml")).build(),
- context->getDiagnostics()));
+ ResourceTable table;
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/one"),
+ ConfigDescription::DefaultConfig(), "",
+ test::ValueBuilder<Id>().SetSource(Source(".xml")).Build(),
+ context->GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ test::ParseNameOrDie("android:string/one"),
+ ConfigDescription::DefaultConfig(), "default",
+ test::ValueBuilder<Id>().SetSource(Source("default.xml")).Build(),
+ context->GetDiagnostics()));
- ProductFilter filter({});
- ASSERT_FALSE(filter.consume(context.get(), &table));
+ ProductFilter filter({});
+ ASSERT_FALSE(filter.Consume(context.get(), &table));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index be7aca3..be787b2 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -14,8 +14,12 @@
* limitations under the License.
*/
+#include "link/ReferenceLinker.h"
+
+#include "android-base/logging.h"
+#include "androidfw/ResourceTypes.h"
+
#include "Diagnostics.h"
-#include "ReferenceLinker.h"
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
@@ -26,310 +30,344 @@
#include "util/Util.h"
#include "xml/XmlUtil.h"
-#include <androidfw/ResourceTypes.h>
-#include <cassert>
-
namespace aapt {
namespace {
/**
- * The ReferenceLinkerVisitor will follow all references and make sure they point
- * to resources that actually exist, either in the local resource table, or as external
- * symbols. Once the target resource has been found, the ID of the resource will be assigned
+ * The ReferenceLinkerVisitor will follow all references and make sure they
+ * point
+ * to resources that actually exist, either in the local resource table, or as
+ * external
+ * symbols. Once the target resource has been found, the ID of the resource will
+ * be assigned
* to the reference object.
*
* NOTE: All of the entries in the ResourceTable must be assigned IDs.
*/
class ReferenceLinkerVisitor : public ValueVisitor {
-public:
- using ValueVisitor::visit;
+ public:
+ using ValueVisitor::Visit;
- ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols, StringPool* stringPool,
- xml::IPackageDeclStack* decl,CallSite* callSite) :
- mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool),
- mCallSite(callSite) {
+ ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols,
+ StringPool* string_pool, xml::IPackageDeclStack* decl,
+ CallSite* callsite)
+ : context_(context),
+ symbols_(symbols),
+ package_decls_(decl),
+ string_pool_(string_pool),
+ callsite_(callsite) {}
+
+ void Visit(Reference* ref) override {
+ if (!ReferenceLinker::LinkReference(ref, context_, symbols_, package_decls_,
+ callsite_)) {
+ error_ = true;
+ }
+ }
+
+ /**
+ * We visit the Style specially because during this phase, values of
+ * attributes are
+ * all RawString values. Now that we are expected to resolve all symbols, we
+ * can
+ * lookup the attributes to find out which types are allowed for the
+ * attributes' values.
+ */
+ void Visit(Style* style) override {
+ if (style->parent) {
+ Visit(&style->parent.value());
}
- void visit(Reference* ref) override {
- if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls, mCallSite)) {
- mError = true;
- }
- }
+ for (Style::Entry& entry : style->entries) {
+ std::string err_str;
- /**
- * We visit the Style specially because during this phase, values of attributes are
- * all RawString values. Now that we are expected to resolve all symbols, we can
- * lookup the attributes to find out which types are allowed for the attributes' values.
- */
- void visit(Style* style) override {
- if (style->parent) {
- visit(&style->parent.value());
+ // Transform the attribute reference so that it is using the fully
+ // qualified package
+ // name. This will also mark the reference as being able to see private
+ // resources if
+ // there was a '*' in the reference or if the package came from the
+ // private namespace.
+ Reference transformed_reference = entry.key;
+ TransformReferenceFromNamespace(package_decls_,
+ context_->GetCompilationPackage(),
+ &transformed_reference);
+
+ // Find the attribute in the symbol table and check if it is visible from
+ // this callsite.
+ const SymbolTable::Symbol* symbol =
+ ReferenceLinker::ResolveAttributeCheckVisibility(
+ transformed_reference, context_->GetNameMangler(), symbols_,
+ callsite_, &err_str);
+ if (symbol) {
+ // Assign our style key the correct ID.
+ // The ID may not exist.
+ entry.key.id = symbol->id;
+
+ // Try to convert the value to a more specific, typed value based on the
+ // attribute it is set to.
+ entry.value = ParseValueWithAttribute(std::move(entry.value),
+ symbol->attribute.get());
+
+ // Link/resolve the final value (mostly if it's a reference).
+ entry.value->Accept(this);
+
+ // Now verify that the type of this item is compatible with the
+ // attribute it
+ // is defined for. We pass `nullptr` as the DiagMessage so that this
+ // check is
+ // fast and we avoid creating a DiagMessage when the match is
+ // successful.
+ if (!symbol->attribute->Matches(entry.value.get(), nullptr)) {
+ // The actual type of this item is incompatible with the attribute.
+ DiagMessage msg(entry.key.GetSource());
+
+ // Call the matches method again, this time with a DiagMessage so we
+ // fill
+ // in the actual error message.
+ symbol->attribute->Matches(entry.value.get(), &msg);
+ context_->GetDiagnostics()->Error(msg);
+ error_ = true;
}
- for (Style::Entry& entry : style->entries) {
- std::string errStr;
+ } else {
+ DiagMessage msg(entry.key.GetSource());
+ msg << "style attribute '";
+ ReferenceLinker::WriteResourceName(&msg, entry.key,
+ transformed_reference);
+ msg << "' " << err_str;
+ context_->GetDiagnostics()->Error(msg);
+ error_ = true;
+ }
+ }
+ }
- // Transform the attribute reference so that it is using the fully qualified package
- // name. This will also mark the reference as being able to see private resources if
- // there was a '*' in the reference or if the package came from the private namespace.
- Reference transformedReference = entry.key;
- transformReferenceFromNamespace(mPackageDecls, mContext->getCompilationPackage(),
- &transformedReference);
+ bool HasError() { return error_; }
- // Find the attribute in the symbol table and check if it is visible from this callsite.
- const SymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
- transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
- if (symbol) {
- // Assign our style key the correct ID.
- // The ID may not exist.
- entry.key.id = symbol->id;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
- // Try to convert the value to a more specific, typed value based on the
- // attribute it is set to.
- entry.value = parseValueWithAttribute(std::move(entry.value),
- symbol->attribute.get());
+ /**
+ * Transform a RawString value into a more specific, appropriate value, based
+ * on the
+ * Attribute. If a non RawString value is passed in, this is an identity
+ * transform.
+ */
+ std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
+ const Attribute* attr) {
+ if (RawString* raw_string = ValueCast<RawString>(value.get())) {
+ std::unique_ptr<Item> transformed =
+ ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
- // Link/resolve the final value (mostly if it's a reference).
- entry.value->accept(this);
-
- // Now verify that the type of this item is compatible with the attribute it
- // is defined for. We pass `nullptr` as the DiagMessage so that this check is
- // fast and we avoid creating a DiagMessage when the match is successful.
- if (!symbol->attribute->matches(entry.value.get(), nullptr)) {
- // The actual type of this item is incompatible with the attribute.
- DiagMessage msg(entry.key.getSource());
-
- // Call the matches method again, this time with a DiagMessage so we fill
- // in the actual error message.
- symbol->attribute->matches(entry.value.get(), &msg);
- mContext->getDiagnostics()->error(msg);
- mError = true;
- }
-
- } else {
- DiagMessage msg(entry.key.getSource());
- msg << "style attribute '";
- ReferenceLinker::writeResourceName(&msg, entry.key, transformedReference);
- msg << "' " << errStr;
- mContext->getDiagnostics()->error(msg);
- mError = true;
- }
+ // If we could not parse as any specific type, try a basic STRING.
+ if (!transformed &&
+ (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
+ util::StringBuilder string_builder;
+ string_builder.Append(*raw_string->value);
+ if (string_builder) {
+ transformed = util::make_unique<String>(
+ string_pool_->MakeRef(string_builder.ToString()));
}
- }
+ }
- bool hasError() {
- return mError;
- }
+ if (transformed) {
+ return transformed;
+ }
+ };
+ return value;
+ }
-private:
- IAaptContext* mContext;
- SymbolTable* mSymbols;
- xml::IPackageDeclStack* mPackageDecls;
- StringPool* mStringPool;
- CallSite* mCallSite;
- bool mError = false;
-
- /**
- * Transform a RawString value into a more specific, appropriate value, based on the
- * Attribute. If a non RawString value is passed in, this is an identity transform.
- */
- std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
- const Attribute* attr) {
- if (RawString* rawString = valueCast<RawString>(value.get())) {
- std::unique_ptr<Item> transformed =
- 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)) {
- util::StringBuilder stringBuilder;
- stringBuilder.append(*rawString->value);
- if (stringBuilder) {
- transformed = util::make_unique<String>(
- mStringPool->makeRef(stringBuilder.str()));
- }
- }
-
- if (transformed) {
- return transformed;
- }
- };
- return value;
- }
+ IAaptContext* context_;
+ SymbolTable* symbols_;
+ xml::IPackageDeclStack* package_decls_;
+ StringPool* string_pool_;
+ CallSite* callsite_;
+ bool error_ = false;
};
-} // namespace
+class EmptyDeclStack : public xml::IPackageDeclStack {
+ public:
+ EmptyDeclStack() = default;
+
+ Maybe<xml::ExtractedPackage> TransformPackageAlias(
+ const StringPiece& alias,
+ const StringPiece& local_package) const override {
+ if (alias.empty()) {
+ return xml::ExtractedPackage{local_package.ToString(),
+ true /* private */};
+ }
+ return {};
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
+};
+
+} // namespace
/**
- * The symbol is visible if it is public, or if the reference to it is requesting private access
+ * The symbol is visible if it is public, or if the reference to it is
+ * requesting private access
* or if the callsite comes from the same package.
*/
-bool ReferenceLinker::isSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
- const CallSite& callSite) {
- if (!symbol.isPublic && !ref.privateReference) {
- if (ref.name) {
- return callSite.resource.package == ref.name.value().package;
- } else if (ref.id && symbol.id) {
- return ref.id.value().packageId() == symbol.id.value().packageId();
- } else {
- return false;
- }
- }
- return true;
-}
-
-const SymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
- NameMangler* mangler,
- SymbolTable* symbols) {
- if (reference.name) {
- Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
- return symbols->findByName(mangled ? mangled.value() : reference.name.value());
- } else if (reference.id) {
- return symbols->findById(reference.id.value());
+bool ReferenceLinker::IsSymbolVisible(const SymbolTable::Symbol& symbol,
+ const Reference& ref,
+ const CallSite& callsite) {
+ if (!symbol.is_public && !ref.private_reference) {
+ if (ref.name) {
+ return callsite.resource.package == ref.name.value().package;
+ } else if (ref.id && symbol.id) {
+ return ref.id.value().package_id() == symbol.id.value().package_id();
} else {
- return nullptr;
+ return false;
}
+ }
+ return true;
}
-const SymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
- const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
- CallSite* callSite, std::string* outError) {
- const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
- if (!symbol) {
- if (outError) *outError = "not found";
- return nullptr;
- }
-
- if (!isSymbolVisible(*symbol, reference, *callSite)) {
- if (outError) *outError = "is private";
- return nullptr;
- }
- return symbol;
+const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(
+ const Reference& reference, NameMangler* mangler, SymbolTable* symbols) {
+ if (reference.name) {
+ Maybe<ResourceName> mangled = mangler->MangleName(reference.name.value());
+ return symbols->FindByName(mangled ? mangled.value()
+ : reference.name.value());
+ } else if (reference.id) {
+ return symbols->FindById(reference.id.value());
+ } else {
+ return nullptr;
+ }
}
-const SymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
- const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
- CallSite* callSite, std::string* outError) {
- const SymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
- symbols, callSite,
- outError);
- if (!symbol) {
- return nullptr;
- }
+const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(
+ const Reference& reference, NameMangler* name_mangler, SymbolTable* symbols,
+ CallSite* callsite, std::string* out_error) {
+ const SymbolTable::Symbol* symbol =
+ ResolveSymbol(reference, name_mangler, symbols);
+ if (!symbol) {
+ if (out_error) *out_error = "not found";
+ return nullptr;
+ }
- if (!symbol->attribute) {
- if (outError) *outError = "is not an attribute";
- return nullptr;
- }
- return symbol;
+ if (!IsSymbolVisible(*symbol, reference, *callsite)) {
+ if (out_error) *out_error = "is private";
+ return nullptr;
+ }
+ return symbol;
}
-Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
- NameMangler* nameMangler,
- SymbolTable* symbols,
- CallSite* callSite,
- std::string* outError) {
- const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
- if (!symbol) {
- if (outError) *outError = "not found";
- return {};
- }
+const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
+ const Reference& reference, NameMangler* name_mangler, SymbolTable* symbols,
+ CallSite* callsite, std::string* out_error) {
+ const SymbolTable::Symbol* symbol = ResolveSymbolCheckVisibility(
+ reference, name_mangler, symbols, callsite, out_error);
+ if (!symbol) {
+ return nullptr;
+ }
- if (!symbol->attribute) {
- if (outError) *outError = "is not an attribute";
- return {};
- }
- return xml::AaptAttribute{ symbol->id, *symbol->attribute };
+ if (!symbol->attribute) {
+ if (out_error) *out_error = "is not an attribute";
+ return nullptr;
+ }
+ return symbol;
}
-void ReferenceLinker::writeResourceName(DiagMessage* outMsg, const Reference& orig,
+Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(
+ const Reference& reference, NameMangler* name_mangler, SymbolTable* symbols,
+ CallSite* callsite, std::string* out_error) {
+ const SymbolTable::Symbol* symbol =
+ ResolveSymbol(reference, name_mangler, symbols);
+ if (!symbol) {
+ if (out_error) *out_error = "not found";
+ return {};
+ }
+
+ if (!symbol->attribute) {
+ if (out_error) *out_error = "is not an attribute";
+ return {};
+ }
+ return xml::AaptAttribute{symbol->id, *symbol->attribute};
+}
+
+void ReferenceLinker::WriteResourceName(DiagMessage* out_msg,
+ const Reference& orig,
const Reference& transformed) {
- assert(outMsg);
+ CHECK(out_msg != nullptr);
- if (orig.name) {
- *outMsg << orig.name.value();
- if (transformed.name.value() != orig.name.value()) {
- *outMsg << " (aka " << transformed.name.value() << ")";
- }
- } else {
- *outMsg << orig.id.value();
+ if (orig.name) {
+ *out_msg << orig.name.value();
+ if (transformed.name.value() != orig.name.value()) {
+ *out_msg << " (aka " << transformed.name.value() << ")";
}
+ } else {
+ *out_msg << orig.id.value();
+ }
}
-bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
- SymbolTable* symbols, xml::IPackageDeclStack* decls,
- CallSite* callSite) {
- assert(reference);
- assert(reference->name || reference->id);
+bool ReferenceLinker::LinkReference(Reference* reference, IAaptContext* context,
+ SymbolTable* symbols,
+ xml::IPackageDeclStack* decls,
+ CallSite* callsite) {
+ CHECK(reference != nullptr);
+ CHECK(reference->name || reference->id);
- Reference transformedReference = *reference;
- transformReferenceFromNamespace(decls, context->getCompilationPackage(),
- &transformedReference);
+ Reference transformed_reference = *reference;
+ TransformReferenceFromNamespace(decls, context->GetCompilationPackage(),
+ &transformed_reference);
- std::string errStr;
- const SymbolTable::Symbol* s = resolveSymbolCheckVisibility(
- transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
- if (s) {
- // The ID may not exist. This is fine because of the possibility of building against
- // libraries without assigned IDs.
- // Ex: Linking against own resources when building a static library.
- reference->id = s->id;
- return true;
- }
+ std::string err_str;
+ const SymbolTable::Symbol* s = ResolveSymbolCheckVisibility(
+ transformed_reference, context->GetNameMangler(), symbols, callsite,
+ &err_str);
+ if (s) {
+ // The ID may not exist. This is fine because of the possibility of building
+ // against libraries without assigned IDs.
+ // Ex: Linking against own resources when building a static library.
+ reference->id = s->id;
+ return true;
+ }
- DiagMessage errorMsg(reference->getSource());
- errorMsg << "resource ";
- writeResourceName(&errorMsg, *reference, transformedReference);
- errorMsg << " " << errStr;
- context->getDiagnostics()->error(errorMsg);
- return false;
+ DiagMessage error_msg(reference->GetSource());
+ error_msg << "resource ";
+ WriteResourceName(&error_msg, *reference, transformed_reference);
+ error_msg << " " << err_str;
+ context->GetDiagnostics()->Error(error_msg);
+ return false;
}
-namespace {
-
-struct EmptyDeclStack : public xml::IPackageDeclStack {
- Maybe<xml::ExtractedPackage> transformPackageAlias(
- const StringPiece& alias, const StringPiece& localPackage) const override {
- if (alias.empty()) {
- return xml::ExtractedPackage{ localPackage.toString(), true /* private */ };
+bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
+ EmptyDeclStack decl_stack;
+ bool error = false;
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ // Symbol state information may be lost if there is no value for the
+ // resource.
+ if (entry->symbol_status.state != SymbolState::kUndefined &&
+ entry->values.empty()) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(entry->symbol_status.source)
+ << "no definition for declared symbol '"
+ << ResourceNameRef(package->name, type->type, entry->name)
+ << "'");
+ error = true;
}
- return {};
- }
-};
-} // namespace
+ CallSite callsite = {
+ ResourceNameRef(package->name, type->type, entry->name)};
+ ReferenceLinkerVisitor visitor(context, context->GetExternalSymbols(),
+ &table->string_pool, &decl_stack,
+ &callsite);
-bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
- EmptyDeclStack declStack;
- bool error = false;
- for (auto& package : table->packages) {
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- // Symbol state information may be lost if there is no value for the resource.
- if (entry->symbolStatus.state != SymbolState::kUndefined && entry->values.empty()) {
- context->getDiagnostics()->error(
- DiagMessage(entry->symbolStatus.source)
- << "no definition for declared symbol '"
- << ResourceNameRef(package->name, type->type, entry->name)
- << "'");
- error = true;
- }
-
- CallSite callSite = { ResourceNameRef(package->name, type->type, entry->name) };
- ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
- &table->stringPool, &declStack, &callSite);
-
- for (auto& configValue : entry->values) {
- configValue->value->accept(&visitor);
- }
-
- if (visitor.hasError()) {
- error = true;
- }
- }
+ for (auto& config_value : entry->values) {
+ config_value->value->Accept(&visitor);
}
+
+ if (visitor.HasError()) {
+ error = true;
+ }
+ }
}
- return !error;
+ }
+ return !error;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 7993aaf..bdabf24 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -17,6 +17,8 @@
#ifndef AAPT_LINKER_REFERENCELINKER_H
#define AAPT_LINKER_REFERENCELINKER_H
+#include "android-base/macros.h"
+
#include "Resource.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
@@ -25,82 +27,91 @@
#include "process/SymbolTable.h"
#include "xml/XmlDom.h"
-#include <cassert>
-
namespace aapt {
/**
- * Resolves all references to resources in the ResourceTable and assigns them IDs.
+ * Resolves all references to resources in the ResourceTable and assigns them
+ * IDs.
* The ResourceTable must already have IDs assigned to each resource.
- * Once the ResourceTable is processed by this linker, it is ready to be flattened.
+ * Once the ResourceTable is processed by this linker, it is ready to be
+ * flattened.
*/
-struct ReferenceLinker : public IResourceTableConsumer {
- /**
- * Returns true if the symbol is visible by the reference and from the callsite.
- */
- static bool isSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
- const CallSite& callSite);
+class ReferenceLinker : public IResourceTableConsumer {
+ public:
+ ReferenceLinker() = default;
- /**
- * Performs name mangling and looks up the resource in the symbol table. Returns nullptr
- * if the symbol was not found.
- */
- static const SymbolTable::Symbol* resolveSymbol(const Reference& reference,
- NameMangler* mangler, SymbolTable* symbols);
+ /**
+ * Returns true if the symbol is visible by the reference and from the
+ * callsite.
+ */
+ static bool IsSymbolVisible(const SymbolTable::Symbol& symbol,
+ const Reference& ref, const CallSite& callsite);
- /**
- * Performs name mangling and looks up the resource in the symbol table. If the symbol is
- * not visible by the reference at the callsite, nullptr is returned. outError holds
- * the error message.
- */
- static const SymbolTable::Symbol* resolveSymbolCheckVisibility(const Reference& reference,
- NameMangler* nameMangler,
- SymbolTable* symbols,
- CallSite* callSite,
- std::string* outError);
+ /**
+ * Performs name mangling and looks up the resource in the symbol table.
+ * Returns nullptr if the symbol was not found.
+ */
+ static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference,
+ NameMangler* mangler,
+ SymbolTable* symbols);
- /**
- * Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
- * That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute.
- */
- static const SymbolTable::Symbol* resolveAttributeCheckVisibility(const Reference& reference,
- NameMangler* nameMangler,
- SymbolTable* symbols,
- CallSite* callSite,
- std::string* outError);
+ /**
+ * Performs name mangling and looks up the resource in the symbol table. If
+ * the symbol is not visible by the reference at the callsite, nullptr is
+ * returned. out_error holds the error message.
+ */
+ static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(
+ const Reference& reference, NameMangler* name_mangler,
+ SymbolTable* symbols, CallSite* callsite, std::string* out_error);
- /**
- * Resolves the attribute reference and returns an xml::AaptAttribute if successful.
- * If resolution fails, outError holds the error message.
- */
- static Maybe<xml::AaptAttribute> compileXmlAttribute(const Reference& reference,
- NameMangler* nameMangler,
- SymbolTable* symbols,
- CallSite* callSite,
- std::string* outError);
+ /**
+ * Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is
+ * an attribute.
+ * That is, the return value will have a non-null value for
+ * ISymbolTable::Symbol::attribute.
+ */
+ static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(
+ const Reference& reference, NameMangler* name_mangler,
+ SymbolTable* symbols, CallSite* callsite, std::string* out_error);
- /**
- * Writes the resource name to the DiagMessage, using the "orig_name (aka <transformed_name>)"
- * syntax.
- */
- static void writeResourceName(DiagMessage* outMsg, const Reference& orig,
- const Reference& transformed);
+ /**
+ * Resolves the attribute reference and returns an xml::AaptAttribute if
+ * successful.
+ * If resolution fails, outError holds the error message.
+ */
+ static Maybe<xml::AaptAttribute> CompileXmlAttribute(
+ const Reference& reference, NameMangler* name_mangler,
+ SymbolTable* symbols, CallSite* callsite, std::string* out_error);
- /**
- * Transforms the package name of the reference to the fully qualified package name using
- * the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible
- * to the reference at the callsite, the reference is updated with an ID.
- * Returns false on failure, and an error message is logged to the IDiagnostics in the context.
- */
- static bool linkReference(Reference* reference, IAaptContext* context, SymbolTable* symbols,
- xml::IPackageDeclStack* decls, CallSite* callSite);
+ /**
+ * Writes the resource name to the DiagMessage, using the
+ * "orig_name (aka <transformed_name>)" syntax.
+ */
+ static void WriteResourceName(DiagMessage* out_msg, const Reference& orig,
+ const Reference& transformed);
- /**
- * Links all references in the ResourceTable.
- */
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ /**
+ * Transforms the package name of the reference to the fully qualified package
+ * name using
+ * the xml::IPackageDeclStack, then mangles and looks up the symbol. If the
+ * symbol is visible
+ * to the reference at the callsite, the reference is updated with an ID.
+ * Returns false on failure, and an error message is logged to the
+ * IDiagnostics in the context.
+ */
+ static bool LinkReference(Reference* reference, IAaptContext* context,
+ SymbolTable* symbols, xml::IPackageDeclStack* decls,
+ CallSite* callsite);
+
+ /**
+ * Links all references in the ResourceTable.
+ */
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReferenceLinker);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_LINKER_REFERENCELINKER_H */
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 5c1511f..4ca36a9 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -15,6 +15,7 @@
*/
#include "link/ReferenceLinker.h"
+
#include "test/Test.h"
using android::ResTable_map;
@@ -22,206 +23,238 @@
namespace aapt {
TEST(ReferenceLinkerTest, LinkSimpleReferences) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.test", 0x7f)
- .addReference("com.app.test:string/foo", ResourceId(0x7f020000),
- "com.app.test:string/bar")
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddReference("com.app.test:string/foo", ResourceId(0x7f020000),
+ "com.app.test:string/bar")
- // Test use of local reference (w/o package name).
- .addReference("com.app.test:string/bar", ResourceId(0x7f020001), "string/baz")
+ // Test use of local reference (w/o package name).
+ .AddReference("com.app.test:string/bar", ResourceId(0x7f020001),
+ "string/baz")
- .addReference("com.app.test:string/baz", ResourceId(0x7f020002),
- "android:string/ok")
- .build();
+ .AddReference("com.app.test:string/baz", ResourceId(0x7f020002),
+ "android:string/ok")
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" })
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addPublicSymbol("android:string/ok", ResourceId(0x01040034))
- .build())
- .build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .AddPublicSymbol("android:string/ok", ResourceId(0x01040034))
+ .Build())
+ .Build();
- ReferenceLinker linker;
- ASSERT_TRUE(linker.consume(context.get(), table.get()));
+ ReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context.get(), table.get()));
- Reference* ref = test::getValue<Reference>(table.get(), "com.app.test:string/foo");
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
+ Reference* ref =
+ test::GetValue<Reference>(table.get(), "com.app.test:string/foo");
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
- ref = test::getValue<Reference>(table.get(), "com.app.test:string/bar");
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f020002));
+ ref = test::GetValue<Reference>(table.get(), "com.app.test:string/bar");
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f020002));
- ref = test::getValue<Reference>(table.get(), "com.app.test:string/baz");
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x01040034));
+ ref = test::GetValue<Reference>(table.get(), "com.app.test:string/baz");
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x01040034));
}
TEST(ReferenceLinkerTest, LinkStyleAttributes) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.test", 0x7f)
- .addValue("com.app.test:style/Theme", test::StyleBuilder()
- .setParent("android:style/Theme.Material")
- .addItem("android:attr/foo", ResourceUtils::tryParseColor("#ff00ff"))
- .addItem("android:attr/bar", {} /* placeholder */)
- .build())
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddValue("com.app.test:style/Theme",
+ test::StyleBuilder()
+ .SetParent("android:style/Theme.Material")
+ .AddItem("android:attr/foo",
+ ResourceUtils::TryParseColor("#ff00ff"))
+ .AddItem("android:attr/bar", {} /* placeholder */)
+ .Build())
+ .Build();
- {
- // We need to fill in the value for the attribute android:attr/bar after we build the
- // table, because we need access to the string pool.
- Style* style = test::getValue<Style>(table.get(), "com.app.test:style/Theme");
- ASSERT_NE(style, nullptr);
- style->entries.back().value = util::make_unique<RawString>(
- table->stringPool.makeRef("one|two"));
- }
-
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" })
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addPublicSymbol("android:style/Theme.Material",
- ResourceId(0x01060000))
- .addPublicSymbol("android:attr/foo", ResourceId(0x01010001),
- test::AttributeBuilder()
- .setTypeMask(ResTable_map::TYPE_COLOR)
- .build())
- .addPublicSymbol("android:attr/bar", ResourceId(0x01010002),
- test::AttributeBuilder()
- .setTypeMask(ResTable_map::TYPE_FLAGS)
- .addItem("one", 0x01)
- .addItem("two", 0x02)
- .build())
- .build())
- .build();
-
- ReferenceLinker linker;
- ASSERT_TRUE(linker.consume(context.get(), table.get()));
-
- Style* style = test::getValue<Style>(table.get(), "com.app.test:style/Theme");
+ {
+ // We need to fill in the value for the attribute android:attr/bar after we
+ // build the
+ // table, because we need access to the string pool.
+ Style* style =
+ test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
ASSERT_NE(style, nullptr);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().id);
- EXPECT_EQ(style->parent.value().id.value(), ResourceId(0x01060000));
+ style->entries.back().value =
+ util::make_unique<RawString>(table->string_pool.MakeRef("one|two"));
+ }
- ASSERT_EQ(2u, style->entries.size());
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .AddSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .AddPublicSymbol("android:style/Theme.Material",
+ ResourceId(0x01060000))
+ .AddPublicSymbol("android:attr/foo", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .SetTypeMask(ResTable_map::TYPE_COLOR)
+ .Build())
+ .AddPublicSymbol("android:attr/bar", ResourceId(0x01010002),
+ test::AttributeBuilder()
+ .SetTypeMask(ResTable_map::TYPE_FLAGS)
+ .AddItem("one", 0x01)
+ .AddItem("two", 0x02)
+ .Build())
+ .Build())
+ .Build();
- AAPT_ASSERT_TRUE(style->entries[0].key.id);
- EXPECT_EQ(style->entries[0].key.id.value(), ResourceId(0x01010001));
- ASSERT_NE(valueCast<BinaryPrimitive>(style->entries[0].value.get()), nullptr);
+ ReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context.get(), table.get()));
- AAPT_ASSERT_TRUE(style->entries[1].key.id);
- EXPECT_EQ(style->entries[1].key.id.value(), ResourceId(0x01010002));
- ASSERT_NE(valueCast<BinaryPrimitive>(style->entries[1].value.get()), nullptr);
+ Style* style = test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
+ ASSERT_NE(style, nullptr);
+ AAPT_ASSERT_TRUE(style->parent);
+ AAPT_ASSERT_TRUE(style->parent.value().id);
+ EXPECT_EQ(style->parent.value().id.value(), ResourceId(0x01060000));
+
+ ASSERT_EQ(2u, style->entries.size());
+
+ AAPT_ASSERT_TRUE(style->entries[0].key.id);
+ EXPECT_EQ(style->entries[0].key.id.value(), ResourceId(0x01010001));
+ ASSERT_NE(ValueCast<BinaryPrimitive>(style->entries[0].value.get()), nullptr);
+
+ AAPT_ASSERT_TRUE(style->entries[1].key.id);
+ EXPECT_EQ(style->entries[1].key.id.value(), ResourceId(0x01010002));
+ ASSERT_NE(ValueCast<BinaryPrimitive>(style->entries[1].value.get()), nullptr);
}
TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test", { "com.android.support" } })
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addPublicSymbol("com.app.test:attr/com.android.support$foo",
- ResourceId(0x7f010000),
- test::AttributeBuilder()
- .setTypeMask(ResTable_map::TYPE_COLOR)
- .build())
- .build())
- .build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .SetNameManglerPolicy(
+ NameManglerPolicy{"com.app.test", {"com.android.support"}})
+ .AddSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .AddPublicSymbol("com.app.test:attr/com.android.support$foo",
+ ResourceId(0x7f010000),
+ test::AttributeBuilder()
+ .SetTypeMask(ResTable_map::TYPE_COLOR)
+ .Build())
+ .Build())
+ .Build();
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.test", 0x7f)
- .addValue("com.app.test:style/Theme", ResourceId(0x7f020000),
- test::StyleBuilder().addItem("com.android.support:attr/foo",
- ResourceUtils::tryParseColor("#ff0000"))
- .build())
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddValue("com.app.test:style/Theme", ResourceId(0x7f020000),
+ test::StyleBuilder()
+ .AddItem("com.android.support:attr/foo",
+ ResourceUtils::TryParseColor("#ff0000"))
+ .Build())
+ .Build();
- ReferenceLinker linker;
- ASSERT_TRUE(linker.consume(context.get(), table.get()));
+ ReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context.get(), table.get()));
- Style* style = test::getValue<Style>(table.get(), "com.app.test:style/Theme");
- ASSERT_NE(style, nullptr);
- ASSERT_EQ(1u, style->entries.size());
- AAPT_ASSERT_TRUE(style->entries.front().key.id);
- EXPECT_EQ(style->entries.front().key.id.value(), ResourceId(0x7f010000));
+ Style* style = test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
+ ASSERT_NE(style, nullptr);
+ ASSERT_EQ(1u, style->entries.size());
+ AAPT_ASSERT_TRUE(style->entries.front().key.id);
+ EXPECT_EQ(style->entries.front().key.id.value(), ResourceId(0x7f010000));
}
TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.test", 0x7f)
- .addReference("com.app.test:string/foo", ResourceId(0x7f020000),
- "android:string/hidden")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddReference("com.app.test:string/foo", ResourceId(0x7f020000),
+ "android:string/hidden")
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" })
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addSymbol("android:string/hidden", ResourceId(0x01040034))
- .build())
- .build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .AddSymbol("android:string/hidden", ResourceId(0x01040034))
+ .Build())
+ .Build();
- ReferenceLinker linker;
- ASSERT_FALSE(linker.consume(context.get(), table.get()));
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.Consume(context.get(), table.get()));
}
TEST(ReferenceLinkerTest, FailToLinkPrivateMangledSymbols) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.test", 0x7f)
- .addReference("com.app.test:string/foo", ResourceId(0x7f020000),
- "com.app.lib:string/hidden")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddReference("com.app.test:string/foo", ResourceId(0x7f020000),
+ "com.app.lib:string/hidden")
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test", { "com.app.lib" } })
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addSymbol("com.app.test:string/com.app.lib$hidden",
- ResourceId(0x7f040034))
- .build())
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .SetNameManglerPolicy(
+ NameManglerPolicy{"com.app.test", {"com.app.lib"}})
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .AddSymbol("com.app.test:string/com.app.lib$hidden",
+ ResourceId(0x7f040034))
+ .Build())
- .build();
+ .Build();
- ReferenceLinker linker;
- ASSERT_FALSE(linker.consume(context.get(), table.get()));
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.Consume(context.get(), table.get()));
}
TEST(ReferenceLinkerTest, FailToLinkPrivateStyleAttributes) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.test", 0x7f)
- .addValue("com.app.test:style/Theme", test::StyleBuilder()
- .addItem("android:attr/hidden", ResourceUtils::tryParseColor("#ff00ff"))
- .build())
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddValue("com.app.test:style/Theme",
+ test::StyleBuilder()
+ .AddItem("android:attr/hidden",
+ ResourceUtils::TryParseColor("#ff00ff"))
+ .Build())
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" })
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addSymbol("android:attr/hidden", ResourceId(0x01010001),
- test::AttributeBuilder()
- .setTypeMask(
- android::ResTable_map::TYPE_COLOR)
- .build())
- .build())
- .build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .AddSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .AddSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .AddSymbol("android:attr/hidden", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .SetTypeMask(android::ResTable_map::TYPE_COLOR)
+ .Build())
+ .Build())
+ .Build();
- ReferenceLinker linker;
- ASSERT_FALSE(linker.consume(context.get(), table.get()));
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.Consume(context.get(), table.get()));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ResourceDeduper.cpp b/tools/aapt2/link/ResourceDeduper.cpp
index 0276261..9431dce 100644
--- a/tools/aapt2/link/ResourceDeduper.cpp
+++ b/tools/aapt2/link/ResourceDeduper.cpp
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-#include "DominatorTree.h"
-#include "ResourceTable.h"
#include "link/Linkers.h"
#include <algorithm>
+#include "DominatorTree.h"
+#include "ResourceTable.h"
+
namespace aapt {
namespace {
@@ -36,79 +37,83 @@
* an equivalent entry value.
*/
class DominatedKeyValueRemover : public DominatorTree::BottomUpVisitor {
-public:
- using Node = DominatorTree::Node;
+ public:
+ using Node = DominatorTree::Node;
- explicit DominatedKeyValueRemover(IAaptContext* context, ResourceEntry* entry) :
- mContext(context), mEntry(entry) {
+ explicit DominatedKeyValueRemover(IAaptContext* context, ResourceEntry* entry)
+ : context_(context), entry_(entry) {}
+
+ void VisitConfig(Node* node) {
+ Node* parent = node->parent();
+ if (!parent) {
+ return;
+ }
+ ResourceConfigValue* node_value = node->value();
+ ResourceConfigValue* parent_value = parent->value();
+ if (!node_value || !parent_value) {
+ return;
+ }
+ if (!node_value->value->Equals(parent_value->value.get())) {
+ return;
}
- void visitConfig(Node* node) {
- Node* parent = node->parent();
- if (!parent) {
- return;
- }
- ResourceConfigValue* nodeValue = node->value();
- ResourceConfigValue* parentValue = parent->value();
- if (!nodeValue || !parentValue) {
- return;
- }
- if (!nodeValue->value->equals(parentValue->value.get())) {
- return;
- }
-
- // Compare compatible configs for this entry and ensure the values are
- // equivalent.
- const ConfigDescription& nodeConfiguration = nodeValue->config;
- for (const auto& sibling : mEntry->values) {
- if (!sibling->value) {
- // Sibling was already removed.
- continue;
- }
- if (nodeConfiguration.isCompatibleWith(sibling->config)
- && !nodeValue->value->equals(sibling->value.get())) {
- // The configurations are compatible, but the value is
- // different, so we can't remove this value.
- return;
- }
- }
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(
- DiagMessage(nodeValue->value->getSource())
- << "removing dominated duplicate resource with name \""
- << mEntry->name << "\"");
- }
- nodeValue->value = {};
+ // Compare compatible configs for this entry and ensure the values are
+ // equivalent.
+ const ConfigDescription& node_configuration = node_value->config;
+ for (const auto& sibling : entry_->values) {
+ if (!sibling->value) {
+ // Sibling was already removed.
+ continue;
+ }
+ if (node_configuration.IsCompatibleWith(sibling->config) &&
+ !node_value->value->Equals(sibling->value.get())) {
+ // The configurations are compatible, but the value is
+ // different, so we can't remove this value.
+ return;
+ }
}
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(
+ DiagMessage(node_value->value->GetSource())
+ << "removing dominated duplicate resource with name \""
+ << entry_->name << "\"");
+ }
+ node_value->value = {};
+ }
-private:
- IAaptContext* mContext;
- ResourceEntry* mEntry;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DominatedKeyValueRemover);
+
+ IAaptContext* context_;
+ ResourceEntry* entry_;
};
-static void dedupeEntry(IAaptContext* context, ResourceEntry* entry) {
- DominatorTree tree(entry->values);
- DominatedKeyValueRemover remover(context, entry);
- tree.accept(&remover);
+static void DedupeEntry(IAaptContext* context, ResourceEntry* entry) {
+ DominatorTree tree(entry->values);
+ DominatedKeyValueRemover remover(context, entry);
+ tree.Accept(&remover);
- // Erase the values that were removed.
- entry->values.erase(std::remove_if(entry->values.begin(), entry->values.end(),
- [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
- return val == nullptr || val->value == nullptr;
- }), entry->values.end());
+ // Erase the values that were removed.
+ entry->values.erase(
+ std::remove_if(
+ entry->values.begin(), entry->values.end(),
+ [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
+ return val == nullptr || val->value == nullptr;
+ }),
+ entry->values.end());
}
-} // namespace
+} // namespace
-bool ResourceDeduper::consume(IAaptContext* context, ResourceTable* table) {
- for (auto& package : table->packages) {
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- dedupeEntry(context, entry.get());
- }
- }
+bool ResourceDeduper::Consume(IAaptContext* context, ResourceTable* table) {
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ DedupeEntry(context, entry.get());
+ }
}
- return true;
+ }
+ return true;
}
-} // aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ResourceDeduper_test.cpp b/tools/aapt2/link/ResourceDeduper_test.cpp
index 47071a51..d38059d 100644
--- a/tools/aapt2/link/ResourceDeduper_test.cpp
+++ b/tools/aapt2/link/ResourceDeduper_test.cpp
@@ -14,70 +14,74 @@
* limitations under the License.
*/
-#include "ResourceTable.h"
#include "link/Linkers.h"
+
+#include "ResourceTable.h"
#include "test/Test.h"
namespace aapt {
TEST(ResourceDeduperTest, SameValuesAreDeduped) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- const ConfigDescription defaultConfig = {};
- const ConfigDescription enConfig = test::parseConfigOrDie("en");
- const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
- // Chosen because this configuration is compatible with en.
- const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ const ConfigDescription default_config = {};
+ const ConfigDescription en_config = test::ParseConfigOrDie("en");
+ const ConfigDescription en_v21_config = test::ParseConfigOrDie("en-v21");
+ // Chosen because this configuration is compatible with en.
+ const ConfigDescription land_config = test::ParseConfigOrDie("land");
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addString("android:string/dedupe", ResourceId{}, defaultConfig, "dedupe")
- .addString("android:string/dedupe", ResourceId{}, enConfig, "dedupe")
- .addString("android:string/dedupe", ResourceId{}, landConfig, "dedupe")
- .addString("android:string/dedupe2", ResourceId{}, defaultConfig, "dedupe")
- .addString("android:string/dedupe2", ResourceId{}, enConfig, "dedupe")
- .addString("android:string/dedupe2", ResourceId{}, enV21Config, "keep")
- .addString("android:string/dedupe2", ResourceId{}, landConfig, "dedupe")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddString("android:string/dedupe", ResourceId{}, default_config,
+ "dedupe")
+ .AddString("android:string/dedupe", ResourceId{}, en_config, "dedupe")
+ .AddString("android:string/dedupe", ResourceId{}, land_config,
+ "dedupe")
+ .AddString("android:string/dedupe2", ResourceId{}, default_config,
+ "dedupe")
+ .AddString("android:string/dedupe2", ResourceId{}, en_config,
+ "dedupe")
+ .AddString("android:string/dedupe2", ResourceId{}, en_v21_config,
+ "keep")
+ .AddString("android:string/dedupe2", ResourceId{}, land_config,
+ "dedupe")
+ .Build();
- ASSERT_TRUE(ResourceDeduper().consume(context.get(), table.get()));
- EXPECT_EQ(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/dedupe", enConfig));
- EXPECT_EQ(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/dedupe", landConfig));
- EXPECT_EQ(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/dedupe2", enConfig));
- EXPECT_NE(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/dedupe2", enV21Config));
+ ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<String>(
+ table.get(), "android:string/dedupe", en_config));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<String>(
+ table.get(), "android:string/dedupe", land_config));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<String>(
+ table.get(), "android:string/dedupe2", en_config));
+ EXPECT_NE(nullptr, test::GetValueForConfig<String>(
+ table.get(), "android:string/dedupe2", en_v21_config));
}
TEST(ResourceDeduperTest, DifferentValuesAreKept) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- const ConfigDescription defaultConfig = {};
- const ConfigDescription enConfig = test::parseConfigOrDie("en");
- const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
- // Chosen because this configuration is compatible with en.
- const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ const ConfigDescription default_config = {};
+ const ConfigDescription en_config = test::ParseConfigOrDie("en");
+ const ConfigDescription en_v21_config = test::ParseConfigOrDie("en-v21");
+ // Chosen because this configuration is compatible with en.
+ const ConfigDescription land_config = test::ParseConfigOrDie("land");
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addString("android:string/keep", ResourceId{}, defaultConfig, "keep")
- .addString("android:string/keep", ResourceId{}, enConfig, "keep")
- .addString("android:string/keep", ResourceId{}, enV21Config, "keep2")
- .addString("android:string/keep", ResourceId{}, landConfig, "keep2")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddString("android:string/keep", ResourceId{}, default_config,
+ "keep")
+ .AddString("android:string/keep", ResourceId{}, en_config, "keep")
+ .AddString("android:string/keep", ResourceId{}, en_v21_config,
+ "keep2")
+ .AddString("android:string/keep", ResourceId{}, land_config, "keep2")
+ .Build();
- ASSERT_TRUE(ResourceDeduper().consume(context.get(), table.get()));
- EXPECT_NE(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/keep", enConfig));
- EXPECT_NE(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/keep", enV21Config));
- EXPECT_NE(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/keep", landConfig));
+ ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+ EXPECT_NE(nullptr, test::GetValueForConfig<String>(
+ table.get(), "android:string/keep", en_config));
+ EXPECT_NE(nullptr, test::GetValueForConfig<String>(
+ table.get(), "android:string/keep", en_v21_config));
+ EXPECT_NE(nullptr, test::GetValueForConfig<String>(
+ table.get(), "android:string/keep", land_config));
}
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index eea4306..d808da3 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -14,360 +14,383 @@
* limitations under the License.
*/
+#include "link/TableMerger.h"
+
+#include "android-base/logging.h"
+
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "link/TableMerger.h"
#include "util/Util.h"
-#include <cassert>
-
namespace aapt {
-TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable,
- const TableMergerOptions& options) :
- mContext(context), mMasterTable(outTable), mOptions(options) {
- // Create the desired package that all tables will be merged into.
- mMasterPackage = mMasterTable->createPackage(
- mContext->getCompilationPackage(), mContext->getPackageId());
- assert(mMasterPackage && "package name or ID already taken");
+TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table,
+ const TableMergerOptions& options)
+ : context_(context), master_table_(out_table), options_(options) {
+ // Create the desired package that all tables will be merged into.
+ master_package_ = master_table_->CreatePackage(
+ context_->GetCompilationPackage(), context_->GetPackageId());
+ CHECK(master_package_ != nullptr) << "package name or ID already taken";
}
-bool TableMerger::merge(const Source& src, ResourceTable* table,
+bool TableMerger::Merge(const Source& src, ResourceTable* table,
io::IFileCollection* collection) {
- return mergeImpl(src, table, collection, false /* overlay */, true /* allow new */);
+ return MergeImpl(src, table, collection, false /* overlay */,
+ true /* allow new */);
}
-bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table,
+bool TableMerger::MergeOverlay(const Source& src, ResourceTable* table,
io::IFileCollection* collection) {
- return mergeImpl(src, table, collection, true /* overlay */, mOptions.autoAddOverlay);
+ return MergeImpl(src, table, collection, true /* overlay */,
+ options_.auto_add_overlay);
}
/**
* This will merge packages with the same package name (or no package name).
*/
-bool TableMerger::mergeImpl(const Source& src, ResourceTable* table,
- io::IFileCollection* collection,
- bool overlay, bool allowNew) {
- const uint8_t desiredPackageId = mContext->getPackageId();
+bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
+ io::IFileCollection* collection, bool overlay,
+ bool allow_new) {
+ const uint8_t desired_package_id = context_->GetPackageId();
- bool error = false;
- for (auto& package : table->packages) {
- // Warn of packages with an unrelated ID.
- const Maybe<ResourceId>& id = package->id;
- if (id && id.value() != 0x0 && id.value() != desiredPackageId) {
- mContext->getDiagnostics()->warn(DiagMessage(src)
- << "ignoring package " << package->name);
- continue;
- }
-
- // Only merge an empty package or the package we're building.
- // Other packages may exist, which likely contain attribute definitions.
- // This is because at compile time it is unknown if the attributes are simply
- // uses of the attribute or definitions.
- if (package->name.empty() || mContext->getCompilationPackage() == package->name) {
- FileMergeCallback callback;
- if (collection) {
- callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
- FileReference* newFile, FileReference* oldFile) -> bool {
- // The old file's path points inside the APK, so we can use it as is.
- io::IFile* f = collection->findFile(*oldFile->path);
- if (!f) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "file '"
- << *oldFile->path
- << "' not found");
- return false;
- }
-
- newFile->file = f;
- return true;
- };
- }
-
- // Merge here. Once the entries are merged and mangled, any references to
- // them are still valid. This is because un-mangled references are
- // mangled, then looked up at resolution time.
- // Also, when linking, we convert references with no package name to use
- // the compilation package name.
- error |= !doMerge(src, table, package.get(), false /* mangle */, overlay, allowNew,
- callback);
- }
+ bool error = false;
+ for (auto& package : table->packages) {
+ // Warn of packages with an unrelated ID.
+ const Maybe<ResourceId>& id = package->id;
+ if (id && id.value() != 0x0 && id.value() != desired_package_id) {
+ context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package "
+ << package->name);
+ continue;
}
- return !error;
+
+ // Only merge an empty package or the package we're building.
+ // Other packages may exist, which likely contain attribute definitions.
+ // This is because at compile time it is unknown if the attributes are
+ // simply
+ // uses of the attribute or definitions.
+ if (package->name.empty() ||
+ context_->GetCompilationPackage() == package->name) {
+ FileMergeCallback callback;
+ if (collection) {
+ callback = [&](const ResourceNameRef& name,
+ const ConfigDescription& config, FileReference* new_file,
+ FileReference* old_file) -> bool {
+ // The old file's path points inside the APK, so we can use it as is.
+ io::IFile* f = collection->FindFile(*old_file->path);
+ if (!f) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "file '" << *old_file->path
+ << "' not found");
+ return false;
+ }
+
+ new_file->file = f;
+ return true;
+ };
+ }
+
+ // Merge here. Once the entries are merged and mangled, any references to
+ // them are still valid. This is because un-mangled references are
+ // mangled, then looked up at resolution time.
+ // Also, when linking, we convert references with no package name to use
+ // the compilation package name.
+ error |= !DoMerge(src, table, package.get(), false /* mangle */, overlay,
+ allow_new, callback);
+ }
+ }
+ return !error;
}
/**
* This will merge and mangle resources from a static library.
*/
-bool TableMerger::mergeAndMangle(const Source& src, const StringPiece& packageName,
- ResourceTable* table, io::IFileCollection* collection) {
- bool error = false;
- for (auto& package : table->packages) {
- // Warn of packages with an unrelated ID.
- if (packageName != package->name) {
- mContext->getDiagnostics()->warn(DiagMessage(src)
- << "ignoring package " << package->name);
- continue;
- }
-
- bool mangle = packageName != mContext->getCompilationPackage();
- mMergedPackages.insert(package->name);
-
- auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
- FileReference* newFile, FileReference* oldFile) -> bool {
- // The old file's path points inside the APK, so we can use it as is.
- io::IFile* f = collection->findFile(*oldFile->path);
- if (!f) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "file '" << *oldFile->path
- << "' not found");
- return false;
- }
-
- newFile->file = f;
- return true;
- };
-
- error |= !doMerge(src, table, package.get(),
- mangle, false /* overlay */, true /* allow new */, callback);
+bool TableMerger::MergeAndMangle(const Source& src,
+ const StringPiece& package_name,
+ ResourceTable* table,
+ io::IFileCollection* collection) {
+ bool error = false;
+ for (auto& package : table->packages) {
+ // Warn of packages with an unrelated ID.
+ if (package_name != package->name) {
+ context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package "
+ << package->name);
+ continue;
}
- return !error;
+
+ bool mangle = package_name != context_->GetCompilationPackage();
+ merged_packages_.insert(package->name);
+
+ auto callback = [&](
+ const ResourceNameRef& name, const ConfigDescription& config,
+ FileReference* new_file, FileReference* old_file) -> bool {
+ // The old file's path points inside the APK, so we can use it as is.
+ io::IFile* f = collection->FindFile(*old_file->path);
+ if (!f) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(src) << "file '" << *old_file->path << "' not found");
+ return false;
+ }
+
+ new_file->file = f;
+ return true;
+ };
+
+ error |= !DoMerge(src, table, package.get(), mangle, false /* overlay */,
+ true /* allow new */, callback);
+ }
+ return !error;
}
-static bool mergeType(IAaptContext* context, const Source& src, ResourceTableType* dstType,
- ResourceTableType* srcType) {
- if (dstType->symbolStatus.state < srcType->symbolStatus.state) {
- // The incoming type's visibility is stronger, so we should override
- // the visibility.
- if (srcType->symbolStatus.state == SymbolState::kPublic) {
- // Only copy the ID if the source is public, or else the ID is meaningless.
- dstType->id = srcType->id;
- }
- dstType->symbolStatus = std::move(srcType->symbolStatus);
- } else if (dstType->symbolStatus.state == SymbolState::kPublic
- && srcType->symbolStatus.state == SymbolState::kPublic
- && dstType->id && srcType->id
- && dstType->id.value() != srcType->id.value()) {
- // Both types are public and have different IDs.
- context->getDiagnostics()->error(DiagMessage(src)
- << "cannot merge type '" << srcType->type
- << "': conflicting public IDs");
- return false;
+static bool MergeType(IAaptContext* context, const Source& src,
+ ResourceTableType* dst_type,
+ ResourceTableType* src_type) {
+ if (dst_type->symbol_status.state < src_type->symbol_status.state) {
+ // The incoming type's visibility is stronger, so we should override
+ // the visibility.
+ if (src_type->symbol_status.state == SymbolState::kPublic) {
+ // Only copy the ID if the source is public, or else the ID is
+ // meaningless.
+ dst_type->id = src_type->id;
}
- return true;
+ dst_type->symbol_status = std::move(src_type->symbol_status);
+ } else if (dst_type->symbol_status.state == SymbolState::kPublic &&
+ src_type->symbol_status.state == SymbolState::kPublic &&
+ dst_type->id && src_type->id &&
+ dst_type->id.value() != src_type->id.value()) {
+ // Both types are public and have different IDs.
+ context->GetDiagnostics()->Error(DiagMessage(src)
+ << "cannot merge type '" << src_type->type
+ << "': conflicting public IDs");
+ return false;
+ }
+ return true;
}
-static bool mergeEntry(IAaptContext* context, const Source& src, ResourceEntry* dstEntry,
- ResourceEntry* srcEntry) {
- if (dstEntry->symbolStatus.state < srcEntry->symbolStatus.state) {
- // The incoming type's visibility is stronger, so we should override
- // the visibility.
- if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
- // Only copy the ID if the source is public, or else the ID is meaningless.
- dstEntry->id = srcEntry->id;
- }
- dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
- } else if (srcEntry->symbolStatus.state == SymbolState::kPublic
- && dstEntry->symbolStatus.state == SymbolState::kPublic
- && dstEntry->id && srcEntry->id
- && dstEntry->id.value() != srcEntry->id.value()) {
- // Both entries are public and have different IDs.
- context->getDiagnostics()->error(DiagMessage(src)
- << "cannot merge entry '" << srcEntry->name
- << "': conflicting public IDs");
- return false;
+static bool MergeEntry(IAaptContext* context, const Source& src,
+ ResourceEntry* dst_entry, ResourceEntry* src_entry) {
+ if (dst_entry->symbol_status.state < src_entry->symbol_status.state) {
+ // The incoming type's visibility is stronger, so we should override
+ // the visibility.
+ if (src_entry->symbol_status.state == SymbolState::kPublic) {
+ // Only copy the ID if the source is public, or else the ID is
+ // meaningless.
+ dst_entry->id = src_entry->id;
}
- return true;
+ dst_entry->symbol_status = std::move(src_entry->symbol_status);
+ } else if (src_entry->symbol_status.state == SymbolState::kPublic &&
+ dst_entry->symbol_status.state == SymbolState::kPublic &&
+ dst_entry->id && src_entry->id &&
+ dst_entry->id.value() != src_entry->id.value()) {
+ // Both entries are public and have different IDs.
+ context->GetDiagnostics()->Error(
+ DiagMessage(src) << "cannot merge entry '" << src_entry->name
+ << "': conflicting public IDs");
+ return false;
+ }
+ return true;
}
/**
* Modified CollisionResolver which will merge Styleables. Used with overlays.
*
* Styleables are not actual resources, but they are treated as such during the
- * compilation phase. Styleables don't simply overlay each other, their definitions merge
- * and accumulate. If both values are Styleables, we just merge them into the existing value.
+ * compilation phase. Styleables don't simply overlay each other, their
+ * definitions merge
+ * and accumulate. If both values are Styleables, we just merge them into the
+ * existing value.
*/
-static ResourceTable::CollisionResult resolveMergeCollision(Value* existing, Value* incoming) {
- if (Styleable* existingStyleable = valueCast<Styleable>(existing)) {
- if (Styleable* incomingStyleable = valueCast<Styleable>(incoming)) {
- // Styleables get merged.
- existingStyleable->mergeWith(incomingStyleable);
- return ResourceTable::CollisionResult::kKeepOriginal;
- }
+static ResourceTable::CollisionResult ResolveMergeCollision(Value* existing,
+ Value* incoming) {
+ if (Styleable* existing_styleable = ValueCast<Styleable>(existing)) {
+ if (Styleable* incoming_styleable = ValueCast<Styleable>(incoming)) {
+ // Styleables get merged.
+ existing_styleable->MergeWith(incoming_styleable);
+ return ResourceTable::CollisionResult::kKeepOriginal;
}
- // Delegate to the default handler.
- return ResourceTable::resolveValueCollision(existing, incoming);
+ }
+ // Delegate to the default handler.
+ return ResourceTable::ResolveValueCollision(existing, incoming);
}
-static ResourceTable::CollisionResult mergeConfigValue(IAaptContext* context,
- const ResourceNameRef& resName,
- const bool overlay,
- ResourceConfigValue* dstConfigValue,
- ResourceConfigValue* srcConfigValue) {
- using CollisionResult = ResourceTable::CollisionResult;
+static ResourceTable::CollisionResult MergeConfigValue(
+ IAaptContext* context, const ResourceNameRef& res_name, const bool overlay,
+ ResourceConfigValue* dst_config_value,
+ ResourceConfigValue* src_config_value) {
+ using CollisionResult = ResourceTable::CollisionResult;
- Value* dstValue = dstConfigValue->value.get();
- Value* srcValue = srcConfigValue->value.get();
+ Value* dst_value = dst_config_value->value.get();
+ Value* src_value = src_config_value->value.get();
- CollisionResult collisionResult;
+ CollisionResult collision_result;
+ if (overlay) {
+ collision_result = ResolveMergeCollision(dst_value, src_value);
+ } else {
+ collision_result =
+ ResourceTable::ResolveValueCollision(dst_value, src_value);
+ }
+
+ if (collision_result == CollisionResult::kConflict) {
if (overlay) {
- collisionResult = resolveMergeCollision(dstValue, srcValue);
- } else {
- collisionResult = ResourceTable::resolveValueCollision(dstValue, srcValue);
+ return CollisionResult::kTakeNew;
}
- if (collisionResult == CollisionResult::kConflict) {
- if (overlay) {
- return CollisionResult::kTakeNew;
- }
-
- // Error!
- context->getDiagnostics()->error(DiagMessage(srcValue->getSource())
- << "resource '" << resName
- << "' has a conflicting value for "
- << "configuration ("
- << srcConfigValue->config << ")");
- context->getDiagnostics()->note(DiagMessage(dstValue->getSource())
- << "originally defined here");
- return CollisionResult::kConflict;
- }
- return collisionResult;
+ // Error!
+ context->GetDiagnostics()->Error(
+ DiagMessage(src_value->GetSource())
+ << "resource '" << res_name << "' has a conflicting value for "
+ << "configuration (" << src_config_value->config << ")");
+ context->GetDiagnostics()->Note(DiagMessage(dst_value->GetSource())
+ << "originally defined here");
+ return CollisionResult::kConflict;
+ }
+ return collision_result;
}
-bool TableMerger::doMerge(const Source& src,
- ResourceTable* srcTable,
- ResourceTablePackage* srcPackage,
- const bool manglePackage,
- const bool overlay,
- const bool allowNewResources,
+bool TableMerger::DoMerge(const Source& src, ResourceTable* src_table,
+ ResourceTablePackage* src_package,
+ const bool mangle_package, const bool overlay,
+ const bool allow_new_resources,
const FileMergeCallback& callback) {
- bool error = false;
+ bool error = false;
- for (auto& srcType : srcPackage->types) {
- ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
- if (!mergeType(mContext, src, dstType, srcType.get())) {
+ for (auto& src_type : src_package->types) {
+ ResourceTableType* dst_type =
+ master_package_->FindOrCreateType(src_type->type);
+ if (!MergeType(context_, src, dst_type, src_type.get())) {
+ error = true;
+ continue;
+ }
+
+ for (auto& src_entry : src_type->entries) {
+ std::string entry_name = src_entry->name;
+ if (mangle_package) {
+ entry_name =
+ NameMangler::MangleEntry(src_package->name, src_entry->name);
+ }
+
+ ResourceEntry* dst_entry;
+ if (allow_new_resources) {
+ dst_entry = dst_type->FindOrCreateEntry(entry_name);
+ } else {
+ dst_entry = dst_type->FindEntry(entry_name);
+ }
+
+ const ResourceNameRef res_name(src_package->name, src_type->type,
+ src_entry->name);
+
+ if (!dst_entry) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(src) << "resource " << res_name
+ << " does not override an existing resource");
+ context_->GetDiagnostics()->Note(
+ DiagMessage(src) << "define an <add-resource> tag or use "
+ << "--auto-add-overlay");
+ error = true;
+ continue;
+ }
+
+ if (!MergeEntry(context_, src, dst_entry, src_entry.get())) {
+ error = true;
+ continue;
+ }
+
+ for (auto& src_config_value : src_entry->values) {
+ using CollisionResult = ResourceTable::CollisionResult;
+
+ ResourceConfigValue* dst_config_value = dst_entry->FindValue(
+ src_config_value->config, src_config_value->product);
+ if (dst_config_value) {
+ CollisionResult collision_result =
+ MergeConfigValue(context_, res_name, overlay, dst_config_value,
+ src_config_value.get());
+ if (collision_result == CollisionResult::kConflict) {
error = true;
continue;
+ } else if (collision_result == CollisionResult::kKeepOriginal) {
+ continue;
+ }
+ } else {
+ dst_config_value = dst_entry->FindOrCreateValue(
+ src_config_value->config, src_config_value->product);
}
- for (auto& srcEntry : srcType->entries) {
- std::string entryName = srcEntry->name;
- if (manglePackage) {
- entryName = NameMangler::mangleEntry(srcPackage->name, srcEntry->name);
+ // Continue if we're taking the new resource.
+
+ if (FileReference* f =
+ ValueCast<FileReference>(src_config_value->value.get())) {
+ std::unique_ptr<FileReference> new_file_ref;
+ if (mangle_package) {
+ new_file_ref = CloneAndMangleFile(src_package->name, *f);
+ } else {
+ new_file_ref = std::unique_ptr<FileReference>(
+ f->Clone(&master_table_->string_pool));
+ }
+
+ if (callback) {
+ if (!callback(res_name, src_config_value->config,
+ new_file_ref.get(), f)) {
+ error = true;
+ continue;
}
+ }
+ dst_config_value->value = std::move(new_file_ref);
- ResourceEntry* dstEntry;
- if (allowNewResources) {
- dstEntry = dstType->findOrCreateEntry(entryName);
- } else {
- dstEntry = dstType->findEntry(entryName);
- }
-
- const ResourceNameRef resName(srcPackage->name, srcType->type, srcEntry->name);
-
- if (!dstEntry) {
- mContext->getDiagnostics()->error(DiagMessage(src)
- << "resource " << resName
- << " does not override an existing resource");
- mContext->getDiagnostics()->note(DiagMessage(src)
- << "define an <add-resource> tag or use "
- << "--auto-add-overlay");
- error = true;
- continue;
- }
-
- if (!mergeEntry(mContext, src, dstEntry, srcEntry.get())) {
- error = true;
- continue;
- }
-
- for (auto& srcConfigValue : srcEntry->values) {
- using CollisionResult = ResourceTable::CollisionResult;
-
- ResourceConfigValue* dstConfigValue = dstEntry->findValue(srcConfigValue->config,
- srcConfigValue->product);
- if (dstConfigValue) {
- CollisionResult collisionResult = mergeConfigValue(
- mContext, resName, overlay, dstConfigValue, srcConfigValue.get());
- if (collisionResult == CollisionResult::kConflict) {
- error = true;
- continue;
- } else if (collisionResult == CollisionResult::kKeepOriginal) {
- continue;
- }
- } else {
- dstConfigValue = dstEntry->findOrCreateValue(srcConfigValue->config,
- srcConfigValue->product);
- }
-
- // Continue if we're taking the new resource.
-
- if (FileReference* f = valueCast<FileReference>(srcConfigValue->value.get())) {
- std::unique_ptr<FileReference> newFileRef;
- if (manglePackage) {
- newFileRef = cloneAndMangleFile(srcPackage->name, *f);
- } else {
- newFileRef = std::unique_ptr<FileReference>(f->clone(
- &mMasterTable->stringPool));
- }
-
- if (callback) {
- if (!callback(resName, srcConfigValue->config, newFileRef.get(), f)) {
- error = true;
- continue;
- }
- }
- dstConfigValue->value = std::move(newFileRef);
-
- } else {
- dstConfigValue->value = std::unique_ptr<Value>(srcConfigValue->value->clone(
- &mMasterTable->stringPool));
- }
- }
+ } else {
+ dst_config_value->value = std::unique_ptr<Value>(
+ src_config_value->value->Clone(&master_table_->string_pool));
}
+ }
}
- return !error;
+ }
+ return !error;
}
-std::unique_ptr<FileReference> TableMerger::cloneAndMangleFile(const std::string& package,
- const FileReference& fileRef) {
- StringPiece prefix, entry, suffix;
- if (util::extractResFilePathParts(*fileRef.path, &prefix, &entry, &suffix)) {
- std::string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
- std::string newPath = prefix.toString() + mangledEntry + suffix.toString();
- std::unique_ptr<FileReference> newFileRef = util::make_unique<FileReference>(
- mMasterTable->stringPool.makeRef(newPath));
- newFileRef->setComment(fileRef.getComment());
- newFileRef->setSource(fileRef.getSource());
- return newFileRef;
- }
- return std::unique_ptr<FileReference>(fileRef.clone(&mMasterTable->stringPool));
+std::unique_ptr<FileReference> TableMerger::CloneAndMangleFile(
+ const std::string& package, const FileReference& file_ref) {
+ StringPiece prefix, entry, suffix;
+ if (util::ExtractResFilePathParts(*file_ref.path, &prefix, &entry, &suffix)) {
+ std::string mangled_entry =
+ NameMangler::MangleEntry(package, entry.ToString());
+ std::string newPath = prefix.ToString() + mangled_entry + suffix.ToString();
+ std::unique_ptr<FileReference> new_file_ref =
+ util::make_unique<FileReference>(
+ master_table_->string_pool.MakeRef(newPath));
+ new_file_ref->SetComment(file_ref.GetComment());
+ new_file_ref->SetSource(file_ref.GetSource());
+ return new_file_ref;
+ }
+ return std::unique_ptr<FileReference>(
+ file_ref.Clone(&master_table_->string_pool));
}
-bool TableMerger::mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay) {
- ResourceTable table;
- std::string path = ResourceUtils::buildResourceFileName(fileDesc, nullptr);
- std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
- table.stringPool.makeRef(path));
- fileRef->setSource(fileDesc.source);
- fileRef->file = file;
+bool TableMerger::MergeFileImpl(const ResourceFile& file_desc, io::IFile* file,
+ bool overlay) {
+ ResourceTable table;
+ std::string path = ResourceUtils::BuildResourceFileName(file_desc);
+ std::unique_ptr<FileReference> file_ref =
+ util::make_unique<FileReference>(table.string_pool.MakeRef(path));
+ file_ref->SetSource(file_desc.source);
+ file_ref->file = file;
- ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
- pkg->findOrCreateType(fileDesc.name.type)
- ->findOrCreateEntry(fileDesc.name.entry)
- ->findOrCreateValue(fileDesc.config, {})
- ->value = std::move(fileRef);
+ ResourceTablePackage* pkg = table.CreatePackage(file_desc.name.package, 0x0);
+ pkg->FindOrCreateType(file_desc.name.type)
+ ->FindOrCreateEntry(file_desc.name.entry)
+ ->FindOrCreateValue(file_desc.config, {})
+ ->value = std::move(file_ref);
- return doMerge(file->getSource(), &table, pkg,
- false /* mangle */, overlay /* overlay */, true /* allow new */, {});
+ return DoMerge(file->GetSource(), &table, pkg, false /* mangle */,
+ overlay /* overlay */, true /* allow_new */, {});
}
-bool TableMerger::mergeFile(const ResourceFile& fileDesc, io::IFile* file) {
- return mergeFileImpl(fileDesc, file, false /* overlay */);
+bool TableMerger::MergeFile(const ResourceFile& file_desc, io::IFile* file) {
+ return MergeFileImpl(file_desc, file, false /* overlay */);
}
-bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file) {
- return mergeFileImpl(fileDesc, file, true /* overlay */);
+bool TableMerger::MergeFileOverlay(const ResourceFile& file_desc,
+ io::IFile* file) {
+ return MergeFileImpl(file_desc, file, true /* overlay */);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index 3473a27..4ab83c3 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -17,6 +17,11 @@
#ifndef AAPT_TABLEMERGER_H
#define AAPT_TABLEMERGER_H
+#include <functional>
+#include <map>
+
+#include "android-base/macros.h"
+
#include "Resource.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
@@ -25,102 +30,114 @@
#include "process/IResourceTableConsumer.h"
#include "util/Util.h"
-#include <functional>
-#include <map>
-
namespace aapt {
struct TableMergerOptions {
- /**
- * If true, resources in overlays can be added without previously having existed.
- */
- bool autoAddOverlay = false;
+ /**
+ * If true, resources in overlays can be added without previously having
+ * existed.
+ */
+ bool auto_add_overlay = false;
};
/**
- * TableMerger takes resource tables and merges all packages within the tables that have the same
+ * TableMerger takes resource tables and merges all packages within the tables
+ * that have the same
* package ID.
*
- * If a package has a different name, all the entries in that table have their names mangled
- * to include the package name. This way there are no collisions. In order to do this correctly,
- * the TableMerger needs to also mangle any FileReference paths. Once these are mangled,
- * the original source path of the file, along with the new destination path is recorded in the
+ * If a package has a different name, all the entries in that table have their
+ * names mangled
+ * to include the package name. This way there are no collisions. In order to do
+ * this correctly,
+ * the TableMerger needs to also mangle any FileReference paths. Once these are
+ * mangled,
+ * the original source path of the file, along with the new destination path is
+ * recorded in the
* queue returned from getFileMergeQueue().
*
- * Once the merging is complete, a separate process can go collect the files from the various
- * source APKs and either copy or process their XML and put them in the correct location in
+ * Once the merging is complete, a separate process can go collect the files
+ * from the various
+ * source APKs and either copy or process their XML and put them in the correct
+ * location in
* the final APK.
*/
class TableMerger {
-public:
- /**
- * Note: The outTable ResourceTable must live longer than this TableMerger. References
- * are made to this ResourceTable for efficiency reasons.
- */
- TableMerger(IAaptContext* context, ResourceTable* outTable, const TableMergerOptions& options);
+ public:
+ /**
+ * Note: The out_table ResourceTable must live longer than this TableMerger.
+ * References are made to this ResourceTable for efficiency reasons.
+ */
+ TableMerger(IAaptContext* context, ResourceTable* out_table,
+ const TableMergerOptions& options);
- const std::set<std::string>& getMergedPackages() const {
- return mMergedPackages;
- }
+ const std::set<std::string>& merged_packages() const {
+ return merged_packages_;
+ }
- /**
- * Merges resources from the same or empty package. This is for local sources.
- * An io::IFileCollection is optional and used to find the referenced Files and process them.
- */
- bool merge(const Source& src, ResourceTable* table,
- io::IFileCollection* collection = nullptr);
+ /**
+ * Merges resources from the same or empty package. This is for local sources.
+ * An io::IFileCollection is optional and used to find the referenced Files
+ * and process them.
+ */
+ bool Merge(const Source& src, ResourceTable* table,
+ io::IFileCollection* collection = nullptr);
- /**
- * Merges resources from an overlay ResourceTable.
- * An io::IFileCollection is optional and used to find the referenced Files and process them.
- */
- bool mergeOverlay(const Source& src, ResourceTable* table,
- io::IFileCollection* collection = nullptr);
+ /**
+ * Merges resources from an overlay ResourceTable.
+ * An io::IFileCollection is optional and used to find the referenced Files
+ * and process them.
+ */
+ bool MergeOverlay(const Source& src, ResourceTable* table,
+ io::IFileCollection* collection = nullptr);
- /**
- * Merges resources from the given package, mangling the name. This is for static libraries.
- * An io::IFileCollection is needed in order to find the referenced Files and process them.
- */
- bool mergeAndMangle(const Source& src, const StringPiece& package, ResourceTable* table,
- io::IFileCollection* collection);
+ /**
+ * Merges resources from the given package, mangling the name. This is for
+ * static libraries.
+ * An io::IFileCollection is needed in order to find the referenced Files and
+ * process them.
+ */
+ bool MergeAndMangle(const Source& src, const StringPiece& package,
+ ResourceTable* table, io::IFileCollection* collection);
- /**
- * Merges a compiled file that belongs to this same or empty package. This is for local sources.
- */
- bool mergeFile(const ResourceFile& fileDesc, io::IFile* file);
+ /**
+ * Merges a compiled file that belongs to this same or empty package. This is
+ * for local sources.
+ */
+ bool MergeFile(const ResourceFile& fileDesc, io::IFile* file);
- /**
- * Merges a compiled file from an overlay, overriding an existing definition.
- */
- bool mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file);
+ /**
+ * Merges a compiled file from an overlay, overriding an existing definition.
+ */
+ bool MergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file);
-private:
- using FileMergeCallback = std::function<bool(const ResourceNameRef&,
- const ConfigDescription& config,
- FileReference*, FileReference*)>;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TableMerger);
- IAaptContext* mContext;
- ResourceTable* mMasterTable;
- TableMergerOptions mOptions;
- ResourceTablePackage* mMasterPackage;
+ using FileMergeCallback = std::function<bool(const ResourceNameRef&,
+ const ConfigDescription& config,
+ FileReference*, FileReference*)>;
- std::set<std::string> mMergedPackages;
+ IAaptContext* context_;
+ ResourceTable* master_table_;
+ TableMergerOptions options_;
+ ResourceTablePackage* master_package_;
+ std::set<std::string> merged_packages_;
- bool mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay);
+ bool MergeFileImpl(const ResourceFile& file_desc, io::IFile* file,
+ bool overlay);
- bool mergeImpl(const Source& src, ResourceTable* srcTable, io::IFileCollection* collection,
- bool overlay, bool allowNew);
+ bool MergeImpl(const Source& src, ResourceTable* src_table,
+ io::IFileCollection* collection, bool overlay, bool allow_new);
- bool doMerge(const Source& src, ResourceTable* srcTable, ResourceTablePackage* srcPackage,
- const bool manglePackage,
- const bool overlay,
- const bool allowNewResources,
- const FileMergeCallback& callback);
+ bool DoMerge(const Source& src, ResourceTable* src_table,
+ ResourceTablePackage* src_package, const bool mangle_package,
+ const bool overlay, const bool allow_new_resources,
+ const FileMergeCallback& callback);
- std::unique_ptr<FileReference> cloneAndMangleFile(const std::string& package,
- const FileReference& value);
+ std::unique_ptr<FileReference> CloneAndMangleFile(const std::string& package,
+ const FileReference& value);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_TABLEMERGER_H */
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index fb1cb21..742f5a7 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -14,303 +14,334 @@
* limitations under the License.
*/
+#include "link/TableMerger.h"
+
#include "filter/ConfigFilter.h"
#include "io/FileSystem.h"
-#include "link/TableMerger.h"
-#include "test/Builders.h"
-#include "test/Context.h"
-
-#include <gtest/gtest.h>
+#include "test/Test.h"
namespace aapt {
struct TableMergerTest : public ::testing::Test {
- std::unique_ptr<IAaptContext> mContext;
+ std::unique_ptr<IAaptContext> context_;
- void SetUp() override {
- mContext = test::ContextBuilder()
- // We are compiling this package.
- .setCompilationPackage("com.app.a")
+ void SetUp() override {
+ context_ =
+ test::ContextBuilder()
+ // We are compiling this package.
+ .SetCompilationPackage("com.app.a")
- // Merge all packages that have this package ID.
- .setPackageId(0x7f)
+ // Merge all packages that have this package ID.
+ .SetPackageId(0x7f)
- // Mangle all packages that do not have this package name.
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.a", { "com.app.b" } })
+ // Mangle all packages that do not have this package name.
+ .SetNameManglerPolicy(NameManglerPolicy{"com.app.a", {"com.app.b"}})
- .build();
- }
+ .Build();
+ }
};
TEST_F(TableMergerTest, SimpleMerge) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .setPackageId("com.app.a", 0x7f)
- .addReference("com.app.a:id/foo", "com.app.a:id/bar")
- .addReference("com.app.a:id/bar", "com.app.b:id/foo")
- .addValue("com.app.a:styleable/view", test::StyleableBuilder()
- .addItem("com.app.b:id/foo")
- .build())
- .build();
+ std::unique_ptr<ResourceTable> table_a =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .AddReference("com.app.a:id/foo", "com.app.a:id/bar")
+ .AddReference("com.app.a:id/bar", "com.app.b:id/foo")
+ .AddValue(
+ "com.app.a:styleable/view",
+ test::StyleableBuilder().AddItem("com.app.b:id/foo").Build())
+ .Build();
- std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
- .setPackageId("com.app.b", 0x7f)
- .addSimple("com.app.b:id/foo")
- .build();
+ std::unique_ptr<ResourceTable> table_b = test::ResourceTableBuilder()
+ .SetPackageId("com.app.b", 0x7f)
+ .AddSimple("com.app.b:id/foo")
+ .Build();
- ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
- io::FileCollection collection;
+ ResourceTable final_table;
+ TableMerger merger(context_.get(), &final_table, TableMergerOptions{});
+ io::FileCollection collection;
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeAndMangle({}, "com.app.b", tableB.get(), &collection));
+ ASSERT_TRUE(merger.Merge({}, table_a.get()));
+ ASSERT_TRUE(
+ merger.MergeAndMangle({}, "com.app.b", table_b.get(), &collection));
- EXPECT_TRUE(merger.getMergedPackages().count("com.app.b") != 0);
+ EXPECT_TRUE(merger.merged_packages().count("com.app.b") != 0);
- // Entries from com.app.a should not be mangled.
- AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie("com.app.a:id/foo")));
- AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie("com.app.a:id/bar")));
- AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie("com.app.a:styleable/view")));
+ // Entries from com.app.a should not be mangled.
+ AAPT_EXPECT_TRUE(
+ final_table.FindResource(test::ParseNameOrDie("com.app.a:id/foo")));
+ AAPT_EXPECT_TRUE(
+ final_table.FindResource(test::ParseNameOrDie("com.app.a:id/bar")));
+ AAPT_EXPECT_TRUE(final_table.FindResource(
+ test::ParseNameOrDie("com.app.a:styleable/view")));
- // The unmangled name should not be present.
- AAPT_EXPECT_FALSE(finalTable.findResource(test::parseNameOrDie("com.app.b:id/foo")));
+ // The unmangled name should not be present.
+ AAPT_EXPECT_FALSE(
+ final_table.FindResource(test::ParseNameOrDie("com.app.b:id/foo")));
- // Look for the mangled name.
- AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie("com.app.a:id/com.app.b$foo")));
+ // Look for the mangled name.
+ AAPT_EXPECT_TRUE(final_table.FindResource(
+ test::ParseNameOrDie("com.app.a:id/com.app.b$foo")));
}
TEST_F(TableMergerTest, MergeFile) {
- ResourceTable finalTable;
- TableMergerOptions options;
- options.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, options);
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = false;
+ TableMerger merger(context_.get(), &final_table, options);
- ResourceFile fileDesc;
- fileDesc.config = test::parseConfigOrDie("hdpi-v4");
- fileDesc.name = test::parseNameOrDie("layout/main");
- fileDesc.source = Source("res/layout-hdpi/main.xml");
- test::TestFile testFile("path/to/res/layout-hdpi/main.xml.flat");
+ ResourceFile file_desc;
+ file_desc.config = test::ParseConfigOrDie("hdpi-v4");
+ file_desc.name = test::ParseNameOrDie("layout/main");
+ file_desc.source = Source("res/layout-hdpi/main.xml");
+ test::TestFile test_file("path/to/res/layout-hdpi/main.xml.flat");
- ASSERT_TRUE(merger.mergeFile(fileDesc, &testFile));
+ ASSERT_TRUE(merger.MergeFile(file_desc, &test_file));
- FileReference* file = test::getValueForConfig<FileReference>(&finalTable,
- "com.app.a:layout/main",
- test::parseConfigOrDie("hdpi-v4"));
- ASSERT_NE(nullptr, file);
- EXPECT_EQ(std::string("res/layout-hdpi-v4/main.xml"), *file->path);
+ FileReference* file = test::GetValueForConfig<FileReference>(
+ &final_table, "com.app.a:layout/main", test::ParseConfigOrDie("hdpi-v4"));
+ ASSERT_NE(nullptr, file);
+ EXPECT_EQ(std::string("res/layout-hdpi-v4/main.xml"), *file->path);
}
TEST_F(TableMergerTest, MergeFileOverlay) {
- ResourceTable finalTable;
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = false;
+ TableMerger merger(context_.get(), &final_table, options);
- ResourceFile fileDesc;
- fileDesc.name = test::parseNameOrDie("xml/foo");
- test::TestFile fileA("path/to/fileA.xml.flat");
- test::TestFile fileB("path/to/fileB.xml.flat");
+ ResourceFile file_desc;
+ file_desc.name = test::ParseNameOrDie("xml/foo");
+ test::TestFile file_a("path/to/fileA.xml.flat");
+ test::TestFile file_b("path/to/fileB.xml.flat");
- ASSERT_TRUE(merger.mergeFile(fileDesc, &fileA));
- ASSERT_TRUE(merger.mergeFileOverlay(fileDesc, &fileB));
+ ASSERT_TRUE(merger.MergeFile(file_desc, &file_a));
+ ASSERT_TRUE(merger.MergeFileOverlay(file_desc, &file_b));
}
TEST_F(TableMergerTest, MergeFileReferences) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .setPackageId("com.app.a", 0x7f)
- .addFileReference("com.app.a:xml/file", "res/xml/file.xml")
- .build();
- std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
- .setPackageId("com.app.b", 0x7f)
- .addFileReference("com.app.b:xml/file", "res/xml/file.xml")
- .build();
+ std::unique_ptr<ResourceTable> table_a =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .AddFileReference("com.app.a:xml/file", "res/xml/file.xml")
+ .Build();
+ std::unique_ptr<ResourceTable> table_b =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.b", 0x7f)
+ .AddFileReference("com.app.b:xml/file", "res/xml/file.xml")
+ .Build();
- ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
- io::FileCollection collection;
- collection.insertFile("res/xml/file.xml");
+ ResourceTable final_table;
+ TableMerger merger(context_.get(), &final_table, TableMergerOptions{});
+ io::FileCollection collection;
+ collection.InsertFile("res/xml/file.xml");
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeAndMangle({}, "com.app.b", tableB.get(), &collection));
+ ASSERT_TRUE(merger.Merge({}, table_a.get()));
+ ASSERT_TRUE(
+ merger.MergeAndMangle({}, "com.app.b", table_b.get(), &collection));
- FileReference* f = test::getValue<FileReference>(&finalTable, "com.app.a:xml/file");
- ASSERT_NE(f, nullptr);
- EXPECT_EQ(std::string("res/xml/file.xml"), *f->path);
+ FileReference* f =
+ test::GetValue<FileReference>(&final_table, "com.app.a:xml/file");
+ ASSERT_NE(f, nullptr);
+ EXPECT_EQ(std::string("res/xml/file.xml"), *f->path);
- f = test::getValue<FileReference>(&finalTable, "com.app.a:xml/com.app.b$file");
- ASSERT_NE(f, nullptr);
- EXPECT_EQ(std::string("res/xml/com.app.b$file.xml"), *f->path);
+ f = test::GetValue<FileReference>(&final_table,
+ "com.app.a:xml/com.app.b$file");
+ ASSERT_NE(f, nullptr);
+ EXPECT_EQ(std::string("res/xml/com.app.b$file.xml"), *f->path);
}
TEST_F(TableMergerTest, OverrideResourceWithOverlay) {
- std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder()
- .setPackageId("", 0x00)
- .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
- .build();
- std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
- .setPackageId("", 0x00)
- .addValue("bool/foo", ResourceUtils::tryParseBool("false"))
- .build();
+ std::unique_ptr<ResourceTable> base =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x00)
+ .AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
+ .Build();
+ std::unique_ptr<ResourceTable> overlay =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x00)
+ .AddValue("bool/foo", ResourceUtils::TryParseBool("false"))
+ .Build();
- ResourceTable finalTable;
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = false;
+ TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.merge({}, base.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.Merge({}, base.get()));
+ ASSERT_TRUE(merger.MergeOverlay({}, overlay.get()));
- BinaryPrimitive* foo = test::getValue<BinaryPrimitive>(&finalTable, "com.app.a:bool/foo");
- ASSERT_NE(nullptr, foo);
- EXPECT_EQ(0x0u, foo->value.data);
+ BinaryPrimitive* foo =
+ test::GetValue<BinaryPrimitive>(&final_table, "com.app.a:bool/foo");
+ ASSERT_NE(nullptr, foo);
+ EXPECT_EQ(0x0u, foo->value.data);
}
TEST_F(TableMergerTest, OverrideSameResourceIdsWithOverlay) {
- std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic)
- .build();
- std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic)
- .build();
+ std::unique_ptr<ResourceTable> base =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x7f)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
+ SymbolState::kPublic)
+ .Build();
+ std::unique_ptr<ResourceTable> overlay =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x7f)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
+ SymbolState::kPublic)
+ .Build();
- ResourceTable finalTable;
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = false;
+ TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.merge({}, base.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.Merge({}, base.get()));
+ ASSERT_TRUE(merger.MergeOverlay({}, overlay.get()));
}
TEST_F(TableMergerTest, FailToOverrideConflictingTypeIdsWithOverlay) {
- std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic)
- .build();
- std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x02, 0x0001), SymbolState::kPublic)
- .build();
+ std::unique_ptr<ResourceTable> base =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x7f)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
+ SymbolState::kPublic)
+ .Build();
+ std::unique_ptr<ResourceTable> overlay =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x7f)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x02, 0x0001),
+ SymbolState::kPublic)
+ .Build();
- ResourceTable finalTable;
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = false;
+ TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.merge({}, base.get()));
- ASSERT_FALSE(merger.mergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.Merge({}, base.get()));
+ ASSERT_FALSE(merger.MergeOverlay({}, overlay.get()));
}
TEST_F(TableMergerTest, FailToOverrideConflictingEntryIdsWithOverlay) {
- std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic)
- .build();
- std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0002), SymbolState::kPublic)
- .build();
+ std::unique_ptr<ResourceTable> base =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x7f)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
+ SymbolState::kPublic)
+ .Build();
+ std::unique_ptr<ResourceTable> overlay =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x7f)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0002),
+ SymbolState::kPublic)
+ .Build();
- ResourceTable finalTable;
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = false;
+ TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.merge({}, base.get()));
- ASSERT_FALSE(merger.mergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.Merge({}, base.get()));
+ ASSERT_FALSE(merger.MergeOverlay({}, overlay.get()));
}
TEST_F(TableMergerTest, MergeAddResourceFromOverlay) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", {}, SymbolState::kUndefined)
- .build();
- std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
- .build();
+ std::unique_ptr<ResourceTable> table_a =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x7f)
+ .SetSymbolState("bool/foo", {}, SymbolState::kUndefined)
+ .Build();
+ std::unique_ptr<ResourceTable> table_b =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x7f)
+ .AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
+ .Build();
- ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+ ResourceTable final_table;
+ TableMerger merger(context_.get(), &final_table, TableMergerOptions{});
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.Merge({}, table_a.get()));
+ ASSERT_TRUE(merger.MergeOverlay({}, table_b.get()));
}
TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .build();
- std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
- .build();
+ std::unique_ptr<ResourceTable> table_a =
+ test::ResourceTableBuilder().SetPackageId("", 0x7f).Build();
+ std::unique_ptr<ResourceTable> table_b =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x7f)
+ .AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
+ .Build();
- ResourceTable finalTable;
- TableMergerOptions options;
- options.autoAddOverlay = true;
- TableMerger merger(mContext.get(), &finalTable, options);
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = true;
+ TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.Merge({}, table_a.get()));
+ ASSERT_TRUE(merger.MergeOverlay({}, table_b.get()));
}
TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .build();
- std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
- .build();
+ std::unique_ptr<ResourceTable> table_a =
+ test::ResourceTableBuilder().SetPackageId("", 0x7f).Build();
+ std::unique_ptr<ResourceTable> table_b =
+ test::ResourceTableBuilder()
+ .SetPackageId("", 0x7f)
+ .AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
+ .Build();
- ResourceTable finalTable;
- TableMergerOptions options;
- options.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, options);
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = false;
+ TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_FALSE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.Merge({}, table_a.get()));
+ ASSERT_FALSE(merger.MergeOverlay({}, table_b.get()));
}
TEST_F(TableMergerTest, OverlaidStyleablesShouldBeMerged) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .setPackageId("com.app.a", 0x7f)
- .addValue("com.app.a:styleable/Foo", test::StyleableBuilder()
- .addItem("com.app.a:attr/bar")
- .addItem("com.app.a:attr/foo", ResourceId(0x01010000))
- .build())
- .build();
+ std::unique_ptr<ResourceTable> table_a =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .AddValue("com.app.a:styleable/Foo",
+ test::StyleableBuilder()
+ .AddItem("com.app.a:attr/bar")
+ .AddItem("com.app.a:attr/foo", ResourceId(0x01010000))
+ .Build())
+ .Build();
- std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
- .setPackageId("com.app.a", 0x7f)
- .addValue("com.app.a:styleable/Foo", test::StyleableBuilder()
- .addItem("com.app.a:attr/bat")
- .addItem("com.app.a:attr/foo")
- .build())
- .build();
+ std::unique_ptr<ResourceTable> table_b =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .AddValue("com.app.a:styleable/Foo",
+ test::StyleableBuilder()
+ .AddItem("com.app.a:attr/bat")
+ .AddItem("com.app.a:attr/foo")
+ .Build())
+ .Build();
- ResourceTable finalTable;
- TableMergerOptions options;
- options.autoAddOverlay = true;
- TableMerger merger(mContext.get(), &finalTable, options);
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = true;
+ TableMerger merger(context_.get(), &final_table, options);
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.Merge({}, table_a.get()));
+ ASSERT_TRUE(merger.MergeOverlay({}, table_b.get()));
- Debug::printTable(&finalTable, {});
+ Styleable* styleable =
+ test::GetValue<Styleable>(&final_table, "com.app.a:styleable/Foo");
+ ASSERT_NE(nullptr, styleable);
- Styleable* styleable = test::getValue<Styleable>(&finalTable, "com.app.a:styleable/Foo");
- ASSERT_NE(nullptr, styleable);
+ std::vector<Reference> expected_refs = {
+ Reference(test::ParseNameOrDie("com.app.a:attr/bar")),
+ Reference(test::ParseNameOrDie("com.app.a:attr/bat")),
+ Reference(test::ParseNameOrDie("com.app.a:attr/foo"),
+ ResourceId(0x01010000)),
+ };
- std::vector<Reference> expectedRefs = {
- Reference(test::parseNameOrDie("com.app.a:attr/bar")),
- Reference(test::parseNameOrDie("com.app.a:attr/bat")),
- Reference(test::parseNameOrDie("com.app.a:attr/foo"), ResourceId(0x01010000)),
- };
-
- EXPECT_EQ(expectedRefs, styleable->entries);
+ EXPECT_EQ(expected_refs, styleable->entries);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/VersionCollapser.cpp b/tools/aapt2/link/VersionCollapser.cpp
index 949d656..3df5899 100644
--- a/tools/aapt2/link/VersionCollapser.cpp
+++ b/tools/aapt2/link/VersionCollapser.cpp
@@ -14,139 +14,149 @@
* limitations under the License.
*/
-#include "ResourceTable.h"
#include "link/Linkers.h"
#include <algorithm>
#include <vector>
+#include "ResourceTable.h"
+
namespace aapt {
template <typename Iterator, typename Pred>
class FilterIterator {
-public:
- FilterIterator(Iterator begin, Iterator end, Pred pred=Pred()) :
- mCurrent(begin), mEnd(end), mPred(pred) {
- advance();
- }
+ public:
+ FilterIterator(Iterator begin, Iterator end, Pred pred = Pred())
+ : current_(begin), end_(end), pred_(pred) {
+ Advance();
+ }
- bool hasNext() {
- return mCurrent != mEnd;
- }
+ bool HasNext() { return current_ != end_; }
- Iterator nextIter() {
- Iterator iter = mCurrent;
- ++mCurrent;
- advance();
- return iter;
- }
+ Iterator NextIter() {
+ Iterator iter = current_;
+ ++current_;
+ Advance();
+ return iter;
+ }
- typename Iterator::reference next() {
- return *nextIter();
- }
+ typename Iterator::reference Next() { return *NextIter(); }
-private:
- void advance() {
- for (; mCurrent != mEnd; ++mCurrent) {
- if (mPred(*mCurrent)) {
- return;
- }
- }
+ private:
+ void Advance() {
+ for (; current_ != end_; ++current_) {
+ if (pred_(*current_)) {
+ return;
+ }
}
+ }
- Iterator mCurrent, mEnd;
- Pred mPred;
+ Iterator current_, end_;
+ Pred pred_;
};
template <typename Iterator, typename Pred>
-FilterIterator<Iterator, Pred> makeFilterIterator(Iterator begin, Iterator end=Iterator(),
- Pred pred=Pred()) {
- return FilterIterator<Iterator, Pred>(begin, end, pred);
+FilterIterator<Iterator, Pred> make_filter_iterator(Iterator begin,
+ Iterator end = Iterator(),
+ Pred pred = Pred()) {
+ return FilterIterator<Iterator, Pred>(begin, end, pred);
}
/**
- * Every Configuration with an SDK version specified that is less than minSdk will be removed.
- * The exception is when there is no exact matching resource for the minSdk. The next smallest
+ * Every Configuration with an SDK version specified that is less than minSdk
+ * will be removed.
+ * The exception is when there is no exact matching resource for the minSdk. The
+ * next smallest
* one will be kept.
*/
-static void collapseVersions(int minSdk, ResourceEntry* entry) {
- // First look for all sdks less than minSdk.
- for (auto iter = entry->values.rbegin(); iter != entry->values.rend(); ++iter) {
- // Check if the item was already marked for removal.
- if (!(*iter)) {
- continue;
- }
-
- const ConfigDescription& config = (*iter)->config;
- if (config.sdkVersion <= minSdk) {
- // This is the first configuration we've found with a smaller or equal SDK level
- // to the minimum. We MUST keep this one, but remove all others we find, which get
- // overridden by this one.
-
- ConfigDescription configWithoutSdk = config;
- configWithoutSdk.sdkVersion = 0;
- auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
- // Check that the value hasn't already been marked for removal.
- if (!val) {
- return false;
- }
-
- // Only return Configs that differ in SDK version.
- configWithoutSdk.sdkVersion = val->config.sdkVersion;
- return configWithoutSdk == val->config && val->config.sdkVersion <= minSdk;
- };
-
- // Remove the rest that match.
- auto filterIter = makeFilterIterator(iter + 1, entry->values.rend(), pred);
- while (filterIter.hasNext()) {
- filterIter.next() = {};
- }
- }
+static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
+ // First look for all sdks less than minSdk.
+ for (auto iter = entry->values.rbegin(); iter != entry->values.rend();
+ ++iter) {
+ // Check if the item was already marked for removal.
+ if (!(*iter)) {
+ continue;
}
- // Now erase the nullptr values.
- entry->values.erase(std::remove_if(entry->values.begin(), entry->values.end(),
- [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
- return val == nullptr;
- }), entry->values.end());
+ const ConfigDescription& config = (*iter)->config;
+ if (config.sdkVersion <= min_sdk) {
+ // This is the first configuration we've found with a smaller or equal SDK
+ // level
+ // to the minimum. We MUST keep this one, but remove all others we find,
+ // which get
+ // overridden by this one.
- // Strip the version qualifiers for every resource with version <= minSdk. This will ensure
- // that the resource entries are all packed together in the same ResTable_type struct
- // and take up less space in the resources.arsc table.
- bool modified = false;
- for (std::unique_ptr<ResourceConfigValue>& configValue : entry->values) {
- if (configValue->config.sdkVersion != 0 && configValue->config.sdkVersion <= minSdk) {
- // Override the resource with a Configuration without an SDK.
- std::unique_ptr<ResourceConfigValue> newValue = util::make_unique<ResourceConfigValue>(
- configValue->config.copyWithoutSdkVersion(), configValue->product);
- newValue->value = std::move(configValue->value);
- configValue = std::move(newValue);
-
- modified = true;
+ ConfigDescription config_without_sdk = config.CopyWithoutSdkVersion();
+ auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
+ // Check that the value hasn't already been marked for removal.
+ if (!val) {
+ return false;
}
- }
- if (modified) {
- // We've modified the keys (ConfigDescription) by changing the sdkVersion to 0.
- // We MUST re-sort to ensure ordering guarantees hold.
- std::sort(entry->values.begin(), entry->values.end(),
- [](const std::unique_ptr<ResourceConfigValue>& a,
- const std::unique_ptr<ResourceConfigValue>& b) -> bool {
- return a->config.compare(b->config) < 0;
- });
+ // Only return Configs that differ in SDK version.
+ config_without_sdk.sdkVersion = val->config.sdkVersion;
+ return config_without_sdk == val->config &&
+ val->config.sdkVersion <= min_sdk;
+ };
+
+ // Remove the rest that match.
+ auto filter_iter =
+ make_filter_iterator(iter + 1, entry->values.rend(), pred);
+ while (filter_iter.HasNext()) {
+ filter_iter.Next() = {};
+ }
}
+ }
+
+ // Now erase the nullptr values.
+ entry->values.erase(
+ std::remove_if(entry->values.begin(), entry->values.end(),
+ [](const std::unique_ptr<ResourceConfigValue>& val)
+ -> bool { return val == nullptr; }),
+ entry->values.end());
+
+ // Strip the version qualifiers for every resource with version <= minSdk.
+ // This will ensure
+ // that the resource entries are all packed together in the same ResTable_type
+ // struct
+ // and take up less space in the resources.arsc table.
+ bool modified = false;
+ for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
+ if (config_value->config.sdkVersion != 0 &&
+ config_value->config.sdkVersion <= min_sdk) {
+ // Override the resource with a Configuration without an SDK.
+ std::unique_ptr<ResourceConfigValue> new_value =
+ util::make_unique<ResourceConfigValue>(
+ config_value->config.CopyWithoutSdkVersion(),
+ config_value->product);
+ new_value->value = std::move(config_value->value);
+ config_value = std::move(new_value);
+
+ modified = true;
+ }
+ }
+
+ if (modified) {
+ // We've modified the keys (ConfigDescription) by changing the sdkVersion to
+ // 0. We MUST re-sort to ensure ordering guarantees hold.
+ std::sort(entry->values.begin(), entry->values.end(),
+ [](const std::unique_ptr<ResourceConfigValue>& a,
+ const std::unique_ptr<ResourceConfigValue>& b) -> bool {
+ return a->config.compare(b->config) < 0;
+ });
+ }
}
-bool VersionCollapser::consume(IAaptContext* context, ResourceTable* table) {
- const int minSdk = context->getMinSdkVersion();
- for (auto& package : table->packages) {
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- collapseVersions(minSdk, entry.get());
- }
- }
+bool VersionCollapser::Consume(IAaptContext* context, ResourceTable* table) {
+ const int min_sdk = context->GetMinSdkVersion();
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ CollapseVersions(min_sdk, entry.get());
+ }
}
- return true;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/VersionCollapser_test.cpp b/tools/aapt2/link/VersionCollapser_test.cpp
index dd5f1d1..1b5592f 100644
--- a/tools/aapt2/link/VersionCollapser_test.cpp
+++ b/tools/aapt2/link/VersionCollapser_test.cpp
@@ -15,89 +15,103 @@
*/
#include "link/Linkers.h"
+
#include "test/Test.h"
namespace aapt {
-template <typename T>
-using uptr = std::unique_ptr<T>;
-
-static uptr<ResourceTable> buildTableWithConfigs(const StringPiece& name,
- std::initializer_list<std::string> list) {
- test::ResourceTableBuilder builder;
- for (const std::string& item : list) {
- builder.addSimple(name, test::parseConfigOrDie(item));
- }
- return builder.build();
+static std::unique_ptr<ResourceTable> BuildTableWithConfigs(
+ const StringPiece& name, std::initializer_list<std::string> list) {
+ test::ResourceTableBuilder builder;
+ for (const std::string& item : list) {
+ builder.AddSimple(name, test::ParseConfigOrDie(item));
+ }
+ return builder.Build();
}
TEST(VersionCollapserTest, CollapseVersions) {
- uptr<IAaptContext> context = test::ContextBuilder().setMinSdkVersion(7).build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder().SetMinSdkVersion(7).Build();
- const StringPiece resName = "@android:string/foo";
+ const StringPiece res_name = "@android:string/foo";
- uptr<ResourceTable> table =
- buildTableWithConfigs(resName,
- { "land-v4", "land-v5", "sw600dp", "land-v6",
- "land-v14", "land-v21" });
+ std::unique_ptr<ResourceTable> table = BuildTableWithConfigs(
+ res_name,
+ {"land-v4", "land-v5", "sw600dp", "land-v6", "land-v14", "land-v21"});
- VersionCollapser collapser;
- ASSERT_TRUE(collapser.consume(context.get(), table.get()));
+ VersionCollapser collapser;
+ ASSERT_TRUE(collapser.Consume(context.get(), table.get()));
- // These should be removed.
- EXPECT_EQ(nullptr,
- test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v4")));
- EXPECT_EQ(nullptr,
- test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v5")));
- // This one should be removed because it was renamed to 'land', with the version dropped.
- EXPECT_EQ(nullptr,
- test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v6")));
+ // These should be removed.
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land-v4")));
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land-v5")));
+ // This one should be removed because it was renamed to 'land', with the
+ // version dropped.
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land-v6")));
- // These should remain.
- EXPECT_NE(nullptr,
- test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("sw600dp")));
+ // These should remain.
+ EXPECT_NE(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("sw600dp")));
- // 'land' should be present because it was renamed from 'land-v6'.
- EXPECT_NE(nullptr,
- test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land")));
- EXPECT_NE(nullptr,
- test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v14")));
- EXPECT_NE(nullptr,
- test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v21")));
+ // 'land' should be present because it was renamed from 'land-v6'.
+ EXPECT_NE(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land")));
+ EXPECT_NE(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land-v14")));
+ EXPECT_NE(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land-v21")));
}
TEST(VersionCollapserTest, CollapseVersionsWhenMinSdkIsHighest) {
- uptr<IAaptContext> context = test::ContextBuilder().setMinSdkVersion(21).build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder().SetMinSdkVersion(21).Build();
- const StringPiece resName = "@android:string/foo";
+ const StringPiece res_name = "@android:string/foo";
- uptr<ResourceTable> table =
- buildTableWithConfigs(resName,
- { "land-v4", "land-v5", "sw600dp", "land-v6",
- "land-v14", "land-v21", "land-v22" });
- VersionCollapser collapser;
- ASSERT_TRUE(collapser.consume(context.get(), table.get()));
+ std::unique_ptr<ResourceTable> table = BuildTableWithConfigs(
+ res_name, {"land-v4", "land-v5", "sw600dp", "land-v6", "land-v14",
+ "land-v21", "land-v22"});
+ VersionCollapser collapser;
+ ASSERT_TRUE(collapser.Consume(context.get(), table.get()));
- // These should all be removed.
- EXPECT_EQ(nullptr, test::getValueForConfig<Id>(table.get(), resName,
- test::parseConfigOrDie("land-v4")));
- EXPECT_EQ(nullptr, test::getValueForConfig<Id>(table.get(), resName,
- test::parseConfigOrDie("land-v5")));
- EXPECT_EQ(nullptr, test::getValueForConfig<Id>(table.get(), resName,
- test::parseConfigOrDie("land-v6")));
- EXPECT_EQ(nullptr, test::getValueForConfig<Id>(table.get(), resName,
- test::parseConfigOrDie("land-v14")));
+ // These should all be removed.
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land-v4")));
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land-v5")));
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land-v6")));
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land-v14")));
- // These should remain.
- EXPECT_NE(nullptr, test::getValueForConfig<Id>(
- table.get(), resName, test::parseConfigOrDie("sw600dp").copyWithoutSdkVersion()));
+ // These should remain.
+ EXPECT_NE(nullptr,
+ test::GetValueForConfig<Id>(
+ table.get(), res_name,
+ test::ParseConfigOrDie("sw600dp").CopyWithoutSdkVersion()));
- // land-v21 should have been converted to land.
- EXPECT_NE(nullptr, test::getValueForConfig<Id>(table.get(), resName,
- test::parseConfigOrDie("land")));
- // land-v22 should remain as-is.
- EXPECT_NE(nullptr, test::getValueForConfig<Id>(table.get(), resName,
- test::parseConfigOrDie("land-v22")));
+ // land-v21 should have been converted to land.
+ EXPECT_NE(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land")));
+ // land-v22 should remain as-is.
+ EXPECT_NE(nullptr,
+ test::GetValueForConfig<Id>(table.get(), res_name,
+ test::ParseConfigOrDie("land-v22")));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlNamespaceRemover.cpp b/tools/aapt2/link/XmlNamespaceRemover.cpp
index 9f95177..24aa566 100644
--- a/tools/aapt2/link/XmlNamespaceRemover.cpp
+++ b/tools/aapt2/link/XmlNamespaceRemover.cpp
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include "ResourceTable.h"
#include "link/Linkers.h"
#include <algorithm>
+#include "ResourceTable.h"
+
namespace aapt {
namespace {
@@ -27,57 +28,61 @@
* Visits each xml Node, removing URI references and nested namespaces.
*/
class XmlVisitor : public xml::Visitor {
-public:
- XmlVisitor(bool keepUris) : mKeepUris(keepUris) {
- }
+ public:
+ explicit XmlVisitor(bool keep_uris) : keep_uris_(keep_uris) {}
- void visit(xml::Element* el) override {
- // Strip namespaces
- for (auto& child : el->children) {
- while (child && xml::nodeCast<xml::Namespace>(child.get())) {
- if (child->children.empty()) {
- child = {};
- } else {
- child = std::move(child->children.front());
- child->parent = el;
- }
- }
+ void Visit(xml::Element* el) override {
+ // Strip namespaces
+ for (auto& child : el->children) {
+ while (child && xml::NodeCast<xml::Namespace>(child.get())) {
+ if (child->children.empty()) {
+ child = {};
+ } else {
+ child = std::move(child->children.front());
+ child->parent = el;
}
- el->children.erase(std::remove_if(el->children.begin(), el->children.end(),
- [](const std::unique_ptr<xml::Node>& child) -> bool {
- return child == nullptr;
- }), el->children.end());
-
- if (!mKeepUris) {
- for (xml::Attribute& attr : el->attributes) {
- attr.namespaceUri = std::string();
- }
- el->namespaceUri = std::string();
- }
- xml::Visitor::visit(el);
+ }
}
+ el->children.erase(
+ std::remove_if(el->children.begin(), el->children.end(),
+ [](const std::unique_ptr<xml::Node>& child) -> bool {
+ return child == nullptr;
+ }),
+ el->children.end());
-private:
- bool mKeepUris;
+ if (!keep_uris_) {
+ for (xml::Attribute& attr : el->attributes) {
+ attr.namespace_uri = std::string();
+ }
+ el->namespace_uri = std::string();
+ }
+ xml::Visitor::Visit(el);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XmlVisitor);
+
+ bool keep_uris_;
};
-} // namespace
+} // namespace
-bool XmlNamespaceRemover::consume(IAaptContext* context, xml::XmlResource* resource) {
- if (!resource->root) {
- return false;
+bool XmlNamespaceRemover::Consume(IAaptContext* context,
+ xml::XmlResource* resource) {
+ if (!resource->root) {
+ return false;
+ }
+ // Replace any root namespaces until the root is a non-namespace node
+ while (xml::NodeCast<xml::Namespace>(resource->root.get())) {
+ if (resource->root->children.empty()) {
+ break;
}
- // Replace any root namespaces until the root is a non-namespace node
- while (xml::nodeCast<xml::Namespace>(resource->root.get())) {
- if (resource->root->children.empty()) {
- break;
- }
- resource->root = std::move(resource->root->children.front());
- resource->root->parent = nullptr;
- }
- XmlVisitor visitor(mKeepUris);
- resource->root->accept(&visitor);
- return true;
+ resource->root = std::move(resource->root->children.front());
+ resource->root->parent = nullptr;
+ }
+ XmlVisitor visitor(keep_uris_);
+ resource->root->Accept(&visitor);
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlNamespaceRemover_test.cpp b/tools/aapt2/link/XmlNamespaceRemover_test.cpp
index e72ea439..a176c03 100644
--- a/tools/aapt2/link/XmlNamespaceRemover_test.cpp
+++ b/tools/aapt2/link/XmlNamespaceRemover_test.cpp
@@ -15,95 +15,108 @@
*/
#include "link/Linkers.h"
+
#include "test/Test.h"
namespace aapt {
class XmlUriTestVisitor : public xml::Visitor {
-public:
- void visit(xml::Element* el) override {
- for (const auto& attr : el->attributes) {
- EXPECT_EQ(std::string(), attr.namespaceUri);
- }
- EXPECT_EQ(std::string(), el->namespaceUri);
- xml::Visitor::visit(el);
- }
+ public:
+ XmlUriTestVisitor() = default;
- void visit(xml::Namespace* ns) override {
- EXPECT_EQ(std::string(), ns->namespaceUri);
- xml::Visitor::visit(ns);
+ void Visit(xml::Element* el) override {
+ for (const auto& attr : el->attributes) {
+ EXPECT_EQ(std::string(), attr.namespace_uri);
}
+ EXPECT_EQ(std::string(), el->namespace_uri);
+ xml::Visitor::Visit(el);
+ }
+
+ void Visit(xml::Namespace* ns) override {
+ EXPECT_EQ(std::string(), ns->namespace_uri);
+ xml::Visitor::Visit(ns);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XmlUriTestVisitor);
};
class XmlNamespaceTestVisitor : public xml::Visitor {
-public:
- void visit(xml::Namespace* ns) override {
- ADD_FAILURE() << "Detected namespace: "
- << ns->namespacePrefix << "=\"" << ns->namespaceUri << "\"";
- xml::Visitor::visit(ns);
- }
+ public:
+ XmlNamespaceTestVisitor() = default;
+
+ void Visit(xml::Namespace* ns) override {
+ ADD_FAILURE() << "Detected namespace: " << ns->namespace_prefix << "=\""
+ << ns->namespace_uri << "\"";
+ xml::Visitor::Visit(ns);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XmlNamespaceTestVisitor);
};
class XmlNamespaceRemoverTest : public ::testing::Test {
-public:
- void SetUp() override {
- mContext = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .build();
- }
+ public:
+ void SetUp() override {
+ context_ =
+ test::ContextBuilder().SetCompilationPackage("com.app.test").Build();
+ }
-protected:
- std::unique_ptr<IAaptContext> mContext;
+ protected:
+ std::unique_ptr<IAaptContext> context_;
};
TEST_F(XmlNamespaceRemoverTest, RemoveUris) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDomForPackageName(context_.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:text="hello" />)EOF");
- XmlNamespaceRemover remover;
- ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
+ XmlNamespaceRemover remover;
+ ASSERT_TRUE(remover.Consume(context_.get(), doc.get()));
- xml::Node* root = doc.get()->root.get();
- ASSERT_NE(root, nullptr);
+ xml::Node* root = doc.get()->root.get();
+ ASSERT_NE(root, nullptr);
- XmlUriTestVisitor visitor;
- root->accept(&visitor);
+ XmlUriTestVisitor visitor;
+ root->Accept(&visitor);
}
TEST_F(XmlNamespaceRemoverTest, RemoveNamespaces) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDomForPackageName(context_.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:foo="http://schemas.android.com/apk/res/foo"
foo:bar="foobar"
android:text="hello" />)EOF");
- XmlNamespaceRemover remover;
- ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
+ XmlNamespaceRemover remover;
+ ASSERT_TRUE(remover.Consume(context_.get(), doc.get()));
- xml::Node* root = doc.get()->root.get();
- ASSERT_NE(root, nullptr);
+ xml::Node* root = doc.get()->root.get();
+ ASSERT_NE(root, nullptr);
- XmlNamespaceTestVisitor visitor;
- root->accept(&visitor);
+ XmlNamespaceTestVisitor visitor;
+ root->Accept(&visitor);
}
TEST_F(XmlNamespaceRemoverTest, RemoveNestedNamespaces) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDomForPackageName(context_.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:text="hello">
<View xmlns:foo="http://schemas.example.com/foo"
android:text="foo"/>
</View>)EOF");
- XmlNamespaceRemover remover;
- ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
+ XmlNamespaceRemover remover;
+ ASSERT_TRUE(remover.Consume(context_.get(), doc.get()));
- xml::Node* root = doc.get()->root.get();
- ASSERT_NE(root, nullptr);
+ xml::Node* root = doc.get()->root.get();
+ ASSERT_NE(root, nullptr);
- XmlNamespaceTestVisitor visitor;
- root->accept(&visitor);
+ XmlNamespaceTestVisitor visitor;
+ root->Accept(&visitor);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index 59ffe15..a819831 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+#include "link/Linkers.h"
+
#include "Diagnostics.h"
#include "ResourceUtils.h"
#include "SdkConstants.h"
-#include "link/Linkers.h"
#include "link/ReferenceLinker.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
@@ -29,146 +30,161 @@
namespace {
/**
- * Visits all references (including parents of styles, references in styles, arrays, etc) and
- * links their symbolic name to their Resource ID, performing mangling and package aliasing
+ * Visits all references (including parents of styles, references in styles,
+ * arrays, etc) and
+ * links their symbolic name to their Resource ID, performing mangling and
+ * package aliasing
* as needed.
*/
class ReferenceVisitor : public ValueVisitor {
-public:
- using ValueVisitor::visit;
+ public:
+ using ValueVisitor::Visit;
- ReferenceVisitor(IAaptContext* context, SymbolTable* symbols, xml::IPackageDeclStack* decls,
- CallSite* callSite) :
- mContext(context), mSymbols(symbols), mDecls(decls), mCallSite(callSite),
- mError(false) {
+ ReferenceVisitor(IAaptContext* context, SymbolTable* symbols,
+ xml::IPackageDeclStack* decls, CallSite* callsite)
+ : context_(context),
+ symbols_(symbols),
+ decls_(decls),
+ callsite_(callsite),
+ error_(false) {}
+
+ void Visit(Reference* ref) override {
+ if (!ReferenceLinker::LinkReference(ref, context_, symbols_, decls_,
+ callsite_)) {
+ error_ = true;
}
+ }
- void visit(Reference* ref) override {
- if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mDecls, mCallSite)) {
- mError = true;
- }
- }
+ bool HasError() const { return error_; }
- bool hasError() const {
- return mError;
- }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor);
-private:
- IAaptContext* mContext;
- SymbolTable* mSymbols;
- xml::IPackageDeclStack* mDecls;
- CallSite* mCallSite;
- bool mError;
+ IAaptContext* context_;
+ SymbolTable* symbols_;
+ xml::IPackageDeclStack* decls_;
+ CallSite* callsite_;
+ bool error_;
};
/**
* Visits each xml Element and compiles the attributes within.
*/
class XmlVisitor : public xml::PackageAwareVisitor {
-public:
- using xml::PackageAwareVisitor::visit;
+ public:
+ using xml::PackageAwareVisitor::Visit;
- XmlVisitor(IAaptContext* context, SymbolTable* symbols, const Source& source,
- std::set<int>* sdkLevelsFound, CallSite* callSite) :
- mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound),
- mCallSite(callSite), mReferenceVisitor(context, symbols, this, callSite) {
- }
+ XmlVisitor(IAaptContext* context, SymbolTable* symbols, const Source& source,
+ std::set<int>* sdk_levels_found, CallSite* callsite)
+ : context_(context),
+ symbols_(symbols),
+ source_(source),
+ sdk_levels_found_(sdk_levels_found),
+ callsite_(callsite),
+ reference_visitor_(context, symbols, this, callsite) {}
- void visit(xml::Element* el) override {
- const Source source = mSource.withLine(el->lineNumber);
- for (xml::Attribute& attr : el->attributes) {
- Maybe<xml::ExtractedPackage> maybePackage =
- xml::extractPackageFromNamespace(attr.namespaceUri);
- if (maybePackage) {
- // There is a valid package name for this attribute. We will look this up.
- StringPiece package = maybePackage.value().package;
- if (package.empty()) {
- // Empty package means the 'current' or 'local' package.
- package = mContext->getCompilationPackage();
- }
-
- Reference attrRef(ResourceNameRef(package, ResourceType::kAttr, attr.name));
- attrRef.privateReference = maybePackage.value().privateNamespace;
-
- std::string errStr;
- attr.compiledAttribute = ReferenceLinker::compileXmlAttribute(
- attrRef, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
-
- // Convert the string value into a compiled Value if this is a valid attribute.
- if (attr.compiledAttribute) {
- if (attr.compiledAttribute.value().id) {
- // Record all SDK levels from which the attributes were defined.
- const size_t sdkLevel = findAttributeSdkLevel(
- attr.compiledAttribute.value().id.value());
- if (sdkLevel > 1) {
- mSdkLevelsFound->insert(sdkLevel);
- }
- }
-
- const Attribute* attribute = &attr.compiledAttribute.value().attribute;
- attr.compiledValue = ResourceUtils::tryParseItemForAttribute(attr.value,
- attribute);
- if (!attr.compiledValue &&
- !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) {
- // We won't be able to encode this as a string.
- mContext->getDiagnostics()->error(
- DiagMessage(source) << "'" << attr.value << "' "
- << "is incompatible with attribute "
- << package << ":" << attr.name << " "
- << *attribute);
- mError = true;
- }
-
- } else {
- mContext->getDiagnostics()->error(DiagMessage(source)
- << "attribute '" << package << ":"
- << attr.name << "' " << errStr);
- mError = true;
-
- }
- } else if (!attr.compiledValue) {
- // We still encode references, but only if we haven't manually set this to
- // another compiled value.
- attr.compiledValue = ResourceUtils::tryParseReference(attr.value);
- }
-
- if (attr.compiledValue) {
- // With a compiledValue, we must resolve the reference and assign it an ID.
- attr.compiledValue->setSource(source);
- attr.compiledValue->accept(&mReferenceVisitor);
- }
+ void Visit(xml::Element* el) override {
+ const Source source = source_.WithLine(el->line_number);
+ for (xml::Attribute& attr : el->attributes) {
+ Maybe<xml::ExtractedPackage> maybe_package =
+ xml::ExtractPackageFromNamespace(attr.namespace_uri);
+ if (maybe_package) {
+ // There is a valid package name for this attribute. We will look this
+ // up.
+ StringPiece package = maybe_package.value().package;
+ if (package.empty()) {
+ // Empty package means the 'current' or 'local' package.
+ package = context_->GetCompilationPackage();
}
- // Call the super implementation.
- xml::PackageAwareVisitor::visit(el);
+ Reference attr_ref(
+ ResourceNameRef(package, ResourceType::kAttr, attr.name));
+ attr_ref.private_reference = maybe_package.value().private_namespace;
+
+ std::string err_str;
+ attr.compiled_attribute = ReferenceLinker::CompileXmlAttribute(
+ attr_ref, context_->GetNameMangler(), symbols_, callsite_,
+ &err_str);
+
+ // Convert the string value into a compiled Value if this is a valid
+ // attribute.
+ if (attr.compiled_attribute) {
+ if (attr.compiled_attribute.value().id) {
+ // Record all SDK levels from which the attributes were defined.
+ const size_t sdk_level = FindAttributeSdkLevel(
+ attr.compiled_attribute.value().id.value());
+ if (sdk_level > 1) {
+ sdk_levels_found_->insert(sdk_level);
+ }
+ }
+
+ const Attribute* attribute =
+ &attr.compiled_attribute.value().attribute;
+ attr.compiled_value =
+ ResourceUtils::TryParseItemForAttribute(attr.value, attribute);
+ if (!attr.compiled_value &&
+ !(attribute->type_mask & android::ResTable_map::TYPE_STRING)) {
+ // We won't be able to encode this as a string.
+ context_->GetDiagnostics()->Error(
+ DiagMessage(source) << "'" << attr.value << "' "
+ << "is incompatible with attribute "
+ << package << ":" << attr.name << " "
+ << *attribute);
+ error_ = true;
+ }
+
+ } else {
+ context_->GetDiagnostics()->Error(DiagMessage(source)
+ << "attribute '" << package << ":"
+ << attr.name << "' " << err_str);
+ error_ = true;
+ }
+ } else if (!attr.compiled_value) {
+ // We still encode references, but only if we haven't manually set this
+ // to
+ // another compiled value.
+ attr.compiled_value = ResourceUtils::TryParseReference(attr.value);
+ }
+
+ if (attr.compiled_value) {
+ // With a compiledValue, we must resolve the reference and assign it an
+ // ID.
+ attr.compiled_value->SetSource(source);
+ attr.compiled_value->Accept(&reference_visitor_);
+ }
}
- bool hasError() {
- return mError || mReferenceVisitor.hasError();
- }
+ // Call the super implementation.
+ xml::PackageAwareVisitor::Visit(el);
+ }
-private:
- IAaptContext* mContext;
- SymbolTable* mSymbols;
- Source mSource;
- std::set<int>* mSdkLevelsFound;
- CallSite* mCallSite;
- ReferenceVisitor mReferenceVisitor;
- bool mError = false;
+ bool HasError() { return error_ || reference_visitor_.HasError(); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XmlVisitor);
+
+ IAaptContext* context_;
+ SymbolTable* symbols_;
+ Source source_;
+ std::set<int>* sdk_levels_found_;
+ CallSite* callsite_;
+ ReferenceVisitor reference_visitor_;
+ bool error_ = false;
};
-} // namespace
+} // namespace
-bool XmlReferenceLinker::consume(IAaptContext* context, xml::XmlResource* resource) {
- mSdkLevelsFound.clear();
- CallSite callSite = { resource->file.name };
- XmlVisitor visitor(context, context->getExternalSymbols(), resource->file.source,
- &mSdkLevelsFound, &callSite);
- if (resource->root) {
- resource->root->accept(&visitor);
- return !visitor.hasError();
- }
- return false;
+bool XmlReferenceLinker::Consume(IAaptContext* context,
+ xml::XmlResource* resource) {
+ sdk_levels_found_.clear();
+ CallSite callsite = {resource->file.name};
+ XmlVisitor visitor(context, context->GetExternalSymbols(),
+ resource->file.source, &sdk_levels_found_, &callsite);
+ if (resource->root) {
+ resource->root->Accept(&visitor);
+ return !visitor.HasError();
+ }
+ return false;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 51eb62c..810f63c 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -15,242 +15,285 @@
*/
#include "link/Linkers.h"
+
#include "test/Test.h"
namespace aapt {
class XmlReferenceLinkerTest : public ::testing::Test {
-public:
- void SetUp() override {
- mContext = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setNameManglerPolicy(
- NameManglerPolicy{ "com.app.test", { "com.android.support" } })
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addPublicSymbol("android:attr/layout_width", ResourceId(0x01010000),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_ENUM |
- android::ResTable_map::TYPE_DIMENSION)
- .addItem("match_parent", 0xffffffff)
- .build())
- .addPublicSymbol("android:attr/background", ResourceId(0x01010001),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
- .addPublicSymbol("android:attr/attr", ResourceId(0x01010002),
- test::AttributeBuilder().build())
- .addPublicSymbol("android:attr/text", ResourceId(0x01010003),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_STRING)
- .build())
+ public:
+ void SetUp() override {
+ context_ =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetNameManglerPolicy(
+ NameManglerPolicy{"com.app.test", {"com.android.support"}})
+ .AddSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .AddPublicSymbol(
+ "android:attr/layout_width", ResourceId(0x01010000),
+ test::AttributeBuilder()
+ .SetTypeMask(android::ResTable_map::TYPE_ENUM |
+ android::ResTable_map::TYPE_DIMENSION)
+ .AddItem("match_parent", 0xffffffff)
+ .Build())
+ .AddPublicSymbol(
+ "android:attr/background", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .SetTypeMask(android::ResTable_map::TYPE_COLOR)
+ .Build())
+ .AddPublicSymbol("android:attr/attr",
+ ResourceId(0x01010002),
+ test::AttributeBuilder().Build())
+ .AddPublicSymbol(
+ "android:attr/text", ResourceId(0x01010003),
+ test::AttributeBuilder()
+ .SetTypeMask(android::ResTable_map::TYPE_STRING)
+ .Build())
- // Add one real symbol that was introduces in v21
- .addPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435),
- test::AttributeBuilder().build())
+ // Add one real symbol that was introduces in v21
+ .AddPublicSymbol("android:attr/colorAccent",
+ ResourceId(0x01010435),
+ test::AttributeBuilder().Build())
- // Private symbol.
- .addSymbol("android:color/hidden", ResourceId(0x01020001))
+ // Private symbol.
+ .AddSymbol("android:color/hidden", ResourceId(0x01020001))
- .addPublicSymbol("android:id/id", ResourceId(0x01030000))
- .addSymbol("com.app.test:id/id", ResourceId(0x7f030000))
- .addSymbol("com.app.test:color/green", ResourceId(0x7f020000))
- .addSymbol("com.app.test:color/red", ResourceId(0x7f020001))
- .addSymbol("com.app.test:attr/colorAccent", ResourceId(0x7f010000),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
- .addPublicSymbol("com.app.test:attr/com.android.support$colorAccent",
- ResourceId(0x7f010001), test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
- .addPublicSymbol("com.app.test:attr/attr", ResourceId(0x7f010002),
- test::AttributeBuilder().build())
- .build())
- .build();
- }
+ .AddPublicSymbol("android:id/id", ResourceId(0x01030000))
+ .AddSymbol("com.app.test:id/id", ResourceId(0x7f030000))
+ .AddSymbol("com.app.test:color/green",
+ ResourceId(0x7f020000))
+ .AddSymbol("com.app.test:color/red", ResourceId(0x7f020001))
+ .AddSymbol(
+ "com.app.test:attr/colorAccent", ResourceId(0x7f010000),
+ test::AttributeBuilder()
+ .SetTypeMask(android::ResTable_map::TYPE_COLOR)
+ .Build())
+ .AddPublicSymbol(
+ "com.app.test:attr/com.android.support$colorAccent",
+ ResourceId(0x7f010001),
+ test::AttributeBuilder()
+ .SetTypeMask(android::ResTable_map::TYPE_COLOR)
+ .Build())
+ .AddPublicSymbol("com.app.test:attr/attr",
+ ResourceId(0x7f010002),
+ test::AttributeBuilder().Build())
+ .Build())
+ .Build();
+ }
-protected:
- std::unique_ptr<IAaptContext> mContext;
+ protected:
+ std::unique_ptr<IAaptContext> context_;
};
TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDomForPackageName(context_.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="@color/green"
android:text="hello"
class="hello" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* view_el = xml::FindRootElement(doc.get());
+ ASSERT_NE(view_el, nullptr);
- xml::Attribute* xmlAttr = viewEl->findAttribute(xml::kSchemaAndroid, "layout_width");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010000));
- ASSERT_NE(xmlAttr->compiledValue, nullptr);
- ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
+ xml::Attribute* xml_attr =
+ view_el->FindAttribute(xml::kSchemaAndroid, "layout_width");
+ ASSERT_NE(xml_attr, nullptr);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(),
+ ResourceId(0x01010000));
+ ASSERT_NE(xml_attr->compiled_value, nullptr);
+ ASSERT_NE(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()),
+ nullptr);
- xmlAttr = viewEl->findAttribute(xml::kSchemaAndroid, "background");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010001));
- ASSERT_NE(xmlAttr->compiledValue, nullptr);
- Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->name);
- EXPECT_EQ(ref->name.value(), test::parseNameOrDie("color/green")); // Make sure the name
- // didn't change.
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f020000));
+ xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "background");
+ ASSERT_NE(xml_attr, nullptr);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(),
+ ResourceId(0x01010001));
+ ASSERT_NE(xml_attr->compiled_value, nullptr);
+ Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->name);
+ EXPECT_EQ(ref->name.value(),
+ test::ParseNameOrDie("color/green")); // Make sure the name
+ // didn't change.
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f020000));
- xmlAttr = viewEl->findAttribute(xml::kSchemaAndroid, "text");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- ASSERT_FALSE(xmlAttr->compiledValue); // Strings don't get compiled for memory sake.
+ xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
+ ASSERT_NE(xml_attr, nullptr);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
+ ASSERT_FALSE(
+ xml_attr->compiled_value); // Strings don't get compiled for memory sake.
- xmlAttr = viewEl->findAttribute("", "class");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_FALSE(xmlAttr->compiledAttribute);
- ASSERT_EQ(xmlAttr->compiledValue, nullptr);
+ xml_attr = view_el->FindAttribute("", "class");
+ ASSERT_NE(xml_attr, nullptr);
+ AAPT_ASSERT_FALSE(xml_attr->compiled_attribute);
+ ASSERT_EQ(xml_attr->compiled_value, nullptr);
}
TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDomForPackageName(context_.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="@android:color/hidden" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_FALSE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
}
-TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+TEST_F(XmlReferenceLinkerTest,
+ PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDomForPackageName(context_.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="@*android:color/hidden" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
}
TEST_F(XmlReferenceLinkerTest, SdkLevelsAreRecorded) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDomForPackageName(context_.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="#ffffff" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- EXPECT_TRUE(linker.getSdkLevels().count(21) == 1);
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+ EXPECT_TRUE(linker.sdk_levels().count(21) == 1);
}
TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDomForPackageName(context_.get(), R"EOF(
<View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
support:colorAccent="#ff0000" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* view_el = xml::FindRootElement(doc.get());
+ ASSERT_NE(view_el, nullptr);
- xml::Attribute* xmlAttr = viewEl->findAttribute(
- xml::buildPackageNamespace("com.android.support"), "colorAccent");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010001));
- ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
+ xml::Attribute* xml_attr = view_el->FindAttribute(
+ xml::BuildPackageNamespace("com.android.support"), "colorAccent");
+ ASSERT_NE(xml_attr, nullptr);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(),
+ ResourceId(0x7f010001));
+ ASSERT_NE(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()),
+ nullptr);
}
TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDomForPackageName(context_.get(), R"EOF(
<View xmlns:app="http://schemas.android.com/apk/res-auto"
app:colorAccent="@app:color/red" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* view_el = xml::FindRootElement(doc.get());
+ ASSERT_NE(view_el, nullptr);
- xml::Attribute* xmlAttr = viewEl->findAttribute(xml::kSchemaAuto, "colorAccent");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010000));
- Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->name);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
+ xml::Attribute* xml_attr =
+ view_el->FindAttribute(xml::kSchemaAuto, "colorAccent");
+ ASSERT_NE(xml_attr, nullptr);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(),
+ ResourceId(0x7f010000));
+ Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->name);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDomForPackageName(context_.get(), R"EOF(
<View xmlns:app="http://schemas.android.com/apk/res/android"
app:attr="@app:id/id">
<View xmlns:app="http://schemas.android.com/apk/res/com.app.test"
app:attr="@app:id/id"/>
</View>)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* view_el = xml::FindRootElement(doc.get());
+ ASSERT_NE(view_el, nullptr);
- // All attributes and references in this element should be referring to "android" (0x01).
- xml::Attribute* xmlAttr = viewEl->findAttribute(xml::kSchemaAndroid, "attr");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010002));
- Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x01030000));
+ // All attributes and references in this element should be referring to
+ // "android" (0x01).
+ xml::Attribute* xml_attr =
+ view_el->FindAttribute(xml::kSchemaAndroid, "attr");
+ ASSERT_NE(xml_attr, nullptr);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(),
+ ResourceId(0x01010002));
+ Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x01030000));
- ASSERT_FALSE(viewEl->getChildElements().empty());
- viewEl = viewEl->getChildElements().front();
- ASSERT_NE(viewEl, nullptr);
+ ASSERT_FALSE(view_el->GetChildElements().empty());
+ view_el = view_el->GetChildElements().front();
+ ASSERT_NE(view_el, nullptr);
- // All attributes and references in this element should be referring to "com.app.test" (0x7f).
- xmlAttr = viewEl->findAttribute(xml::buildPackageNamespace("com.app.test"), "attr");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002));
- ref = valueCast<Reference>(xmlAttr->compiledValue.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
+ // All attributes and references in this element should be referring to
+ // "com.app.test" (0x7f).
+ xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"),
+ "attr");
+ ASSERT_NE(xml_attr, nullptr);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(),
+ ResourceId(0x7f010002));
+ ref = ValueCast<Reference>(xml_attr->compiled_value.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDomForPackageName(context_.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
android:attr="@id/id"/>)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* view_el = xml::FindRootElement(doc.get());
+ ASSERT_NE(view_el, nullptr);
- // All attributes and references in this element should be referring to "com.app.test" (0x7f).
- xml::Attribute* xmlAttr = viewEl->findAttribute(xml::buildPackageNamespace("com.app.test"),
- "attr");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002));
- Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
+ // All attributes and references in this element should be referring to
+ // "com.app.test" (0x7f).
+ xml::Attribute* xml_attr = view_el->FindAttribute(
+ xml::BuildPackageNamespace("com.app.test"), "attr");
+ ASSERT_NE(xml_attr, nullptr);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
+ AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(),
+ ResourceId(0x7f010002));
+ Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index a7e752a..4526a79 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -17,37 +17,37 @@
#ifndef AAPT_PROCESS_IRESOURCETABLECONSUMER_H
#define AAPT_PROCESS_IRESOURCETABLECONSUMER_H
+#include <iostream>
+#include <list>
+#include <sstream>
+
#include "Diagnostics.h"
#include "NameMangler.h"
#include "Resource.h"
#include "ResourceValues.h"
#include "Source.h"
-#include <iostream>
-#include <list>
-#include <sstream>
-
namespace aapt {
class ResourceTable;
class SymbolTable;
struct IAaptContext {
- virtual ~IAaptContext() = default;
+ virtual ~IAaptContext() = default;
- virtual SymbolTable* getExternalSymbols() = 0;
- virtual IDiagnostics* getDiagnostics() = 0;
- virtual const std::string& getCompilationPackage() = 0;
- virtual uint8_t getPackageId() = 0;
- virtual NameMangler* getNameMangler() = 0;
- virtual bool verbose() = 0;
- virtual int getMinSdkVersion() = 0;
+ virtual SymbolTable* GetExternalSymbols() = 0;
+ virtual IDiagnostics* GetDiagnostics() = 0;
+ virtual const std::string& GetCompilationPackage() = 0;
+ virtual uint8_t GetPackageId() = 0;
+ virtual NameMangler* GetNameMangler() = 0;
+ virtual bool IsVerbose() = 0;
+ virtual int GetMinSdkVersion() = 0;
};
struct IResourceTableConsumer {
- virtual ~IResourceTableConsumer() = default;
+ virtual ~IResourceTableConsumer() = default;
- virtual bool consume(IAaptContext* context, ResourceTable* table) = 0;
+ virtual bool Consume(IAaptContext* context, ResourceTable* table) = 0;
};
namespace xml {
@@ -55,11 +55,11 @@
}
struct IXmlResourceConsumer {
- virtual ~IXmlResourceConsumer() = default;
+ virtual ~IXmlResourceConsumer() = default;
- virtual bool consume(IAaptContext* context, xml::XmlResource* resource) = 0;
+ virtual bool Consume(IAaptContext* context, xml::XmlResource* resource) = 0;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_PROCESS_IRESOURCETABLECONSUMER_H */
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 0c92718..767384d 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -14,267 +14,288 @@
* limitations under the License.
*/
+#include "process/SymbolTable.h"
+
+#include "androidfw/AssetManager.h"
+#include "androidfw/ResourceTypes.h"
+
#include "ConfigDescription.h"
#include "Resource.h"
#include "ResourceUtils.h"
#include "ValueVisitor.h"
-#include "process/SymbolTable.h"
#include "util/Util.h"
-#include <androidfw/AssetManager.h>
-#include <androidfw/ResourceTypes.h>
-
namespace aapt {
-void SymbolTable::appendSource(std::unique_ptr<ISymbolSource> source) {
- mSources.push_back(std::move(source));
+void SymbolTable::AppendSource(std::unique_ptr<ISymbolSource> source) {
+ sources_.push_back(std::move(source));
- // We do not clear the cache, because sources earlier in the list take precedent.
+ // We do not clear the cache, because sources earlier in the list take
+ // precedent.
}
-void SymbolTable::prependSource(std::unique_ptr<ISymbolSource> source) {
- mSources.insert(mSources.begin(), std::move(source));
+void SymbolTable::PrependSource(std::unique_ptr<ISymbolSource> source) {
+ sources_.insert(sources_.begin(), std::move(source));
- // We must clear the cache in case we did a lookup before adding this resource.
- mCache.clear();
+ // We must clear the cache in case we did a lookup before adding this
+ // resource.
+ cache_.clear();
}
-const SymbolTable::Symbol* SymbolTable::findByName(const ResourceName& name) {
- if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
- return s.get();
- }
+const SymbolTable::Symbol* SymbolTable::FindByName(const ResourceName& name) {
+ if (const std::shared_ptr<Symbol>& s = cache_.get(name)) {
+ return s.get();
+ }
- // We did not find it in the cache, so look through the sources.
- for (auto& symbolSource : mSources) {
- std::unique_ptr<Symbol> symbol = symbolSource->findByName(name);
- if (symbol) {
- // Take ownership of the symbol into a shared_ptr. We do this because LruCache
- // doesn't support unique_ptr.
- std::shared_ptr<Symbol> sharedSymbol = std::shared_ptr<Symbol>(symbol.release());
- mCache.put(name, sharedSymbol);
+ // We did not find it in the cache, so look through the sources.
+ for (auto& symbolSource : sources_) {
+ std::unique_ptr<Symbol> symbol = symbolSource->FindByName(name);
+ if (symbol) {
+ // Take ownership of the symbol into a shared_ptr. We do this because
+ // LruCache
+ // doesn't support unique_ptr.
+ std::shared_ptr<Symbol> shared_symbol =
+ std::shared_ptr<Symbol>(symbol.release());
+ cache_.put(name, shared_symbol);
- if (sharedSymbol->id) {
- // The symbol has an ID, so we can also cache this!
- mIdCache.put(sharedSymbol->id.value(), sharedSymbol);
- }
- return sharedSymbol.get();
- }
+ if (shared_symbol->id) {
+ // The symbol has an ID, so we can also cache this!
+ id_cache_.put(shared_symbol->id.value(), shared_symbol);
+ }
+ return shared_symbol.get();
}
- return nullptr;
+ }
+ return nullptr;
}
-const SymbolTable::Symbol* SymbolTable::findById(const ResourceId& id) {
- if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
- return s.get();
- }
+const SymbolTable::Symbol* SymbolTable::FindById(const ResourceId& id) {
+ if (const std::shared_ptr<Symbol>& s = id_cache_.get(id)) {
+ return s.get();
+ }
- // We did not find it in the cache, so look through the sources.
- for (auto& symbolSource : mSources) {
- std::unique_ptr<Symbol> symbol = symbolSource->findById(id);
- if (symbol) {
- // Take ownership of the symbol into a shared_ptr. We do this because LruCache
- // doesn't support unique_ptr.
- std::shared_ptr<Symbol> sharedSymbol = std::shared_ptr<Symbol>(symbol.release());
- mIdCache.put(id, sharedSymbol);
- return sharedSymbol.get();
- }
+ // We did not find it in the cache, so look through the sources.
+ for (auto& symbolSource : sources_) {
+ std::unique_ptr<Symbol> symbol = symbolSource->FindById(id);
+ if (symbol) {
+ // Take ownership of the symbol into a shared_ptr. We do this because
+ // LruCache
+ // doesn't support unique_ptr.
+ std::shared_ptr<Symbol> shared_symbol =
+ std::shared_ptr<Symbol>(symbol.release());
+ id_cache_.put(id, shared_symbol);
+ return shared_symbol.get();
}
- return nullptr;
+ }
+ return nullptr;
}
-const SymbolTable::Symbol* SymbolTable::findByReference(const Reference& ref) {
- // First try the ID. This is because when we lookup by ID, we only fill in the ID cache.
- // Looking up by name fills in the name and ID cache. So a cache miss will cause a failed
- // ID lookup, then a successful name lookup. Subsequent look ups will hit immediately
- // because the ID is cached too.
- //
- // If we looked up by name first, a cache miss would mean we failed to lookup by name, then
- // succeeded to lookup by ID. Subsequent lookups will miss then hit.
- const SymbolTable::Symbol* symbol = nullptr;
- if (ref.id) {
- symbol = findById(ref.id.value());
- }
+const SymbolTable::Symbol* SymbolTable::FindByReference(const Reference& ref) {
+ // First try the ID. This is because when we lookup by ID, we only fill in the
+ // ID cache.
+ // Looking up by name fills in the name and ID cache. So a cache miss will
+ // cause a failed
+ // ID lookup, then a successful name lookup. Subsequent look ups will hit
+ // immediately
+ // because the ID is cached too.
+ //
+ // If we looked up by name first, a cache miss would mean we failed to lookup
+ // by name, then
+ // succeeded to lookup by ID. Subsequent lookups will miss then hit.
+ const SymbolTable::Symbol* symbol = nullptr;
+ if (ref.id) {
+ symbol = FindById(ref.id.value());
+ }
- if (ref.name && !symbol) {
- symbol = findByName(ref.name.value());
- }
- return symbol;
+ if (ref.name && !symbol) {
+ symbol = FindByName(ref.name.value());
+ }
+ return symbol;
}
-std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::findByName(
- const ResourceName& name) {
- Maybe<ResourceTable::SearchResult> result = mTable->findResource(name);
- if (!result) {
- if (name.type == ResourceType::kAttr) {
- // Recurse and try looking up a private attribute.
- return findByName(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
- }
+std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::FindByName(
+ const ResourceName& name) {
+ Maybe<ResourceTable::SearchResult> result = table_->FindResource(name);
+ if (!result) {
+ if (name.type == ResourceType::kAttr) {
+ // Recurse and try looking up a private attribute.
+ return FindByName(
+ ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
+ }
+ return {};
+ }
+
+ ResourceTable::SearchResult sr = result.value();
+
+ std::unique_ptr<SymbolTable::Symbol> symbol =
+ util::make_unique<SymbolTable::Symbol>();
+ symbol->is_public = (sr.entry->symbol_status.state == SymbolState::kPublic);
+
+ if (sr.package->id && sr.type->id && sr.entry->id) {
+ symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(),
+ sr.entry->id.value());
+ }
+
+ if (name.type == ResourceType::kAttr ||
+ name.type == ResourceType::kAttrPrivate) {
+ const ConfigDescription kDefaultConfig;
+ ResourceConfigValue* config_value = sr.entry->FindValue(kDefaultConfig);
+ if (config_value) {
+ // This resource has an Attribute.
+ if (Attribute* attr = ValueCast<Attribute>(config_value->value.get())) {
+ symbol->attribute = std::make_shared<Attribute>(*attr);
+ } else {
return {};
+ }
}
-
- ResourceTable::SearchResult sr = result.value();
-
- std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>();
- symbol->isPublic = (sr.entry->symbolStatus.state == SymbolState::kPublic);
-
- if (sr.package->id && sr.type->id && sr.entry->id) {
- symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
- }
-
- if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
- const ConfigDescription kDefaultConfig;
- ResourceConfigValue* configValue = sr.entry->findValue(kDefaultConfig);
- if (configValue) {
- // This resource has an Attribute.
- if (Attribute* attr = valueCast<Attribute>(configValue->value.get())) {
- symbol->attribute = std::make_shared<Attribute>(*attr);
- } else {
- return {};
- }
- }
- }
- return symbol;
+ }
+ return symbol;
}
-bool AssetManagerSymbolSource::addAssetPath(const StringPiece& path) {
- int32_t cookie = 0;
- return mAssets.addAssetPath(android::String8(path.data(), path.size()), &cookie);
+bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) {
+ int32_t cookie = 0;
+ return assets_.addAssetPath(android::String8(path.data(), path.size()),
+ &cookie);
}
-static std::unique_ptr<SymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table,
- ResourceId id) {
- // Try as a bag.
- const android::ResTable::bag_entry* entry;
- ssize_t count = table.lockBag(id.id, &entry);
- if (count < 0) {
- table.unlockBag(entry);
- return nullptr;
+static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
+ const android::ResTable& table, ResourceId id) {
+ // Try as a bag.
+ const android::ResTable::bag_entry* entry;
+ ssize_t count = table.lockBag(id.id, &entry);
+ if (count < 0) {
+ table.unlockBag(entry);
+ return nullptr;
+ }
+
+ // We found a resource.
+ std::unique_ptr<SymbolTable::Symbol> s =
+ util::make_unique<SymbolTable::Symbol>();
+ s->id = id;
+
+ // Check to see if it is an attribute.
+ for (size_t i = 0; i < (size_t)count; i++) {
+ if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
+ s->attribute = std::make_shared<Attribute>(false);
+ s->attribute->type_mask = entry[i].map.value.data;
+ break;
}
+ }
- // We found a resource.
- std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>();
- s->id = id;
-
- // Check to see if it is an attribute.
- for (size_t i = 0; i < (size_t) count; i++) {
- if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
- s->attribute = std::make_shared<Attribute>(false);
- s->attribute->typeMask = entry[i].map.value.data;
+ if (s->attribute) {
+ for (size_t i = 0; i < (size_t)count; i++) {
+ const android::ResTable_map& map_entry = entry[i].map;
+ if (Res_INTERNALID(map_entry.name.ident)) {
+ switch (map_entry.name.ident) {
+ case android::ResTable_map::ATTR_MIN:
+ s->attribute->min_int = static_cast<int32_t>(map_entry.value.data);
+ break;
+ case android::ResTable_map::ATTR_MAX:
+ s->attribute->max_int = static_cast<int32_t>(map_entry.value.data);
break;
}
+ continue;
+ }
+
+ android::ResTable::resource_name entry_name;
+ if (!table.getResourceName(map_entry.name.ident, false, &entry_name)) {
+ table.unlockBag(entry);
+ return nullptr;
+ }
+
+ Maybe<ResourceName> parsed_name =
+ ResourceUtils::ToResourceName(entry_name);
+ if (!parsed_name) {
+ return nullptr;
+ }
+
+ Attribute::Symbol symbol;
+ symbol.symbol.name = parsed_name.value();
+ symbol.symbol.id = ResourceId(map_entry.name.ident);
+ symbol.value = map_entry.value.data;
+ s->attribute->symbols.push_back(std::move(symbol));
}
+ }
+ table.unlockBag(entry);
+ return s;
+}
- if (s->attribute) {
- for (size_t i = 0; i < (size_t) count; i++) {
- const android::ResTable_map& mapEntry = entry[i].map;
- if (Res_INTERNALID(mapEntry.name.ident)) {
- switch (mapEntry.name.ident) {
- case android::ResTable_map::ATTR_MIN:
- s->attribute->minInt = static_cast<int32_t>(mapEntry.value.data);
- break;
- case android::ResTable_map::ATTR_MAX:
- s->attribute->maxInt = static_cast<int32_t>(mapEntry.value.data);
- break;
- }
- continue;
- }
+std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
+ const ResourceName& name) {
+ const android::ResTable& table = assets_.getResources(false);
- android::ResTable::resource_name entryName;
- if (!table.getResourceName(mapEntry.name.ident, false, &entryName)) {
- table.unlockBag(entry);
- return nullptr;
- }
+ const std::u16string package16 = util::Utf8ToUtf16(name.package);
+ const std::u16string type16 = util::Utf8ToUtf16(ToString(name.type));
+ const std::u16string entry16 = util::Utf8ToUtf16(name.entry);
- Maybe<ResourceName> parsedName = ResourceUtils::toResourceName(entryName);
- if (!parsedName) {
- return nullptr;
- }
+ uint32_t type_spec_flags = 0;
+ ResourceId res_id = table.identifierForName(
+ entry16.data(), entry16.size(), type16.data(), type16.size(),
+ package16.data(), package16.size(), &type_spec_flags);
+ if (!res_id.is_valid()) {
+ return {};
+ }
- Attribute::Symbol symbol;
- symbol.symbol.name = parsedName.value();
- symbol.symbol.id = ResourceId(mapEntry.name.ident);
- symbol.value = mapEntry.value.data;
- s->attribute->symbols.push_back(std::move(symbol));
- }
- }
- table.unlockBag(entry);
+ std::unique_ptr<SymbolTable::Symbol> s;
+ if (name.type == ResourceType::kAttr) {
+ s = LookupAttributeInTable(table, res_id);
+ } else {
+ s = util::make_unique<SymbolTable::Symbol>();
+ s->id = res_id;
+ }
+
+ if (s) {
+ s->is_public =
+ (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
return s;
+ }
+ return {};
}
-std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByName(
- const ResourceName& name) {
- const android::ResTable& table = mAssets.getResources(false);
-
- const std::u16string package16 = util::utf8ToUtf16(name.package);
- const std::u16string type16 = util::utf8ToUtf16(toString(name.type));
- const std::u16string entry16 = util::utf8ToUtf16(name.entry);
-
- uint32_t typeSpecFlags = 0;
- ResourceId resId = table.identifierForName(entry16.data(), entry16.size(),
- type16.data(), type16.size(),
- package16.data(), package16.size(),
- &typeSpecFlags);
- if (!resId.isValid()) {
- return {};
- }
-
- std::unique_ptr<SymbolTable::Symbol> s;
- if (name.type == ResourceType::kAttr) {
- s = lookupAttributeInTable(table, resId);
- } else {
- s = util::make_unique<SymbolTable::Symbol>();
- s->id = resId;
- }
-
- if (s) {
- s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- return s;
- }
+static Maybe<ResourceName> GetResourceName(const android::ResTable& table,
+ ResourceId id) {
+ android::ResTable::resource_name res_name = {};
+ if (!table.getResourceName(id.id, true, &res_name)) {
return {};
+ }
+ return ResourceUtils::ToResourceName(res_name);
}
-static Maybe<ResourceName> getResourceName(const android::ResTable& table, ResourceId id) {
- android::ResTable::resource_name resName = {};
- if (!table.getResourceName(id.id, true, &resName)) {
- return {};
- }
- return ResourceUtils::toResourceName(resName);
-}
-
-std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findById(ResourceId id) {
- const android::ResTable& table = mAssets.getResources(false);
- Maybe<ResourceName> maybeName = getResourceName(table, id);
- if (!maybeName) {
- return {};
- }
-
- uint32_t typeSpecFlags = 0;
- table.getResourceFlags(id.id, &typeSpecFlags);
-
- std::unique_ptr<SymbolTable::Symbol> s;
- if (maybeName.value().type == ResourceType::kAttr) {
- s = lookupAttributeInTable(table, id);
- } else {
- s = util::make_unique<SymbolTable::Symbol>();
- s->id = id;
- }
-
- if (s) {
- s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- return s;
- }
+std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
+ ResourceId id) {
+ const android::ResTable& table = assets_.getResources(false);
+ Maybe<ResourceName> maybe_name = GetResourceName(table, id);
+ if (!maybe_name) {
return {};
+ }
+
+ uint32_t type_spec_flags = 0;
+ table.getResourceFlags(id.id, &type_spec_flags);
+
+ std::unique_ptr<SymbolTable::Symbol> s;
+ if (maybe_name.value().type == ResourceType::kAttr) {
+ s = LookupAttributeInTable(table, id);
+ } else {
+ s = util::make_unique<SymbolTable::Symbol>();
+ s->id = id;
+ }
+
+ if (s) {
+ s->is_public =
+ (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
+ return s;
+ }
+ return {};
}
-std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByReference(
- const Reference& ref) {
- // AssetManager always prefers IDs.
- if (ref.id) {
- return findById(ref.id.value());
- } else if (ref.name) {
- return findByName(ref.name.value());
- }
- return {};
+std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByReference(
+ const Reference& ref) {
+ // AssetManager always prefers IDs.
+ if (ref.id) {
+ return FindById(ref.id.value());
+ } else if (ref.name) {
+ return FindByName(ref.name.value());
+ }
+ return {};
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index bd31416..25f7565 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -17,116 +17,115 @@
#ifndef AAPT_PROCESS_SYMBOLTABLE_H
#define AAPT_PROCESS_SYMBOLTABLE_H
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "android-base/macros.h"
+#include "androidfw/AssetManager.h"
+#include "utils/JenkinsHash.h"
+#include "utils/LruCache.h"
+
#include "Resource.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "util/Util.h"
-#include <utils/JenkinsHash.h>
-#include <utils/LruCache.h>
-
-#include <android-base/macros.h>
-#include <androidfw/AssetManager.h>
-#include <algorithm>
-#include <memory>
-#include <vector>
-
namespace aapt {
inline android::hash_t hash_type(const ResourceName& name) {
- std::hash<std::string> strHash;
- android::hash_t hash = 0;
- hash = android::JenkinsHashMix(hash, (uint32_t) strHash(name.package));
- hash = android::JenkinsHashMix(hash, (uint32_t) name.type);
- hash = android::JenkinsHashMix(hash, (uint32_t) strHash(name.entry));
- return hash;
+ std::hash<std::string> str_hash;
+ android::hash_t hash = 0;
+ hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.package));
+ hash = android::JenkinsHashMix(hash, (uint32_t)name.type);
+ hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.entry));
+ return hash;
}
inline android::hash_t hash_type(const ResourceId& id) {
- return android::hash_type(id.id);
+ return android::hash_type(id.id);
}
class ISymbolSource;
class SymbolTable {
-public:
- struct Symbol {
- Symbol() : Symbol(Maybe<ResourceId>{}) {
- }
+ public:
+ struct Symbol {
+ Symbol() : Symbol(Maybe<ResourceId>{}) {}
- explicit 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) :
- Symbol(i, attr, false) {
- }
+ Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr)
+ : Symbol(i, attr, false) {}
- Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr, bool pub) :
- id(i), attribute(attr), isPublic(pub) {
- }
+ Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr,
+ bool pub)
+ : id(i), attribute(attr), is_public(pub) {}
- Symbol(const Symbol&) = default;
- Symbol(Symbol&&) = default;
- Symbol& operator=(const Symbol&) = default;
- Symbol& operator=(Symbol&&) = default;
+ Symbol(const Symbol&) = default;
+ Symbol(Symbol&&) = default;
+ Symbol& operator=(const Symbol&) = default;
+ Symbol& operator=(Symbol&&) = default;
- Maybe<ResourceId> id;
- std::shared_ptr<Attribute> attribute;
- bool isPublic = false;
- };
+ Maybe<ResourceId> id;
+ std::shared_ptr<Attribute> attribute;
+ bool is_public = false;
+ };
- SymbolTable() : mCache(200), mIdCache(200) {
- }
+ SymbolTable() : cache_(200), id_cache_(200) {}
- void appendSource(std::unique_ptr<ISymbolSource> source);
- void prependSource(std::unique_ptr<ISymbolSource> source);
+ void AppendSource(std::unique_ptr<ISymbolSource> source);
+ void PrependSource(std::unique_ptr<ISymbolSource> source);
- /**
- * Never hold on to the result between calls to findByName or findById. The results
- * are typically stored in a cache which may evict entries.
- */
- const Symbol* findByName(const ResourceName& name);
- const Symbol* findById(const ResourceId& id);
+ /**
+ * Never hold on to the result between calls to FindByName or FindById. The
+ * results stored in a cache which may evict entries.
+ */
+ const Symbol* FindByName(const ResourceName& name);
+ const Symbol* FindById(const ResourceId& id);
- /**
- * Let's the ISymbolSource decide whether looking up by name or ID is faster, if both
- * are available.
- */
- const Symbol* findByReference(const Reference& ref);
+ /**
+ * Let's the ISymbolSource decide whether looking up by name or ID is faster,
+ * if both are available.
+ */
+ const Symbol* FindByReference(const Reference& ref);
-private:
- std::vector<std::unique_ptr<ISymbolSource>> mSources;
+ private:
+ std::vector<std::unique_ptr<ISymbolSource>> sources_;
- // We use shared_ptr because unique_ptr is not supported and
- // we need automatic deletion.
- android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache;
- android::LruCache<ResourceId, std::shared_ptr<Symbol>> mIdCache;
+ // We use shared_ptr because unique_ptr is not supported and
+ // we need automatic deletion.
+ android::LruCache<ResourceName, std::shared_ptr<Symbol>> cache_;
+ android::LruCache<ResourceId, std::shared_ptr<Symbol>> id_cache_;
- DISALLOW_COPY_AND_ASSIGN(SymbolTable);
+ DISALLOW_COPY_AND_ASSIGN(SymbolTable);
};
/**
- * An interface that a symbol source implements in order to surface symbol information
+ * An interface that a symbol source implements in order to surface symbol
+ * information
* to the symbol table.
*/
class ISymbolSource {
-public:
- virtual ~ISymbolSource() = default;
+ public:
+ virtual ~ISymbolSource() = default;
- virtual std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) = 0;
- virtual std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) = 0;
+ virtual std::unique_ptr<SymbolTable::Symbol> FindByName(
+ const ResourceName& name) = 0;
+ virtual std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) = 0;
- /**
- * Default implementation tries the name if it exists, else the ID.
- */
- virtual std::unique_ptr<SymbolTable::Symbol> findByReference(const Reference& ref) {
- if (ref.name) {
- return findByName(ref.name.value());
- } else if (ref.id) {
- return findById(ref.id.value());
- }
- return {};
+ /**
+ * Default implementation tries the name if it exists, else the ID.
+ */
+ virtual std::unique_ptr<SymbolTable::Symbol> FindByReference(
+ const Reference& ref) {
+ if (ref.name) {
+ return FindByName(ref.name.value());
+ } else if (ref.id) {
+ return FindById(ref.id.value());
}
+ return {};
+ }
};
/**
@@ -135,38 +134,40 @@
* Lookups by ID are ignored.
*/
class ResourceTableSymbolSource : public ISymbolSource {
-public:
- explicit ResourceTableSymbolSource(ResourceTable* table) : mTable(table) {
- }
+ public:
+ explicit ResourceTableSymbolSource(ResourceTable* table) : table_(table) {}
- std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
+ std::unique_ptr<SymbolTable::Symbol> FindByName(
+ const ResourceName& name) override;
- std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
- return {};
- }
+ std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override {
+ return {};
+ }
-private:
- ResourceTable* mTable;
+ private:
+ ResourceTable* table_;
- DISALLOW_COPY_AND_ASSIGN(ResourceTableSymbolSource);
+ DISALLOW_COPY_AND_ASSIGN(ResourceTableSymbolSource);
};
class AssetManagerSymbolSource : public ISymbolSource {
-public:
- AssetManagerSymbolSource() = default;
+ public:
+ AssetManagerSymbolSource() = default;
- bool addAssetPath(const StringPiece& path);
+ bool AddAssetPath(const StringPiece& path);
- std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
- std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override;
- std::unique_ptr<SymbolTable::Symbol> findByReference(const Reference& ref) override;
+ std::unique_ptr<SymbolTable::Symbol> FindByName(
+ const ResourceName& name) override;
+ std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override;
+ std::unique_ptr<SymbolTable::Symbol> FindByReference(
+ const Reference& ref) override;
-private:
- android::AssetManager mAssets;
+ private:
+ android::AssetManager assets_;
- DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource);
+ DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_PROCESS_SYMBOLTABLE_H */
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index 1626352..9ea0786 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -15,39 +15,44 @@
*/
#include "process/SymbolTable.h"
+
#include "test/Test.h"
namespace aapt {
TEST(ResourceTableSymbolSourceTest, FindSymbols) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:id/foo", ResourceId(0x01020000))
- .addSimple("android:id/bar")
- .addValue("android:attr/foo", ResourceId(0x01010000),
- test::AttributeBuilder().build())
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddSimple("android:id/foo", ResourceId(0x01020000))
+ .AddSimple("android:id/bar")
+ .AddValue("android:attr/foo", ResourceId(0x01010000),
+ test::AttributeBuilder().Build())
+ .Build();
- ResourceTableSymbolSource symbolSource(table.get());
- EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie("android:id/foo")));
- EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie("android:id/bar")));
+ ResourceTableSymbolSource symbol_source(table.get());
+ EXPECT_NE(nullptr,
+ symbol_source.FindByName(test::ParseNameOrDie("android:id/foo")));
+ EXPECT_NE(nullptr,
+ symbol_source.FindByName(test::ParseNameOrDie("android:id/bar")));
- std::unique_ptr<SymbolTable::Symbol> s = symbolSource.findByName(
- test::parseNameOrDie("android:attr/foo"));
- ASSERT_NE(nullptr, s);
- EXPECT_NE(nullptr, s->attribute);
+ std::unique_ptr<SymbolTable::Symbol> s =
+ symbol_source.FindByName(test::ParseNameOrDie("android:attr/foo"));
+ ASSERT_NE(nullptr, s);
+ EXPECT_NE(nullptr, s->attribute);
}
TEST(ResourceTableSymbolSourceTest, FindPrivateAttrSymbol) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addValue("android:^attr-private/foo", ResourceId(0x01010000),
- test::AttributeBuilder().build())
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddValue("android:^attr-private/foo", ResourceId(0x01010000),
+ test::AttributeBuilder().Build())
+ .Build();
- ResourceTableSymbolSource symbolSource(table.get());
- std::unique_ptr<SymbolTable::Symbol> s = symbolSource.findByName(
- test::parseNameOrDie("android:attr/foo"));
- ASSERT_NE(nullptr, s);
- EXPECT_NE(nullptr, s->attribute);
+ ResourceTableSymbolSource symbol_source(table.get());
+ std::unique_ptr<SymbolTable::Symbol> s =
+ symbol_source.FindByName(test::ParseNameOrDie("android:attr/foo"));
+ ASSERT_NE(nullptr, s);
+ EXPECT_NE(nullptr, s->attribute);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/proto/ProtoHelpers.cpp b/tools/aapt2/proto/ProtoHelpers.cpp
index 2aa8aa5..38bf4e3 100644
--- a/tools/aapt2/proto/ProtoHelpers.cpp
+++ b/tools/aapt2/proto/ProtoHelpers.cpp
@@ -18,120 +18,151 @@
namespace aapt {
-void serializeStringPoolToPb(const StringPool& pool, pb::StringPool* outPbPool) {
- BigBuffer buffer(1024);
- StringPool::flattenUtf8(&buffer, pool);
+void SerializeStringPoolToPb(const StringPool& pool,
+ pb::StringPool* out_pb_pool) {
+ BigBuffer buffer(1024);
+ StringPool::FlattenUtf8(&buffer, pool);
- std::string* data = outPbPool->mutable_data();
- data->reserve(buffer.size());
+ std::string* data = out_pb_pool->mutable_data();
+ data->reserve(buffer.size());
- size_t offset = 0;
- for (const BigBuffer::Block& block : buffer) {
- data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size);
- offset += block.size;
- }
+ size_t offset = 0;
+ for (const BigBuffer::Block& block : buffer) {
+ data->insert(data->begin() + offset, block.buffer.get(),
+ block.buffer.get() + block.size);
+ offset += block.size;
+ }
}
-void serializeSourceToPb(const Source& source, StringPool* srcPool, pb::Source* outPbSource) {
- StringPool::Ref ref = srcPool->makeRef(source.path);
- outPbSource->set_path_idx(static_cast<uint32_t>(ref.getIndex()));
- if (source.line) {
- outPbSource->set_line_no(static_cast<uint32_t>(source.line.value()));
- }
+void SerializeSourceToPb(const Source& source, StringPool* src_pool,
+ pb::Source* out_pb_source) {
+ StringPool::Ref ref = src_pool->MakeRef(source.path);
+ out_pb_source->set_path_idx(static_cast<uint32_t>(ref.index()));
+ if (source.line) {
+ out_pb_source->set_line_no(static_cast<uint32_t>(source.line.value()));
+ }
}
-void deserializeSourceFromPb(const pb::Source& pbSource, const android::ResStringPool& srcPool,
- Source* outSource) {
- if (pbSource.has_path_idx()) {
- outSource->path = util::getString(srcPool, pbSource.path_idx());
- }
+void DeserializeSourceFromPb(const pb::Source& pb_source,
+ const android::ResStringPool& src_pool,
+ Source* out_source) {
+ if (pb_source.has_path_idx()) {
+ out_source->path = util::GetString(src_pool, pb_source.path_idx());
+ }
- if (pbSource.has_line_no()) {
- outSource->line = static_cast<size_t>(pbSource.line_no());
- }
+ if (pb_source.has_line_no()) {
+ out_source->line = static_cast<size_t>(pb_source.line_no());
+ }
}
-pb::SymbolStatus_Visibility serializeVisibilityToPb(SymbolState state) {
- switch (state) {
- case SymbolState::kPrivate: return pb::SymbolStatus_Visibility_Private;
- case SymbolState::kPublic: return pb::SymbolStatus_Visibility_Public;
- default: break;
- }
- return pb::SymbolStatus_Visibility_Unknown;
+pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state) {
+ switch (state) {
+ case SymbolState::kPrivate:
+ return pb::SymbolStatus_Visibility_Private;
+ case SymbolState::kPublic:
+ return pb::SymbolStatus_Visibility_Public;
+ default:
+ break;
+ }
+ return pb::SymbolStatus_Visibility_Unknown;
}
-SymbolState deserializeVisibilityFromPb(pb::SymbolStatus_Visibility pbVisibility) {
- switch (pbVisibility) {
- case pb::SymbolStatus_Visibility_Private: return SymbolState::kPrivate;
- case pb::SymbolStatus_Visibility_Public: return SymbolState::kPublic;
- default: break;
- }
- return SymbolState::kUndefined;
+SymbolState DeserializeVisibilityFromPb(
+ pb::SymbolStatus_Visibility pb_visibility) {
+ switch (pb_visibility) {
+ case pb::SymbolStatus_Visibility_Private:
+ return SymbolState::kPrivate;
+ case pb::SymbolStatus_Visibility_Public:
+ return SymbolState::kPublic;
+ default:
+ break;
+ }
+ return SymbolState::kUndefined;
}
-void serializeConfig(const ConfigDescription& config, pb::ConfigDescription* outPbConfig) {
- android::ResTable_config flatConfig = config;
- flatConfig.size = sizeof(flatConfig);
- flatConfig.swapHtoD();
- outPbConfig->set_data(&flatConfig, sizeof(flatConfig));
+void SerializeConfig(const ConfigDescription& config,
+ pb::ConfigDescription* out_pb_config) {
+ android::ResTable_config flat_config = config;
+ flat_config.size = sizeof(flat_config);
+ flat_config.swapHtoD();
+ out_pb_config->set_data(&flat_config, sizeof(flat_config));
}
-bool deserializeConfigDescriptionFromPb(const pb::ConfigDescription& pbConfig,
- ConfigDescription* outConfig) {
- if (!pbConfig.has_data()) {
- return false;
- }
+bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
+ ConfigDescription* out_config) {
+ if (!pb_config.has_data()) {
+ return false;
+ }
- const android::ResTable_config* config;
- if (pbConfig.data().size() > sizeof(*config)) {
- return false;
- }
+ const android::ResTable_config* config;
+ if (pb_config.data().size() > sizeof(*config)) {
+ return false;
+ }
- config = reinterpret_cast<const android::ResTable_config*>(pbConfig.data().data());
- outConfig->copyFromDtoH(*config);
- return true;
+ config = reinterpret_cast<const android::ResTable_config*>(
+ pb_config.data().data());
+ out_config->copyFromDtoH(*config);
+ return true;
}
-pb::Reference_Type serializeReferenceTypeToPb(Reference::Type type) {
- switch (type) {
- case Reference::Type::kResource: return pb::Reference_Type_Ref;
- case Reference::Type::kAttribute: return pb::Reference_Type_Attr;
- default: break;
- }
- return pb::Reference_Type_Ref;
+pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
+ switch (type) {
+ case Reference::Type::kResource:
+ return pb::Reference_Type_Ref;
+ case Reference::Type::kAttribute:
+ return pb::Reference_Type_Attr;
+ default:
+ break;
+ }
+ return pb::Reference_Type_Ref;
}
-Reference::Type deserializeReferenceTypeFromPb(pb::Reference_Type pbType) {
- switch (pbType) {
- case pb::Reference_Type_Ref: return Reference::Type::kResource;
- case pb::Reference_Type_Attr: return Reference::Type::kAttribute;
- default: break;
- }
- return Reference::Type::kResource;
+Reference::Type DeserializeReferenceTypeFromPb(pb::Reference_Type pb_type) {
+ switch (pb_type) {
+ case pb::Reference_Type_Ref:
+ return Reference::Type::kResource;
+ case pb::Reference_Type_Attr:
+ return Reference::Type::kAttribute;
+ default:
+ break;
+ }
+ return Reference::Type::kResource;
}
-pb::Plural_Arity serializePluralEnumToPb(size_t pluralIdx) {
- switch (pluralIdx) {
- case Plural::Zero: return pb::Plural_Arity_Zero;
- case Plural::One: return pb::Plural_Arity_One;
- case Plural::Two: return pb::Plural_Arity_Two;
- case Plural::Few: return pb::Plural_Arity_Few;
- case Plural::Many: return pb::Plural_Arity_Many;
- default: break;
- }
- return pb::Plural_Arity_Other;
+pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx) {
+ switch (plural_idx) {
+ case Plural::Zero:
+ return pb::Plural_Arity_Zero;
+ case Plural::One:
+ return pb::Plural_Arity_One;
+ case Plural::Two:
+ return pb::Plural_Arity_Two;
+ case Plural::Few:
+ return pb::Plural_Arity_Few;
+ case Plural::Many:
+ return pb::Plural_Arity_Many;
+ default:
+ break;
+ }
+ return pb::Plural_Arity_Other;
}
-size_t deserializePluralEnumFromPb(pb::Plural_Arity arity) {
- switch (arity) {
- case pb::Plural_Arity_Zero: return Plural::Zero;
- case pb::Plural_Arity_One: return Plural::One;
- case pb::Plural_Arity_Two: return Plural::Two;
- case pb::Plural_Arity_Few: return Plural::Few;
- case pb::Plural_Arity_Many: return Plural::Many;
- default: break;
- }
- return Plural::Other;
+size_t DeserializePluralEnumFromPb(pb::Plural_Arity arity) {
+ switch (arity) {
+ case pb::Plural_Arity_Zero:
+ return Plural::Zero;
+ case pb::Plural_Arity_One:
+ return Plural::One;
+ case pb::Plural_Arity_Two:
+ return Plural::Two;
+ case pb::Plural_Arity_Few:
+ return Plural::Few;
+ case pb::Plural_Arity_Many:
+ return Plural::Many;
+ default:
+ break;
+ }
+ return Plural::Other;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/proto/ProtoHelpers.h b/tools/aapt2/proto/ProtoHelpers.h
index 02e67f1..735cda0 100644
--- a/tools/aapt2/proto/ProtoHelpers.h
+++ b/tools/aapt2/proto/ProtoHelpers.h
@@ -17,36 +17,45 @@
#ifndef AAPT_PROTO_PROTOHELPERS_H
#define AAPT_PROTO_PROTOHELPERS_H
+#include "androidfw/ResourceTypes.h"
+
#include "ConfigDescription.h"
#include "ResourceTable.h"
#include "Source.h"
#include "StringPool.h"
-
#include "proto/frameworks/base/tools/aapt2/Format.pb.h"
-#include <androidfw/ResourceTypes.h>
-
namespace aapt {
-void serializeStringPoolToPb(const StringPool& pool, pb::StringPool* outPbPool);
+void SerializeStringPoolToPb(const StringPool& pool,
+ pb::StringPool* out_pb_pool);
-void serializeSourceToPb(const Source& source, StringPool* srcPool, pb::Source* outPbSource);
-void deserializeSourceFromPb(const pb::Source& pbSource, const android::ResStringPool& srcPool,
- Source* outSource);
+void SerializeSourceToPb(const Source& source, StringPool* src_pool,
+ pb::Source* out_pb_source);
-pb::SymbolStatus_Visibility serializeVisibilityToPb(SymbolState state);
-SymbolState deserializeVisibilityFromPb(pb::SymbolStatus_Visibility pbVisibility);
+void DeserializeSourceFromPb(const pb::Source& pb_source,
+ const android::ResStringPool& src_pool,
+ Source* out_source);
-void serializeConfig(const ConfigDescription& config, pb::ConfigDescription* outPbConfig);
-bool deserializeConfigDescriptionFromPb(const pb::ConfigDescription& pbConfig,
- ConfigDescription* outConfig);
+pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state);
-pb::Reference_Type serializeReferenceTypeToPb(Reference::Type type);
-Reference::Type deserializeReferenceTypeFromPb(pb::Reference_Type pbType);
+SymbolState DeserializeVisibilityFromPb(
+ pb::SymbolStatus_Visibility pb_visibility);
-pb::Plural_Arity serializePluralEnumToPb(size_t pluralIdx);
-size_t deserializePluralEnumFromPb(pb::Plural_Arity arity);
+void SerializeConfig(const ConfigDescription& config,
+ pb::ConfigDescription* out_pb_config);
-} // namespace aapt
+bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
+ ConfigDescription* out_config);
+
+pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type);
+
+Reference::Type DeserializeReferenceTypeFromPb(pb::Reference_Type pb_type);
+
+pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx);
+
+size_t DeserializePluralEnumFromPb(pb::Plural_Arity arity);
+
+} // namespace aapt
#endif /* AAPT_PROTO_PROTOHELPERS_H */
diff --git a/tools/aapt2/proto/ProtoSerialize.h b/tools/aapt2/proto/ProtoSerialize.h
index cc7c251..39c5003 100644
--- a/tools/aapt2/proto/ProtoSerialize.h
+++ b/tools/aapt2/proto/ProtoSerialize.h
@@ -17,61 +17,61 @@
#ifndef AAPT_FLATTEN_TABLEPROTOSERIALIZER_H
#define AAPT_FLATTEN_TABLEPROTOSERIALIZER_H
+#include "android-base/macros.h"
+#include "google/protobuf/io/coded_stream.h"
+#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+
#include "Diagnostics.h"
#include "ResourceTable.h"
#include "Source.h"
#include "proto/ProtoHelpers.h"
-#include <android-base/macros.h>
-#include <google/protobuf/io/coded_stream.h>
-#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
-
namespace aapt {
class CompiledFileOutputStream {
-public:
- explicit CompiledFileOutputStream(google::protobuf::io::ZeroCopyOutputStream* out);
+ public:
+ explicit CompiledFileOutputStream(
+ google::protobuf::io::ZeroCopyOutputStream* out);
- void WriteLittleEndian32(uint32_t value);
- void WriteCompiledFile(const pb::CompiledFile* compiledFile);
- void WriteData(const BigBuffer* buffer);
- void WriteData(const void* data, size_t len);
- bool HadError();
+ void WriteLittleEndian32(uint32_t value);
+ void WriteCompiledFile(const pb::CompiledFile* compiledFile);
+ void WriteData(const BigBuffer* buffer);
+ void WriteData(const void* data, size_t len);
+ bool HadError();
-private:
- DISALLOW_COPY_AND_ASSIGN(CompiledFileOutputStream);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompiledFileOutputStream);
- void ensureAlignedWrite();
+ void EnsureAlignedWrite();
- google::protobuf::io::CodedOutputStream mOut;
+ google::protobuf::io::CodedOutputStream out_;
};
class CompiledFileInputStream {
-public:
- explicit CompiledFileInputStream(const void* data, size_t size);
+ public:
+ explicit CompiledFileInputStream(const void* data, size_t size);
- bool ReadLittleEndian32(uint32_t* outVal);
- bool ReadCompiledFile(pb::CompiledFile* outVal);
- bool ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen);
+ bool ReadLittleEndian32(uint32_t* outVal);
+ bool ReadCompiledFile(pb::CompiledFile* outVal);
+ bool ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen);
-private:
- DISALLOW_COPY_AND_ASSIGN(CompiledFileInputStream);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompiledFileInputStream);
- void ensureAlignedRead();
+ void EnsureAlignedRead();
- google::protobuf::io::CodedInputStream mIn;
+ google::protobuf::io::CodedInputStream in_;
};
-std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table);
-std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
- const Source& source,
- IDiagnostics* diag);
+std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table);
+std::unique_ptr<ResourceTable> DeserializeTableFromPb(
+ const pb::ResourceTable& pbTable, const Source& source, IDiagnostics* diag);
-std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(const ResourceFile& file);
-std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
- const Source& source,
- IDiagnostics* diag);
+std::unique_ptr<pb::CompiledFile> SerializeCompiledFileToPb(
+ const ResourceFile& file);
+std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
+ const pb::CompiledFile& pbFile, const Source& source, IDiagnostics* diag);
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FLATTEN_TABLEPROTOSERIALIZER_H */
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 595fa6f..d93d495 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -14,458 +14,476 @@
* limitations under the License.
*/
+#include "proto/ProtoSerialize.h"
+
+#include "android-base/logging.h"
+#include "androidfw/ResourceTypes.h"
+
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ValueVisitor.h"
#include "proto/ProtoHelpers.h"
-#include "proto/ProtoSerialize.h"
-
-#include <androidfw/ResourceTypes.h>
namespace aapt {
namespace {
class ReferenceIdToNameVisitor : public ValueVisitor {
-public:
- using ValueVisitor::visit;
+ public:
+ using ValueVisitor::Visit;
- explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping) :
- mMapping(mapping) {
- assert(mMapping);
+ explicit ReferenceIdToNameVisitor(
+ const std::map<ResourceId, ResourceNameRef>* mapping)
+ : mapping_(mapping) {
+ CHECK(mapping_ != nullptr);
+ }
+
+ void Visit(Reference* reference) override {
+ if (!reference->id || !reference->id.value().is_valid()) {
+ return;
}
- void visit(Reference* reference) override {
- if (!reference->id || !reference->id.value().isValid()) {
- return;
- }
-
- ResourceId id = reference->id.value();
- auto cacheIter = mMapping->find(id);
- if (cacheIter != mMapping->end()) {
- reference->name = cacheIter->second.toResourceName();
- }
+ ResourceId id = reference->id.value();
+ auto cache_iter = mapping_->find(id);
+ if (cache_iter != mapping_->end()) {
+ reference->name = cache_iter->second.ToResourceName();
}
+ }
-private:
- const std::map<ResourceId, ResourceNameRef>* mMapping;
+ private:
+ const std::map<ResourceId, ResourceNameRef>* mapping_;
};
class PackagePbDeserializer {
-public:
- PackagePbDeserializer(const android::ResStringPool* valuePool,
- const android::ResStringPool* sourcePool,
- const android::ResStringPool* symbolPool,
- const Source& source, IDiagnostics* diag) :
- mValuePool(valuePool), mSourcePool(sourcePool), mSymbolPool(symbolPool),
- mSource(source), mDiag(diag) {
+ public:
+ PackagePbDeserializer(const android::ResStringPool* valuePool,
+ const android::ResStringPool* sourcePool,
+ const android::ResStringPool* symbolPool,
+ const Source& source, IDiagnostics* diag)
+ : value_pool_(valuePool),
+ source_pool_(sourcePool),
+ symbol_pool_(symbolPool),
+ source_(source),
+ diag_(diag) {}
+
+ public:
+ bool DeserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
+ Maybe<uint8_t> id;
+ if (pbPackage.has_package_id()) {
+ id = static_cast<uint8_t>(pbPackage.package_id());
}
-public:
- bool deserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
- Maybe<uint8_t> id;
- if (pbPackage.has_package_id()) {
- id = static_cast<uint8_t>(pbPackage.package_id());
- }
+ std::map<ResourceId, ResourceNameRef> idIndex;
- std::map<ResourceId, ResourceNameRef> idIndex;
+ ResourceTablePackage* pkg =
+ table->CreatePackage(pbPackage.package_name(), id);
+ for (const pb::Type& pbType : pbPackage.types()) {
+ const ResourceType* resType = ParseResourceType(pbType.name());
+ if (!resType) {
+ diag_->Error(DiagMessage(source_) << "unknown type '" << pbType.name()
+ << "'");
+ return {};
+ }
- ResourceTablePackage* pkg = table->createPackage(pbPackage.package_name(), id);
- for (const pb::Type& pbType : pbPackage.types()) {
- const ResourceType* resType = parseResourceType(pbType.name());
- if (!resType) {
- mDiag->error(DiagMessage(mSource) << "unknown type '" << pbType.name() << "'");
- return {};
+ ResourceTableType* type = pkg->FindOrCreateType(*resType);
+
+ for (const pb::Entry& pbEntry : pbType.entries()) {
+ ResourceEntry* entry = type->FindOrCreateEntry(pbEntry.name());
+
+ // Deserialize the symbol status (public/private with source and
+ // comments).
+ if (pbEntry.has_symbol_status()) {
+ const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
+ if (pbStatus.has_source()) {
+ DeserializeSourceFromPb(pbStatus.source(), *source_pool_,
+ &entry->symbol_status.source);
+ }
+
+ if (pbStatus.has_comment()) {
+ entry->symbol_status.comment = pbStatus.comment();
+ }
+
+ SymbolState visibility =
+ DeserializeVisibilityFromPb(pbStatus.visibility());
+ entry->symbol_status.state = visibility;
+
+ if (visibility == SymbolState::kPublic) {
+ // This is a public symbol, we must encode the ID now if there is
+ // one.
+ if (pbEntry.has_id()) {
+ entry->id = static_cast<uint16_t>(pbEntry.id());
}
- ResourceTableType* type = pkg->findOrCreateType(*resType);
-
- for (const pb::Entry& pbEntry : pbType.entries()) {
- ResourceEntry* entry = type->findOrCreateEntry(pbEntry.name());
-
- // Deserialize the symbol status (public/private with source and comments).
- if (pbEntry.has_symbol_status()) {
- const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
- if (pbStatus.has_source()) {
- deserializeSourceFromPb(pbStatus.source(), *mSourcePool,
- &entry->symbolStatus.source);
- }
-
- if (pbStatus.has_comment()) {
- entry->symbolStatus.comment = pbStatus.comment();
- }
-
- SymbolState visibility = deserializeVisibilityFromPb(pbStatus.visibility());
- entry->symbolStatus.state = visibility;
-
- if (visibility == SymbolState::kPublic) {
- // This is a public symbol, we must encode the ID now if there is one.
- if (pbEntry.has_id()) {
- entry->id = static_cast<uint16_t>(pbEntry.id());
- }
-
- if (type->symbolStatus.state != SymbolState::kPublic) {
- // If the type has not been made public, do so now.
- type->symbolStatus.state = SymbolState::kPublic;
- if (pbType.has_id()) {
- type->id = static_cast<uint8_t>(pbType.id());
- }
- }
- } else if (visibility == SymbolState::kPrivate) {
- if (type->symbolStatus.state == SymbolState::kUndefined) {
- type->symbolStatus.state = SymbolState::kPrivate;
- }
- }
- }
-
- ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
- if (resId.isValid()) {
- idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
- }
-
- for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
- const pb::ConfigDescription& pbConfig = pbConfigValue.config();
-
- ConfigDescription config;
- if (!deserializeConfigDescriptionFromPb(pbConfig, &config)) {
- mDiag->error(DiagMessage(mSource) << "invalid configuration");
- return {};
- }
-
- ResourceConfigValue* configValue = entry->findOrCreateValue(config,
- pbConfig.product());
- if (configValue->value) {
- // Duplicate config.
- mDiag->error(DiagMessage(mSource) << "duplicate configuration");
- return {};
- }
-
- configValue->value = deserializeValueFromPb(pbConfigValue.value(),
- config, &table->stringPool);
- if (!configValue->value) {
- return {};
- }
- }
+ if (type->symbol_status.state != SymbolState::kPublic) {
+ // If the type has not been made public, do so now.
+ type->symbol_status.state = SymbolState::kPublic;
+ if (pbType.has_id()) {
+ type->id = static_cast<uint8_t>(pbType.id());
+ }
}
+ } else if (visibility == SymbolState::kPrivate) {
+ if (type->symbol_status.state == SymbolState::kUndefined) {
+ type->symbol_status.state = SymbolState::kPrivate;
+ }
+ }
}
- ReferenceIdToNameVisitor visitor(&idIndex);
- visitAllValuesInPackage(pkg, &visitor);
- return true;
+ ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
+ if (resId.is_valid()) {
+ idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
+ }
+
+ for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
+ const pb::ConfigDescription& pbConfig = pbConfigValue.config();
+
+ ConfigDescription config;
+ if (!DeserializeConfigDescriptionFromPb(pbConfig, &config)) {
+ diag_->Error(DiagMessage(source_) << "invalid configuration");
+ return {};
+ }
+
+ ResourceConfigValue* configValue =
+ entry->FindOrCreateValue(config, pbConfig.product());
+ if (configValue->value) {
+ // Duplicate config.
+ diag_->Error(DiagMessage(source_) << "duplicate configuration");
+ return {};
+ }
+
+ configValue->value = DeserializeValueFromPb(
+ pbConfigValue.value(), config, &table->string_pool);
+ if (!configValue->value) {
+ return {};
+ }
+ }
+ }
}
-private:
- std::unique_ptr<Item> deserializeItemFromPb(const pb::Item& pbItem,
+ ReferenceIdToNameVisitor visitor(&idIndex);
+ VisitAllValuesInPackage(pkg, &visitor);
+ return true;
+ }
+
+ private:
+ std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
+ const ConfigDescription& config,
+ StringPool* pool) {
+ if (pb_item.has_ref()) {
+ const pb::Reference& pb_ref = pb_item.ref();
+ std::unique_ptr<Reference> ref = util::make_unique<Reference>();
+ if (!DeserializeReferenceFromPb(pb_ref, ref.get())) {
+ return {};
+ }
+ return std::move(ref);
+
+ } else if (pb_item.has_prim()) {
+ const pb::Primitive& pb_prim = pb_item.prim();
+ android::Res_value prim = {};
+ prim.dataType = static_cast<uint8_t>(pb_prim.type());
+ prim.data = pb_prim.data();
+ return util::make_unique<BinaryPrimitive>(prim);
+
+ } else if (pb_item.has_id()) {
+ return util::make_unique<Id>();
+
+ } else if (pb_item.has_str()) {
+ const uint32_t idx = pb_item.str().idx();
+ const std::string str = util::GetString(*value_pool_, idx);
+
+ const android::ResStringPool_span* spans = value_pool_->styleAt(idx);
+ if (spans && spans->name.index != android::ResStringPool_span::END) {
+ StyleString style_str = {str};
+ while (spans->name.index != android::ResStringPool_span::END) {
+ style_str.spans.push_back(
+ Span{util::GetString(*value_pool_, spans->name.index),
+ spans->firstChar, spans->lastChar});
+ spans++;
+ }
+ return util::make_unique<StyledString>(pool->MakeRef(
+ style_str,
+ StringPool::Context(StringPool::Context::kStylePriority, config)));
+ }
+ return util::make_unique<String>(
+ pool->MakeRef(str, StringPool::Context(config)));
+
+ } else if (pb_item.has_raw_str()) {
+ const uint32_t idx = pb_item.raw_str().idx();
+ const std::string str = util::GetString(*value_pool_, idx);
+ return util::make_unique<RawString>(
+ pool->MakeRef(str, StringPool::Context(config)));
+
+ } else if (pb_item.has_file()) {
+ const uint32_t idx = pb_item.file().path_idx();
+ const std::string str = util::GetString(*value_pool_, idx);
+ return util::make_unique<FileReference>(pool->MakeRef(
+ str,
+ StringPool::Context(StringPool::Context::kHighPriority, config)));
+
+ } else {
+ diag_->Error(DiagMessage(source_) << "unknown item");
+ }
+ return {};
+ }
+
+ std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
const ConfigDescription& config,
StringPool* pool) {
- if (pbItem.has_ref()) {
- const pb::Reference& pbRef = pbItem.ref();
- std::unique_ptr<Reference> ref = util::make_unique<Reference>();
- if (!deserializeReferenceFromPb(pbRef, ref.get())) {
- return {};
- }
- return std::move(ref);
+ const bool is_weak = pb_value.has_weak() ? pb_value.weak() : false;
- } else if (pbItem.has_prim()) {
- const pb::Primitive& pbPrim = pbItem.prim();
- android::Res_value prim = {};
- prim.dataType = static_cast<uint8_t>(pbPrim.type());
- prim.data = pbPrim.data();
- return util::make_unique<BinaryPrimitive>(prim);
-
- } else if (pbItem.has_id()) {
- return util::make_unique<Id>();
-
- } else if (pbItem.has_str()) {
- const uint32_t idx = pbItem.str().idx();
- const std::string str = util::getString(*mValuePool, idx);
-
- const android::ResStringPool_span* spans = mValuePool->styleAt(idx);
- if (spans && spans->name.index != android::ResStringPool_span::END) {
- StyleString styleStr = { str };
- while (spans->name.index != android::ResStringPool_span::END) {
- styleStr.spans.push_back(Span{
- util::getString(*mValuePool, spans->name.index),
- spans->firstChar,
- spans->lastChar
- });
- spans++;
- }
- return util::make_unique<StyledString>(
- pool->makeRef(styleStr, StringPool::Context{ 1, config }));
- }
- return util::make_unique<String>(
- pool->makeRef(str, StringPool::Context{ 1, config }));
-
- } else if (pbItem.has_raw_str()) {
- const uint32_t idx = pbItem.raw_str().idx();
- const std::string str = util::getString(*mValuePool, idx);
- return util::make_unique<RawString>(
- pool->makeRef(str, StringPool::Context{ 1, config }));
-
- } else if (pbItem.has_file()) {
- const uint32_t idx = pbItem.file().path_idx();
- const std::string str = util::getString(*mValuePool, idx);
- return util::make_unique<FileReference>(
- pool->makeRef(str, StringPool::Context{ 0, config }));
-
- } else {
- mDiag->error(DiagMessage(mSource) << "unknown item");
- }
+ std::unique_ptr<Value> value;
+ if (pb_value.has_item()) {
+ value = DeserializeItemFromPb(pb_value.item(), config, pool);
+ if (!value) {
return {};
- }
+ }
- std::unique_ptr<Value> deserializeValueFromPb(const pb::Value& pbValue,
- const ConfigDescription& config,
- StringPool* pool) {
- const bool isWeak = pbValue.has_weak() ? pbValue.weak() : false;
-
- std::unique_ptr<Value> value;
- if (pbValue.has_item()) {
- value = deserializeItemFromPb(pbValue.item(), config, pool);
- if (!value) {
- return {};
- }
-
- } else if (pbValue.has_compound_value()) {
- const pb::CompoundValue& pbCompoundValue = pbValue.compound_value();
- if (pbCompoundValue.has_attr()) {
- const pb::Attribute& pbAttr = pbCompoundValue.attr();
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
- attr->typeMask = pbAttr.format_flags();
- attr->minInt = pbAttr.min_int();
- attr->maxInt = pbAttr.max_int();
- for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) {
- Attribute::Symbol symbol;
- deserializeItemCommon(pbSymbol, &symbol.symbol);
- if (!deserializeReferenceFromPb(pbSymbol.name(), &symbol.symbol)) {
- return {};
- }
- symbol.value = pbSymbol.value();
- attr->symbols.push_back(std::move(symbol));
- }
- value = std::move(attr);
-
- } else if (pbCompoundValue.has_style()) {
- const pb::Style& pbStyle = pbCompoundValue.style();
- std::unique_ptr<Style> style = util::make_unique<Style>();
- if (pbStyle.has_parent()) {
- style->parent = Reference();
- if (!deserializeReferenceFromPb(pbStyle.parent(), &style->parent.value())) {
- return {};
- }
-
- if (pbStyle.has_parent_source()) {
- Source parentSource;
- deserializeSourceFromPb(pbStyle.parent_source(), *mSourcePool,
- &parentSource);
- style->parent.value().setSource(std::move(parentSource));
- }
- }
-
- for (const pb::Style_Entry& pbEntry : pbStyle.entries()) {
- Style::Entry entry;
- deserializeItemCommon(pbEntry, &entry.key);
- if (!deserializeReferenceFromPb(pbEntry.key(), &entry.key)) {
- return {};
- }
-
- entry.value = deserializeItemFromPb(pbEntry.item(), config, pool);
- if (!entry.value) {
- return {};
- }
-
- deserializeItemCommon(pbEntry, entry.value.get());
- style->entries.push_back(std::move(entry));
- }
- value = std::move(style);
-
- } else if (pbCompoundValue.has_styleable()) {
- const pb::Styleable& pbStyleable = pbCompoundValue.styleable();
- std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
- for (const pb::Styleable_Entry& pbEntry : pbStyleable.entries()) {
- Reference attrRef;
- deserializeItemCommon(pbEntry, &attrRef);
- deserializeReferenceFromPb(pbEntry.attr(), &attrRef);
- styleable->entries.push_back(std::move(attrRef));
- }
- value = std::move(styleable);
-
- } else if (pbCompoundValue.has_array()) {
- const pb::Array& pbArray = pbCompoundValue.array();
- std::unique_ptr<Array> array = util::make_unique<Array>();
- for (const pb::Array_Entry& pbEntry : pbArray.entries()) {
- std::unique_ptr<Item> item = deserializeItemFromPb(pbEntry.item(), config,
- pool);
- if (!item) {
- return {};
- }
-
- deserializeItemCommon(pbEntry, item.get());
- array->items.push_back(std::move(item));
- }
- value = std::move(array);
-
- } else if (pbCompoundValue.has_plural()) {
- const pb::Plural& pbPlural = pbCompoundValue.plural();
- std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- for (const pb::Plural_Entry& pbEntry : pbPlural.entries()) {
- size_t pluralIdx = deserializePluralEnumFromPb(pbEntry.arity());
- plural->values[pluralIdx] = deserializeItemFromPb(pbEntry.item(), config,
- pool);
- if (!plural->values[pluralIdx]) {
- return {};
- }
-
- deserializeItemCommon(pbEntry, plural->values[pluralIdx].get());
- }
- value = std::move(plural);
-
- } else {
- mDiag->error(DiagMessage(mSource) << "unknown compound value");
- return {};
- }
- } else {
- mDiag->error(DiagMessage(mSource) << "unknown value");
+ } else if (pb_value.has_compound_value()) {
+ const pb::CompoundValue& pb_compound_value = pb_value.compound_value();
+ if (pb_compound_value.has_attr()) {
+ const pb::Attribute& pb_attr = pb_compound_value.attr();
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(is_weak);
+ attr->type_mask = pb_attr.format_flags();
+ attr->min_int = pb_attr.min_int();
+ attr->max_int = pb_attr.max_int();
+ for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbols()) {
+ Attribute::Symbol symbol;
+ DeserializeItemCommon(pb_symbol, &symbol.symbol);
+ if (!DeserializeReferenceFromPb(pb_symbol.name(), &symbol.symbol)) {
return {};
+ }
+ symbol.value = pb_symbol.value();
+ attr->symbols.push_back(std::move(symbol));
+ }
+ value = std::move(attr);
+
+ } else if (pb_compound_value.has_style()) {
+ const pb::Style& pb_style = pb_compound_value.style();
+ std::unique_ptr<Style> style = util::make_unique<Style>();
+ if (pb_style.has_parent()) {
+ style->parent = Reference();
+ if (!DeserializeReferenceFromPb(pb_style.parent(),
+ &style->parent.value())) {
+ return {};
+ }
+
+ if (pb_style.has_parent_source()) {
+ Source parent_source;
+ DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_,
+ &parent_source);
+ style->parent.value().SetSource(std::move(parent_source));
+ }
}
- assert(value && "forgot to set value");
+ for (const pb::Style_Entry& pb_entry : pb_style.entries()) {
+ Style::Entry entry;
+ DeserializeItemCommon(pb_entry, &entry.key);
+ if (!DeserializeReferenceFromPb(pb_entry.key(), &entry.key)) {
+ return {};
+ }
- value->setWeak(isWeak);
- deserializeItemCommon(pbValue, value.get());
- return value;
+ entry.value = DeserializeItemFromPb(pb_entry.item(), config, pool);
+ if (!entry.value) {
+ return {};
+ }
+
+ DeserializeItemCommon(pb_entry, entry.value.get());
+ style->entries.push_back(std::move(entry));
+ }
+ value = std::move(style);
+
+ } else if (pb_compound_value.has_styleable()) {
+ const pb::Styleable& pb_styleable = pb_compound_value.styleable();
+ std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+ for (const pb::Styleable_Entry& pb_entry : pb_styleable.entries()) {
+ Reference attr_ref;
+ DeserializeItemCommon(pb_entry, &attr_ref);
+ DeserializeReferenceFromPb(pb_entry.attr(), &attr_ref);
+ styleable->entries.push_back(std::move(attr_ref));
+ }
+ value = std::move(styleable);
+
+ } else if (pb_compound_value.has_array()) {
+ const pb::Array& pb_array = pb_compound_value.array();
+ std::unique_ptr<Array> array = util::make_unique<Array>();
+ for (const pb::Array_Entry& pb_entry : pb_array.entries()) {
+ std::unique_ptr<Item> item =
+ DeserializeItemFromPb(pb_entry.item(), config, pool);
+ if (!item) {
+ return {};
+ }
+
+ DeserializeItemCommon(pb_entry, item.get());
+ array->items.push_back(std::move(item));
+ }
+ value = std::move(array);
+
+ } else if (pb_compound_value.has_plural()) {
+ const pb::Plural& pb_plural = pb_compound_value.plural();
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ for (const pb::Plural_Entry& pb_entry : pb_plural.entries()) {
+ size_t pluralIdx = DeserializePluralEnumFromPb(pb_entry.arity());
+ plural->values[pluralIdx] =
+ DeserializeItemFromPb(pb_entry.item(), config, pool);
+ if (!plural->values[pluralIdx]) {
+ return {};
+ }
+
+ DeserializeItemCommon(pb_entry, plural->values[pluralIdx].get());
+ }
+ value = std::move(plural);
+
+ } else {
+ diag_->Error(DiagMessage(source_) << "unknown compound value");
+ return {};
+ }
+ } else {
+ diag_->Error(DiagMessage(source_) << "unknown value");
+ return {};
}
- bool deserializeReferenceFromPb(const pb::Reference& pbRef, Reference* outRef) {
- outRef->referenceType = deserializeReferenceTypeFromPb(pbRef.type());
- outRef->privateReference = pbRef.private_();
+ CHECK(value) << "forgot to set value";
- if (!pbRef.has_id() && !pbRef.has_symbol_idx()) {
- return false;
- }
+ value->SetWeak(is_weak);
+ DeserializeItemCommon(pb_value, value.get());
+ return value;
+ }
- if (pbRef.has_id()) {
- outRef->id = ResourceId(pbRef.id());
- }
+ bool DeserializeReferenceFromPb(const pb::Reference& pb_ref,
+ Reference* out_ref) {
+ out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type());
+ out_ref->private_reference = pb_ref.private_();
- if (pbRef.has_symbol_idx()) {
- const std::string strSymbol = util::getString(*mSymbolPool, pbRef.symbol_idx());
- ResourceNameRef nameRef;
- if (!ResourceUtils::parseResourceName(strSymbol, &nameRef, nullptr)) {
- mDiag->error(DiagMessage(mSource) << "invalid reference name '"
- << strSymbol << "'");
- return false;
- }
-
- outRef->name = nameRef.toResourceName();
- }
- return true;
+ if (!pb_ref.has_id() && !pb_ref.has_symbol_idx()) {
+ return false;
}
- template <typename T>
- void deserializeItemCommon(const T& pbItem, Value* outValue) {
- if (pbItem.has_source()) {
- Source source;
- deserializeSourceFromPb(pbItem.source(), *mSourcePool, &source);
- outValue->setSource(std::move(source));
- }
-
- if (pbItem.has_comment()) {
- outValue->setComment(pbItem.comment());
- }
+ if (pb_ref.has_id()) {
+ out_ref->id = ResourceId(pb_ref.id());
}
-private:
- const android::ResStringPool* mValuePool;
- const android::ResStringPool* mSourcePool;
- const android::ResStringPool* mSymbolPool;
- const Source mSource;
- IDiagnostics* mDiag;
+ if (pb_ref.has_symbol_idx()) {
+ const std::string str_symbol =
+ util::GetString(*symbol_pool_, pb_ref.symbol_idx());
+ ResourceNameRef name_ref;
+ if (!ResourceUtils::ParseResourceName(str_symbol, &name_ref, nullptr)) {
+ diag_->Error(DiagMessage(source_) << "invalid reference name '"
+ << str_symbol << "'");
+ return false;
+ }
+
+ out_ref->name = name_ref.ToResourceName();
+ }
+ return true;
+ }
+
+ template <typename T>
+ void DeserializeItemCommon(const T& pb_item, Value* out_value) {
+ if (pb_item.has_source()) {
+ Source source;
+ DeserializeSourceFromPb(pb_item.source(), *source_pool_, &source);
+ out_value->SetSource(std::move(source));
+ }
+
+ if (pb_item.has_comment()) {
+ out_value->SetComment(pb_item.comment());
+ }
+ }
+
+ private:
+ const android::ResStringPool* value_pool_;
+ const android::ResStringPool* source_pool_;
+ const android::ResStringPool* symbol_pool_;
+ const Source source_;
+ IDiagnostics* diag_;
};
-} // namespace
+} // namespace
-std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
- const Source& source,
- IDiagnostics* diag) {
- // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
- // causes errors when qualifying it with android::
- using namespace android;
+std::unique_ptr<ResourceTable> DeserializeTableFromPb(
+ const pb::ResourceTable& pb_table, const Source& source,
+ IDiagnostics* diag) {
+ // We import the android namespace because on Windows NO_ERROR is a macro, not
+ // an enum, which
+ // causes errors when qualifying it with android::
+ using namespace android;
- std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
+ std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
- if (!pbTable.has_string_pool()) {
- diag->error(DiagMessage(source) << "no string pool found");
- return {};
- }
+ if (!pb_table.has_string_pool()) {
+ diag->Error(DiagMessage(source) << "no string pool found");
+ return {};
+ }
- ResStringPool valuePool;
- status_t result = valuePool.setTo(pbTable.string_pool().data().data(),
- pbTable.string_pool().data().size());
+ ResStringPool value_pool;
+ status_t result = value_pool.setTo(pb_table.string_pool().data().data(),
+ pb_table.string_pool().data().size());
+ if (result != NO_ERROR) {
+ diag->Error(DiagMessage(source) << "invalid string pool");
+ return {};
+ }
+
+ ResStringPool source_pool;
+ if (pb_table.has_source_pool()) {
+ result = source_pool.setTo(pb_table.source_pool().data().data(),
+ pb_table.source_pool().data().size());
if (result != NO_ERROR) {
- diag->error(DiagMessage(source) << "invalid string pool");
- return {};
+ diag->Error(DiagMessage(source) << "invalid source pool");
+ return {};
}
+ }
- ResStringPool sourcePool;
- if (pbTable.has_source_pool()) {
- result = sourcePool.setTo(pbTable.source_pool().data().data(),
- pbTable.source_pool().data().size());
- if (result != NO_ERROR) {
- diag->error(DiagMessage(source) << "invalid source pool");
- return {};
- }
+ ResStringPool symbol_pool;
+ if (pb_table.has_symbol_pool()) {
+ result = symbol_pool.setTo(pb_table.symbol_pool().data().data(),
+ pb_table.symbol_pool().data().size());
+ if (result != NO_ERROR) {
+ diag->Error(DiagMessage(source) << "invalid symbol pool");
+ return {};
}
+ }
- ResStringPool symbolPool;
- if (pbTable.has_symbol_pool()) {
- result = symbolPool.setTo(pbTable.symbol_pool().data().data(),
- pbTable.symbol_pool().data().size());
- if (result != NO_ERROR) {
- diag->error(DiagMessage(source) << "invalid symbol pool");
- return {};
- }
+ PackagePbDeserializer package_pb_deserializer(&value_pool, &source_pool,
+ &symbol_pool, source, diag);
+ for (const pb::Package& pb_package : pb_table.packages()) {
+ if (!package_pb_deserializer.DeserializeFromPb(pb_package, table.get())) {
+ return {};
}
-
- PackagePbDeserializer packagePbDeserializer(&valuePool, &sourcePool, &symbolPool, source, diag);
- for (const pb::Package& pbPackage : pbTable.packages()) {
- if (!packagePbDeserializer.deserializeFromPb(pbPackage, table.get())) {
- return {};
- }
- }
- return table;
+ }
+ return table;
}
-std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
- const Source& source,
- IDiagnostics* diag) {
- std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
+std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
+ const pb::CompiledFile& pb_file, const Source& source, IDiagnostics* diag) {
+ std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
- ResourceNameRef nameRef;
+ ResourceNameRef name_ref;
- // Need to create an lvalue here so that nameRef can point to something real.
- if (!ResourceUtils::parseResourceName(pbFile.resource_name(), &nameRef)) {
- diag->error(DiagMessage(source) << "invalid resource name in compiled file header: "
- << pbFile.resource_name());
- return {};
+ // Need to create an lvalue here so that nameRef can point to something real.
+ if (!ResourceUtils::ParseResourceName(pb_file.resource_name(), &name_ref)) {
+ diag->Error(DiagMessage(source)
+ << "invalid resource name in compiled file header: "
+ << pb_file.resource_name());
+ return {};
+ }
+ file->name = name_ref.ToResourceName();
+ file->source.path = pb_file.source_path();
+ DeserializeConfigDescriptionFromPb(pb_file.config(), &file->config);
+
+ for (const pb::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbols()) {
+ // Need to create an lvalue here so that nameRef can point to something
+ // real.
+ if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(),
+ &name_ref)) {
+ diag->Error(DiagMessage(source)
+ << "invalid resource name for exported symbol in "
+ "compiled file header: "
+ << pb_file.resource_name());
+ return {};
}
- file->name = nameRef.toResourceName();
- file->source.path = pbFile.source_path();
- deserializeConfigDescriptionFromPb(pbFile.config(), &file->config);
-
- for (const pb::CompiledFile_Symbol& pbSymbol : pbFile.exported_symbols()) {
- // Need to create an lvalue here so that nameRef can point to something real.
- if (!ResourceUtils::parseResourceName(pbSymbol.resource_name(), &nameRef)) {
- diag->error(DiagMessage(source) << "invalid resource name for exported symbol in "
- "compiled file header: "
- << pbFile.resource_name());
- return {};
- }
- file->exportedSymbols.push_back(
- SourcedResourceName{ nameRef.toResourceName(), pbSymbol.line_no() });
- }
- return file;
+ file->exported_symbols.push_back(
+ SourcedResourceName{name_ref.ToResourceName(), pb_symbol.line_no()});
+ }
+ return file;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index a5c2cbc..68db6b3 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -22,6 +22,8 @@
#include "proto/ProtoSerialize.h"
#include "util/BigBuffer.h"
+#include <android-base/logging.h>
+
using google::protobuf::io::CodedOutputStream;
using google::protobuf::io::CodedInputStream;
using google::protobuf::io::ZeroCopyOutputStream;
@@ -31,179 +33,185 @@
namespace {
class PbSerializerVisitor : public RawValueVisitor {
-public:
- using RawValueVisitor::visit;
+ public:
+ using RawValueVisitor::Visit;
- /**
- * Constructor to use when expecting to serialize any value.
- */
- PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Value* outPbValue) :
- mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(outPbValue),
- mOutPbItem(nullptr) {
+ /**
+ * Constructor to use when expecting to serialize any value.
+ */
+ PbSerializerVisitor(StringPool* source_pool, StringPool* symbol_pool,
+ pb::Value* out_pb_value)
+ : source_pool_(source_pool),
+ symbol_pool_(symbol_pool),
+ out_pb_value_(out_pb_value),
+ out_pb_item_(nullptr) {}
+
+ /**
+ * Constructor to use when expecting to serialize an Item.
+ */
+ PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool,
+ pb::Item* outPbItem)
+ : source_pool_(sourcePool),
+ symbol_pool_(symbolPool),
+ out_pb_value_(nullptr),
+ out_pb_item_(outPbItem) {}
+
+ void Visit(Reference* ref) override {
+ SerializeReferenceToPb(*ref, pb_item()->mutable_ref());
+ }
+
+ void Visit(String* str) override {
+ pb_item()->mutable_str()->set_idx(str->value.index());
+ }
+
+ void Visit(StyledString* str) override {
+ pb_item()->mutable_str()->set_idx(str->value.index());
+ }
+
+ void Visit(FileReference* file) override {
+ pb_item()->mutable_file()->set_path_idx(file->path.index());
+ }
+
+ void Visit(Id* id) override { pb_item()->mutable_id(); }
+
+ void Visit(RawString* raw_str) override {
+ pb_item()->mutable_raw_str()->set_idx(raw_str->value.index());
+ }
+
+ void Visit(BinaryPrimitive* prim) override {
+ android::Res_value val = {};
+ prim->Flatten(&val);
+
+ pb::Primitive* pb_prim = pb_item()->mutable_prim();
+ pb_prim->set_type(val.dataType);
+ pb_prim->set_data(val.data);
+ }
+
+ void VisitItem(Item* item) override { LOG(FATAL) << "unimplemented item"; }
+
+ void Visit(Attribute* attr) override {
+ pb::Attribute* pb_attr = pb_compound_value()->mutable_attr();
+ pb_attr->set_format_flags(attr->type_mask);
+ pb_attr->set_min_int(attr->min_int);
+ pb_attr->set_max_int(attr->max_int);
+
+ for (auto& symbol : attr->symbols) {
+ pb::Attribute_Symbol* pb_symbol = pb_attr->add_symbols();
+ SerializeItemCommonToPb(symbol.symbol, pb_symbol);
+ SerializeReferenceToPb(symbol.symbol, pb_symbol->mutable_name());
+ pb_symbol->set_value(symbol.value);
+ }
+ }
+
+ void Visit(Style* style) override {
+ pb::Style* pb_style = pb_compound_value()->mutable_style();
+ if (style->parent) {
+ SerializeReferenceToPb(style->parent.value(), pb_style->mutable_parent());
+ SerializeSourceToPb(style->parent.value().GetSource(), source_pool_,
+ pb_style->mutable_parent_source());
}
- /**
- * Constructor to use when expecting to serialize an Item.
- */
- PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Item* outPbItem) :
- mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(nullptr),
- mOutPbItem(outPbItem) {
+ for (Style::Entry& entry : style->entries) {
+ pb::Style_Entry* pb_entry = pb_style->add_entries();
+ SerializeReferenceToPb(entry.key, pb_entry->mutable_key());
+
+ pb::Item* pb_item = pb_entry->mutable_item();
+ SerializeItemCommonToPb(entry.key, pb_entry);
+ PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_item);
+ entry.value->Accept(&sub_visitor);
+ }
+ }
+
+ void Visit(Styleable* styleable) override {
+ pb::Styleable* pb_styleable = pb_compound_value()->mutable_styleable();
+ for (Reference& entry : styleable->entries) {
+ pb::Styleable_Entry* pb_entry = pb_styleable->add_entries();
+ SerializeItemCommonToPb(entry, pb_entry);
+ SerializeReferenceToPb(entry, pb_entry->mutable_attr());
+ }
+ }
+
+ void Visit(Array* array) override {
+ pb::Array* pb_array = pb_compound_value()->mutable_array();
+ for (auto& value : array->items) {
+ pb::Array_Entry* pb_entry = pb_array->add_entries();
+ SerializeItemCommonToPb(*value, pb_entry);
+ PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_,
+ pb_entry->mutable_item());
+ value->Accept(&sub_visitor);
+ }
+ }
+
+ void Visit(Plural* plural) override {
+ pb::Plural* pb_plural = pb_compound_value()->mutable_plural();
+ const size_t count = plural->values.size();
+ for (size_t i = 0; i < count; i++) {
+ if (!plural->values[i]) {
+ // No plural value set here.
+ continue;
+ }
+
+ pb::Plural_Entry* pb_entry = pb_plural->add_entries();
+ pb_entry->set_arity(SerializePluralEnumToPb(i));
+ pb::Item* pb_element = pb_entry->mutable_item();
+ SerializeItemCommonToPb(*plural->values[i], pb_entry);
+ PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_element);
+ plural->values[i]->Accept(&sub_visitor);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PbSerializerVisitor);
+
+ pb::Item* pb_item() {
+ if (out_pb_value_) {
+ return out_pb_value_->mutable_item();
+ }
+ return out_pb_item_;
+ }
+
+ pb::CompoundValue* pb_compound_value() {
+ CHECK(out_pb_value_ != nullptr);
+ return out_pb_value_->mutable_compound_value();
+ }
+
+ template <typename T>
+ void SerializeItemCommonToPb(const Item& item, T* pb_item) {
+ SerializeSourceToPb(item.GetSource(), source_pool_,
+ pb_item->mutable_source());
+ if (!item.GetComment().empty()) {
+ pb_item->set_comment(item.GetComment());
+ }
+ }
+
+ void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) {
+ if (ref.id) {
+ pb_ref->set_id(ref.id.value().id);
}
- void visit(Reference* ref) override {
- serializeReferenceToPb(*ref, getPbItem()->mutable_ref());
+ if (ref.name) {
+ StringPool::Ref symbol_ref =
+ symbol_pool_->MakeRef(ref.name.value().ToString());
+ pb_ref->set_symbol_idx(static_cast<uint32_t>(symbol_ref.index()));
}
- void visit(String* str) override {
- getPbItem()->mutable_str()->set_idx(str->value.getIndex());
- }
+ pb_ref->set_private_(ref.private_reference);
+ pb_ref->set_type(SerializeReferenceTypeToPb(ref.reference_type));
+ }
- void visit(StyledString* str) override {
- getPbItem()->mutable_str()->set_idx(str->value.getIndex());
- }
-
- void visit(FileReference* file) override {
- getPbItem()->mutable_file()->set_path_idx(file->path.getIndex());
- }
-
- void visit(Id* id) override {
- getPbItem()->mutable_id();
- }
-
- void visit(RawString* rawStr) override {
- getPbItem()->mutable_raw_str()->set_idx(rawStr->value.getIndex());
- }
-
- void visit(BinaryPrimitive* prim) override {
- android::Res_value val = {};
- prim->flatten(&val);
-
- pb::Primitive* pbPrim = getPbItem()->mutable_prim();
- pbPrim->set_type(val.dataType);
- pbPrim->set_data(val.data);
- }
-
- void visitItem(Item* item) override {
- assert(false && "unimplemented item");
- }
-
- void visit(Attribute* attr) override {
- pb::Attribute* pbAttr = getPbCompoundValue()->mutable_attr();
- pbAttr->set_format_flags(attr->typeMask);
- pbAttr->set_min_int(attr->minInt);
- pbAttr->set_max_int(attr->maxInt);
-
- for (auto& symbol : attr->symbols) {
- pb::Attribute_Symbol* pbSymbol = pbAttr->add_symbols();
- serializeItemCommonToPb(symbol.symbol, pbSymbol);
- serializeReferenceToPb(symbol.symbol, pbSymbol->mutable_name());
- pbSymbol->set_value(symbol.value);
- }
- }
-
- void visit(Style* style) override {
- pb::Style* pbStyle = getPbCompoundValue()->mutable_style();
- if (style->parent) {
- serializeReferenceToPb(style->parent.value(), pbStyle->mutable_parent());
- serializeSourceToPb(style->parent.value().getSource(),
- mSourcePool,
- pbStyle->mutable_parent_source());
- }
-
- for (Style::Entry& entry : style->entries) {
- pb::Style_Entry* pbEntry = pbStyle->add_entries();
- serializeReferenceToPb(entry.key, pbEntry->mutable_key());
-
- pb::Item* pbItem = pbEntry->mutable_item();
- serializeItemCommonToPb(entry.key, pbEntry);
- PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbItem);
- entry.value->accept(&subVisitor);
- }
- }
-
- void visit(Styleable* styleable) override {
- pb::Styleable* pbStyleable = getPbCompoundValue()->mutable_styleable();
- for (Reference& entry : styleable->entries) {
- pb::Styleable_Entry* pbEntry = pbStyleable->add_entries();
- serializeItemCommonToPb(entry, pbEntry);
- serializeReferenceToPb(entry, pbEntry->mutable_attr());
- }
- }
-
- void visit(Array* array) override {
- pb::Array* pbArray = getPbCompoundValue()->mutable_array();
- for (auto& value : array->items) {
- pb::Array_Entry* pbEntry = pbArray->add_entries();
- serializeItemCommonToPb(*value, pbEntry);
- PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbEntry->mutable_item());
- value->accept(&subVisitor);
- }
- }
-
- void visit(Plural* plural) override {
- pb::Plural* pbPlural = getPbCompoundValue()->mutable_plural();
- const size_t count = plural->values.size();
- for (size_t i = 0; i < count; i++) {
- if (!plural->values[i]) {
- // No plural value set here.
- continue;
- }
-
- pb::Plural_Entry* pbEntry = pbPlural->add_entries();
- pbEntry->set_arity(serializePluralEnumToPb(i));
- pb::Item* pbElement = pbEntry->mutable_item();
- serializeItemCommonToPb(*plural->values[i], pbEntry);
- PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbElement);
- plural->values[i]->accept(&subVisitor);
- }
- }
-
-private:
- pb::Item* getPbItem() {
- if (mOutPbValue) {
- return mOutPbValue->mutable_item();
- }
- return mOutPbItem;
- }
-
- pb::CompoundValue* getPbCompoundValue() {
- assert(mOutPbValue);
- return mOutPbValue->mutable_compound_value();
- }
-
- template <typename T>
- void serializeItemCommonToPb(const Item& item, T* pbItem) {
- serializeSourceToPb(item.getSource(), mSourcePool, pbItem->mutable_source());
- if (!item.getComment().empty()) {
- pbItem->set_comment(item.getComment());
- }
- }
-
- void serializeReferenceToPb(const Reference& ref, pb::Reference* pbRef) {
- if (ref.id) {
- pbRef->set_id(ref.id.value().id);
- }
-
- if (ref.name) {
- StringPool::Ref symbolRef = mSymbolPool->makeRef(ref.name.value().toString());
- pbRef->set_symbol_idx(static_cast<uint32_t>(symbolRef.getIndex()));
- }
-
- pbRef->set_private_(ref.privateReference);
- pbRef->set_type(serializeReferenceTypeToPb(ref.referenceType));
- }
-
- StringPool* mSourcePool;
- StringPool* mSymbolPool;
- pb::Value* mOutPbValue;
- pb::Item* mOutPbItem;
+ StringPool* source_pool_;
+ StringPool* symbol_pool_;
+ pb::Value* out_pb_value_;
+ pb::Item* out_pb_item_;
};
-} // namespace
+} // namespace
-std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table) {
- // We must do this before writing the resources, since the string pool IDs may change.
- table->stringPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
+ // We must do this before writing the resources, since the string pool IDs may
+ // change.
+ table->string_pool.Sort(
+ [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
int diff = a.context.priority - b.context.priority;
if (diff < 0) return true;
if (diff > 0) return false;
@@ -211,192 +219,195 @@
if (diff < 0) return true;
if (diff > 0) return false;
return a.value < b.value;
- });
- table->stringPool.prune();
+ });
+ table->string_pool.Prune();
- auto pbTable = util::make_unique<pb::ResourceTable>();
- serializeStringPoolToPb(table->stringPool, pbTable->mutable_string_pool());
+ auto pb_table = util::make_unique<pb::ResourceTable>();
+ SerializeStringPoolToPb(table->string_pool, pb_table->mutable_string_pool());
- StringPool sourcePool, symbolPool;
+ StringPool source_pool, symbol_pool;
- for (auto& package : table->packages) {
- pb::Package* pbPackage = pbTable->add_packages();
- if (package->id) {
- pbPackage->set_package_id(package->id.value());
+ for (auto& package : table->packages) {
+ pb::Package* pb_package = pb_table->add_packages();
+ if (package->id) {
+ pb_package->set_package_id(package->id.value());
+ }
+ pb_package->set_package_name(package->name);
+
+ for (auto& type : package->types) {
+ pb::Type* pb_type = pb_package->add_types();
+ if (type->id) {
+ pb_type->set_id(type->id.value());
+ }
+ pb_type->set_name(ToString(type->type).ToString());
+
+ for (auto& entry : type->entries) {
+ pb::Entry* pb_entry = pb_type->add_entries();
+ if (entry->id) {
+ pb_entry->set_id(entry->id.value());
}
- pbPackage->set_package_name(package->name);
+ pb_entry->set_name(entry->name);
- for (auto& type : package->types) {
- pb::Type* pbType = pbPackage->add_types();
- if (type->id) {
- pbType->set_id(type->id.value());
- }
- pbType->set_name(toString(type->type).toString());
+ // Write the SymbolStatus struct.
+ pb::SymbolStatus* pb_status = pb_entry->mutable_symbol_status();
+ pb_status->set_visibility(
+ SerializeVisibilityToPb(entry->symbol_status.state));
+ SerializeSourceToPb(entry->symbol_status.source, &source_pool,
+ pb_status->mutable_source());
+ pb_status->set_comment(entry->symbol_status.comment);
- for (auto& entry : type->entries) {
- pb::Entry* pbEntry = pbType->add_entries();
- if (entry->id) {
- pbEntry->set_id(entry->id.value());
- }
- pbEntry->set_name(entry->name);
+ for (auto& config_value : entry->values) {
+ pb::ConfigValue* pb_config_value = pb_entry->add_config_values();
+ SerializeConfig(config_value->config,
+ pb_config_value->mutable_config());
+ if (!config_value->product.empty()) {
+ pb_config_value->mutable_config()->set_product(
+ config_value->product);
+ }
- // Write the SymbolStatus struct.
- pb::SymbolStatus* pbStatus = pbEntry->mutable_symbol_status();
- pbStatus->set_visibility(serializeVisibilityToPb(entry->symbolStatus.state));
- serializeSourceToPb(entry->symbolStatus.source, &sourcePool,
- pbStatus->mutable_source());
- pbStatus->set_comment(entry->symbolStatus.comment);
+ pb::Value* pb_value = pb_config_value->mutable_value();
+ SerializeSourceToPb(config_value->value->GetSource(), &source_pool,
+ pb_value->mutable_source());
+ if (!config_value->value->GetComment().empty()) {
+ pb_value->set_comment(config_value->value->GetComment());
+ }
- for (auto& configValue : entry->values) {
- pb::ConfigValue* pbConfigValue = pbEntry->add_config_values();
- serializeConfig(configValue->config, pbConfigValue->mutable_config());
- if (!configValue->product.empty()) {
- pbConfigValue->mutable_config()->set_product(configValue->product);
- }
+ if (config_value->value->IsWeak()) {
+ pb_value->set_weak(true);
+ }
- pb::Value* pbValue = pbConfigValue->mutable_value();
- serializeSourceToPb(configValue->value->getSource(), &sourcePool,
- pbValue->mutable_source());
- if (!configValue->value->getComment().empty()) {
- pbValue->set_comment(configValue->value->getComment());
- }
-
- if (configValue->value->isWeak()) {
- pbValue->set_weak(true);
- }
-
- PbSerializerVisitor visitor(&sourcePool, &symbolPool, pbValue);
- configValue->value->accept(&visitor);
- }
- }
+ PbSerializerVisitor visitor(&source_pool, &symbol_pool, pb_value);
+ config_value->value->Accept(&visitor);
}
+ }
}
+ }
- serializeStringPoolToPb(sourcePool, pbTable->mutable_source_pool());
- serializeStringPoolToPb(symbolPool, pbTable->mutable_symbol_pool());
- return pbTable;
+ SerializeStringPoolToPb(source_pool, pb_table->mutable_source_pool());
+ SerializeStringPoolToPb(symbol_pool, pb_table->mutable_symbol_pool());
+ return pb_table;
}
-std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(const ResourceFile& file) {
- auto pbFile = util::make_unique<pb::CompiledFile>();
- pbFile->set_resource_name(file.name.toString());
- pbFile->set_source_path(file.source.path);
- serializeConfig(file.config, pbFile->mutable_config());
+std::unique_ptr<pb::CompiledFile> SerializeCompiledFileToPb(
+ const ResourceFile& file) {
+ auto pb_file = util::make_unique<pb::CompiledFile>();
+ pb_file->set_resource_name(file.name.ToString());
+ pb_file->set_source_path(file.source.path);
+ SerializeConfig(file.config, pb_file->mutable_config());
- for (const SourcedResourceName& exported : file.exportedSymbols) {
- pb::CompiledFile_Symbol* pbSymbol = pbFile->add_exported_symbols();
- pbSymbol->set_resource_name(exported.name.toString());
- pbSymbol->set_line_no(exported.line);
- }
- return pbFile;
+ for (const SourcedResourceName& exported : file.exported_symbols) {
+ pb::CompiledFile_Symbol* pb_symbol = pb_file->add_exported_symbols();
+ pb_symbol->set_resource_name(exported.name.ToString());
+ pb_symbol->set_line_no(exported.line);
+ }
+ return pb_file;
}
-CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out) : mOut(out) {
-}
+CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out)
+ : out_(out) {}
-void CompiledFileOutputStream::ensureAlignedWrite() {
- const int padding = mOut.ByteCount() % 4;
- if (padding > 0) {
- uint32_t zero = 0u;
- mOut.WriteRaw(&zero, padding);
- }
+void CompiledFileOutputStream::EnsureAlignedWrite() {
+ const int padding = out_.ByteCount() % 4;
+ if (padding > 0) {
+ uint32_t zero = 0u;
+ out_.WriteRaw(&zero, padding);
+ }
}
void CompiledFileOutputStream::WriteLittleEndian32(uint32_t val) {
- ensureAlignedWrite();
- mOut.WriteLittleEndian32(val);
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian32(val);
}
-void CompiledFileOutputStream::WriteCompiledFile(const pb::CompiledFile* compiledFile) {
- ensureAlignedWrite();
- mOut.WriteLittleEndian64(static_cast<uint64_t>(compiledFile->ByteSize()));
- compiledFile->SerializeWithCachedSizes(&mOut);
+void CompiledFileOutputStream::WriteCompiledFile(
+ const pb::CompiledFile* compiled_file) {
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian64(static_cast<uint64_t>(compiled_file->ByteSize()));
+ compiled_file->SerializeWithCachedSizes(&out_);
}
void CompiledFileOutputStream::WriteData(const BigBuffer* buffer) {
- ensureAlignedWrite();
- mOut.WriteLittleEndian64(static_cast<uint64_t>(buffer->size()));
- for (const BigBuffer::Block& block : *buffer) {
- mOut.WriteRaw(block.buffer.get(), block.size);
- }
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian64(static_cast<uint64_t>(buffer->size()));
+ for (const BigBuffer::Block& block : *buffer) {
+ out_.WriteRaw(block.buffer.get(), block.size);
+ }
}
void CompiledFileOutputStream::WriteData(const void* data, size_t len) {
- ensureAlignedWrite();
- mOut.WriteLittleEndian64(static_cast<uint64_t>(len));
- mOut.WriteRaw(data, len);
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian64(static_cast<uint64_t>(len));
+ out_.WriteRaw(data, len);
}
-bool CompiledFileOutputStream::HadError() {
- return mOut.HadError();
+bool CompiledFileOutputStream::HadError() { return out_.HadError(); }
+
+CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size)
+ : in_(static_cast<const uint8_t*>(data), size) {}
+
+void CompiledFileInputStream::EnsureAlignedRead() {
+ const int padding = in_.CurrentPosition() % 4;
+ if (padding > 0) {
+ // Reads are always 4 byte aligned.
+ in_.Skip(padding);
+ }
}
-CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size) :
- mIn(static_cast<const uint8_t*>(data), size) {
+bool CompiledFileInputStream::ReadLittleEndian32(uint32_t* out_val) {
+ EnsureAlignedRead();
+ return in_.ReadLittleEndian32(out_val);
}
-void CompiledFileInputStream::ensureAlignedRead() {
- const int padding = mIn.CurrentPosition() % 4;
- if (padding > 0) {
- // Reads are always 4 byte aligned.
- mIn.Skip(padding);
- }
+bool CompiledFileInputStream::ReadCompiledFile(pb::CompiledFile* out_val) {
+ EnsureAlignedRead();
+
+ uint64_t pb_size = 0u;
+ if (!in_.ReadLittleEndian64(&pb_size)) {
+ return false;
+ }
+
+ CodedInputStream::Limit l = in_.PushLimit(static_cast<int>(pb_size));
+
+ // Check that we haven't tried to read past the end.
+ if (static_cast<uint64_t>(in_.BytesUntilLimit()) != pb_size) {
+ in_.PopLimit(l);
+ in_.PushLimit(0);
+ return false;
+ }
+
+ if (!out_val->ParsePartialFromCodedStream(&in_)) {
+ in_.PopLimit(l);
+ in_.PushLimit(0);
+ return false;
+ }
+
+ in_.PopLimit(l);
+ return true;
}
-bool CompiledFileInputStream::ReadLittleEndian32(uint32_t* outVal) {
- ensureAlignedRead();
- return mIn.ReadLittleEndian32(outVal);
+bool CompiledFileInputStream::ReadDataMetaData(uint64_t* out_offset,
+ uint64_t* out_len) {
+ EnsureAlignedRead();
+
+ uint64_t pb_size = 0u;
+ if (!in_.ReadLittleEndian64(&pb_size)) {
+ return false;
+ }
+
+ // Check that we aren't trying to read past the end.
+ if (pb_size > static_cast<uint64_t>(in_.BytesUntilLimit())) {
+ in_.PushLimit(0);
+ return false;
+ }
+
+ uint64_t offset = static_cast<uint64_t>(in_.CurrentPosition());
+ if (!in_.Skip(pb_size)) {
+ return false;
+ }
+
+ *out_offset = offset;
+ *out_len = pb_size;
+ return true;
}
-bool CompiledFileInputStream::ReadCompiledFile(pb::CompiledFile* outVal) {
- ensureAlignedRead();
-
- uint64_t pbSize = 0u;
- if (!mIn.ReadLittleEndian64(&pbSize)) {
- return false;
- }
-
- CodedInputStream::Limit l = mIn.PushLimit(static_cast<int>(pbSize));
-
- // Check that we haven't tried to read past the end.
- if (static_cast<uint64_t>(mIn.BytesUntilLimit()) != pbSize) {
- mIn.PopLimit(l);
- mIn.PushLimit(0);
- return false;
- }
-
- if (!outVal->ParsePartialFromCodedStream(&mIn)) {
- mIn.PopLimit(l);
- mIn.PushLimit(0);
- return false;
- }
-
- mIn.PopLimit(l);
- return true;
-}
-
-bool CompiledFileInputStream::ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen) {
- ensureAlignedRead();
-
- uint64_t pbSize = 0u;
- if (!mIn.ReadLittleEndian64(&pbSize)) {
- return false;
- }
-
- // Check that we aren't trying to read past the end.
- if (pbSize > static_cast<uint64_t>(mIn.BytesUntilLimit())) {
- mIn.PushLimit(0);
- return false;
- }
-
- uint64_t offset = static_cast<uint64_t>(mIn.CurrentPosition());
- if (!mIn.Skip(pbSize)) {
- return false;
- }
-
- *outOffset = offset;
- *outLen = pbSize;
- return true;
-}
-
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
index 2bd9767..fdd5197 100644
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -14,200 +14,211 @@
* limitations under the License.
*/
-#include "ResourceTable.h"
#include "proto/ProtoSerialize.h"
+
+#include "ResourceTable.h"
#include "test/Test.h"
-using namespace google::protobuf::io;
+using ::google::protobuf::io::StringOutputStream;
namespace aapt {
TEST(TableProtoSerializer, SerializeSinglePackage) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.a", 0x7f)
- .addFileReference("com.app.a:layout/main", ResourceId(0x7f020000),
- "res/layout/main.xml")
- .addReference("com.app.a:layout/other", ResourceId(0x7f020001),
- "com.app.a:layout/main")
- .addString("com.app.a:string/text", {}, "hi")
- .addValue("com.app.a:id/foo", {}, util::make_unique<Id>())
- .build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .AddFileReference("com.app.a:layout/main", ResourceId(0x7f020000),
+ "res/layout/main.xml")
+ .AddReference("com.app.a:layout/other", ResourceId(0x7f020001),
+ "com.app.a:layout/main")
+ .AddString("com.app.a:string/text", {}, "hi")
+ .AddValue("com.app.a:id/foo", {}, util::make_unique<Id>())
+ .Build();
- Symbol publicSymbol;
- publicSymbol.state = SymbolState::kPublic;
- ASSERT_TRUE(table->setSymbolState(test::parseNameOrDie("com.app.a:layout/main"),
- ResourceId(0x7f020000),
- publicSymbol, context->getDiagnostics()));
+ Symbol public_symbol;
+ public_symbol.state = SymbolState::kPublic;
+ ASSERT_TRUE(table->SetSymbolState(
+ test::ParseNameOrDie("com.app.a:layout/main"), ResourceId(0x7f020000),
+ public_symbol, context->GetDiagnostics()));
- Id* id = test::getValue<Id>(table.get(), "com.app.a:id/foo");
- ASSERT_NE(nullptr, id);
+ Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo");
+ ASSERT_NE(nullptr, id);
- // Make a plural.
- std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- plural->values[Plural::One] = util::make_unique<String>(table->stringPool.makeRef("one"));
- ASSERT_TRUE(table->addResource(test::parseNameOrDie("com.app.a:plurals/hey"),
- ConfigDescription{}, {}, std::move(plural),
- context->getDiagnostics()));
+ // Make a plural.
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ plural->values[Plural::One] =
+ util::make_unique<String>(table->string_pool.MakeRef("one"));
+ ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:plurals/hey"),
+ ConfigDescription{}, {}, std::move(plural),
+ context->GetDiagnostics()));
- // Make a resource with different products.
- ASSERT_TRUE(table->addResource(test::parseNameOrDie("com.app.a:integer/one"),
- test::parseConfigOrDie("land"), {},
- test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 123u),
- context->getDiagnostics()));
- ASSERT_TRUE(table->addResource(test::parseNameOrDie("com.app.a:integer/one"),
- test::parseConfigOrDie("land"), "tablet",
- test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 321u),
- context->getDiagnostics()));
+ // Make a resource with different products.
+ ASSERT_TRUE(table->AddResource(
+ test::ParseNameOrDie("com.app.a:integer/one"),
+ test::ParseConfigOrDie("land"), {},
+ test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 123u),
+ context->GetDiagnostics()));
+ ASSERT_TRUE(table->AddResource(
+ test::ParseNameOrDie("com.app.a:integer/one"),
+ test::ParseConfigOrDie("land"), "tablet",
+ test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 321u),
+ context->GetDiagnostics()));
- // Make a reference with both resource name and resource ID.
- // The reference should point to a resource outside of this table to test that both
- // name and id get serialized.
- Reference expectedRef;
- expectedRef.name = test::parseNameOrDie("android:layout/main");
- expectedRef.id = ResourceId(0x01020000);
- ASSERT_TRUE(table->addResource(test::parseNameOrDie("com.app.a:layout/abc"),
- ConfigDescription::defaultConfig(), {},
- util::make_unique<Reference>(expectedRef),
- context->getDiagnostics()));
+ // Make a reference with both resource name and resource ID.
+ // The reference should point to a resource outside of this table to test that
+ // both
+ // name and id get serialized.
+ Reference expected_ref;
+ expected_ref.name = test::ParseNameOrDie("android:layout/main");
+ expected_ref.id = ResourceId(0x01020000);
+ ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:layout/abc"),
+ ConfigDescription::DefaultConfig(), {},
+ util::make_unique<Reference>(expected_ref),
+ context->GetDiagnostics()));
- std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table.get());
- ASSERT_NE(nullptr, pbTable);
+ std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table.get());
+ ASSERT_NE(nullptr, pb_table);
- std::unique_ptr<ResourceTable> newTable = deserializeTableFromPb(*pbTable,
- Source{ "test" },
- context->getDiagnostics());
- ASSERT_NE(nullptr, newTable);
+ std::unique_ptr<ResourceTable> new_table = DeserializeTableFromPb(
+ *pb_table, Source{"test"}, context->GetDiagnostics());
+ ASSERT_NE(nullptr, new_table);
- Id* newId = test::getValue<Id>(newTable.get(), "com.app.a:id/foo");
- ASSERT_NE(nullptr, newId);
- EXPECT_EQ(id->isWeak(), newId->isWeak());
+ Id* new_id = test::GetValue<Id>(new_table.get(), "com.app.a:id/foo");
+ ASSERT_NE(nullptr, new_id);
+ EXPECT_EQ(id->IsWeak(), new_id->IsWeak());
- Maybe<ResourceTable::SearchResult> result = newTable->findResource(
- test::parseNameOrDie("com.app.a:layout/main"));
- AAPT_ASSERT_TRUE(result);
- EXPECT_EQ(SymbolState::kPublic, result.value().type->symbolStatus.state);
- EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
+ Maybe<ResourceTable::SearchResult> result =
+ new_table->FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
+ AAPT_ASSERT_TRUE(result);
+ EXPECT_EQ(SymbolState::kPublic, result.value().type->symbol_status.state);
+ EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state);
- // Find the product-dependent values
- BinaryPrimitive* prim = test::getValueForConfigAndProduct<BinaryPrimitive>(
- newTable.get(), "com.app.a:integer/one", test::parseConfigOrDie("land"), "");
- ASSERT_NE(nullptr, prim);
- EXPECT_EQ(123u, prim->value.data);
+ // Find the product-dependent values
+ BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
+ new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"),
+ "");
+ ASSERT_NE(nullptr, prim);
+ EXPECT_EQ(123u, prim->value.data);
- prim = test::getValueForConfigAndProduct<BinaryPrimitive>(
- newTable.get(), "com.app.a:integer/one", test::parseConfigOrDie("land"), "tablet");
- ASSERT_NE(nullptr, prim);
- EXPECT_EQ(321u, prim->value.data);
+ prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
+ new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"),
+ "tablet");
+ ASSERT_NE(nullptr, prim);
+ EXPECT_EQ(321u, prim->value.data);
- Reference* actualRef = test::getValue<Reference>(newTable.get(), "com.app.a:layout/abc");
- ASSERT_NE(nullptr, actualRef);
- AAPT_ASSERT_TRUE(actualRef->name);
- AAPT_ASSERT_TRUE(actualRef->id);
- EXPECT_EQ(expectedRef.name.value(), actualRef->name.value());
- EXPECT_EQ(expectedRef.id.value(), actualRef->id.value());
+ Reference* actual_ref =
+ test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc");
+ ASSERT_NE(nullptr, actual_ref);
+ AAPT_ASSERT_TRUE(actual_ref->name);
+ AAPT_ASSERT_TRUE(actual_ref->id);
+ EXPECT_EQ(expected_ref.name.value(), actual_ref->name.value());
+ EXPECT_EQ(expected_ref.id.value(), actual_ref->id.value());
}
TEST(TableProtoSerializer, SerializeFileHeader) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- ResourceFile f;
- f.config = test::parseConfigOrDie("hdpi-v9");
- f.name = test::parseNameOrDie("com.app.a:layout/main");
- f.source.path = "res/layout-hdpi-v9/main.xml";
- f.exportedSymbols.push_back(SourcedResourceName{ test::parseNameOrDie("id/unchecked"), 23u });
+ ResourceFile f;
+ f.config = test::ParseConfigOrDie("hdpi-v9");
+ f.name = test::ParseNameOrDie("com.app.a:layout/main");
+ f.source.path = "res/layout-hdpi-v9/main.xml";
+ f.exported_symbols.push_back(
+ SourcedResourceName{test::ParseNameOrDie("id/unchecked"), 23u});
- const std::string expectedData1 = "123";
- const std::string expectedData2 = "1234";
+ const std::string expected_data1 = "123";
+ const std::string expected_data2 = "1234";
- std::string outputStr;
- {
- std::unique_ptr<pb::CompiledFile> pbFile1 = serializeCompiledFileToPb(f);
+ std::string output_str;
+ {
+ std::unique_ptr<pb::CompiledFile> pb_file1 = SerializeCompiledFileToPb(f);
- f.name.entry = "__" + f.name.entry + "$0";
- std::unique_ptr<pb::CompiledFile> pbFile2 = serializeCompiledFileToPb(f);
+ f.name.entry = "__" + f.name.entry + "$0";
+ std::unique_ptr<pb::CompiledFile> pb_file2 = SerializeCompiledFileToPb(f);
- StringOutputStream outStream(&outputStr);
- CompiledFileOutputStream outFileStream(&outStream);
- outFileStream.WriteLittleEndian32(2);
- outFileStream.WriteCompiledFile(pbFile1.get());
- outFileStream.WriteData(expectedData1.data(), expectedData1.size());
- outFileStream.WriteCompiledFile(pbFile2.get());
- outFileStream.WriteData(expectedData2.data(), expectedData2.size());
- ASSERT_FALSE(outFileStream.HadError());
- }
+ StringOutputStream out_stream(&output_str);
+ CompiledFileOutputStream out_file_stream(&out_stream);
+ out_file_stream.WriteLittleEndian32(2);
+ out_file_stream.WriteCompiledFile(pb_file1.get());
+ out_file_stream.WriteData(expected_data1.data(), expected_data1.size());
+ out_file_stream.WriteCompiledFile(pb_file2.get());
+ out_file_stream.WriteData(expected_data2.data(), expected_data2.size());
+ ASSERT_FALSE(out_file_stream.HadError());
+ }
- CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size());
- uint32_t numFiles = 0;
- ASSERT_TRUE(inFileStream.ReadLittleEndian32(&numFiles));
- ASSERT_EQ(2u, numFiles);
+ CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
+ uint32_t num_files = 0;
+ ASSERT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
+ ASSERT_EQ(2u, num_files);
- // Read the first compiled file.
+ // Read the first compiled file.
- pb::CompiledFile newPbFile;
- ASSERT_TRUE(inFileStream.ReadCompiledFile(&newPbFile));
+ pb::CompiledFile new_pb_file;
+ ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_file));
- std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(newPbFile, Source("test"),
- context->getDiagnostics());
- ASSERT_NE(nullptr, file);
+ std::unique_ptr<ResourceFile> file = DeserializeCompiledFileFromPb(
+ new_pb_file, Source("test"), context->GetDiagnostics());
+ ASSERT_NE(nullptr, file);
- uint64_t offset, len;
- ASSERT_TRUE(inFileStream.ReadDataMetaData(&offset, &len));
+ uint64_t offset, len;
+ ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
- std::string actualData(outputStr.data() + offset, len);
- EXPECT_EQ(expectedData1, actualData);
+ std::string actual_data(output_str.data() + offset, len);
+ EXPECT_EQ(expected_data1, actual_data);
- // Expect the data to be aligned.
- EXPECT_EQ(0u, offset & 0x03);
+ // Expect the data to be aligned.
+ EXPECT_EQ(0u, offset & 0x03);
- ASSERT_EQ(1u, file->exportedSymbols.size());
- EXPECT_EQ(test::parseNameOrDie("id/unchecked"), file->exportedSymbols[0].name);
+ ASSERT_EQ(1u, file->exported_symbols.size());
+ EXPECT_EQ(test::ParseNameOrDie("id/unchecked"),
+ file->exported_symbols[0].name);
- // Read the second compiled file.
+ // Read the second compiled file.
- ASSERT_TRUE(inFileStream.ReadCompiledFile(&newPbFile));
+ ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_file));
- file = deserializeCompiledFileFromPb(newPbFile, Source("test"), context->getDiagnostics());
- ASSERT_NE(nullptr, file);
+ file = DeserializeCompiledFileFromPb(new_pb_file, Source("test"),
+ context->GetDiagnostics());
+ ASSERT_NE(nullptr, file);
- ASSERT_TRUE(inFileStream.ReadDataMetaData(&offset, &len));
+ ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
- actualData = std::string(outputStr.data() + offset, len);
- EXPECT_EQ(expectedData2, actualData);
+ actual_data = std::string(output_str.data() + offset, len);
+ EXPECT_EQ(expected_data2, actual_data);
- // Expect the data to be aligned.
- EXPECT_EQ(0u, offset & 0x03);
+ // Expect the data to be aligned.
+ EXPECT_EQ(0u, offset & 0x03);
}
TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) {
- ResourceFile f;
- std::unique_ptr<pb::CompiledFile> pbFile = serializeCompiledFileToPb(f);
+ ResourceFile f;
+ std::unique_ptr<pb::CompiledFile> pb_file = SerializeCompiledFileToPb(f);
- const std::string expectedData = "1234";
+ const std::string expected_data = "1234";
- std::string outputStr;
- {
- StringOutputStream outStream(&outputStr);
- CompiledFileOutputStream outFileStream(&outStream);
- outFileStream.WriteLittleEndian32(1);
- outFileStream.WriteCompiledFile(pbFile.get());
- outFileStream.WriteData(expectedData.data(), expectedData.size());
- ASSERT_FALSE(outFileStream.HadError());
- }
+ std::string output_str;
+ {
+ StringOutputStream out_stream(&output_str);
+ CompiledFileOutputStream out_file_stream(&out_stream);
+ out_file_stream.WriteLittleEndian32(1);
+ out_file_stream.WriteCompiledFile(pb_file.get());
+ out_file_stream.WriteData(expected_data.data(), expected_data.size());
+ ASSERT_FALSE(out_file_stream.HadError());
+ }
- outputStr[4] = 0xff;
+ output_str[4] = 0xff;
- CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size());
+ CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
- uint32_t numFiles = 0;
- EXPECT_TRUE(inFileStream.ReadLittleEndian32(&numFiles));
- EXPECT_EQ(1u, numFiles);
+ uint32_t num_files = 0;
+ EXPECT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
+ EXPECT_EQ(1u, num_files);
- pb::CompiledFile newPbFile;
- EXPECT_FALSE(inFileStream.ReadCompiledFile(&newPbFile));
+ pb::CompiledFile new_pb_file;
+ EXPECT_FALSE(in_file_stream.ReadCompiledFile(&new_pb_file));
- uint64_t offset, len;
- EXPECT_FALSE(inFileStream.ReadDataMetaData(&offset, &len));
+ uint64_t offset, len;
+ EXPECT_FALSE(in_file_stream.ReadDataMetaData(&offset, &len));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 08b9ee9..7aad86f 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -14,251 +14,278 @@
* limitations under the License.
*/
-#include "ConfigDescription.h"
-#include "ResourceTable.h"
#include "split/TableSplitter.h"
-#include "util/Util.h"
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <vector>
+#include "android-base/logging.h"
+
+#include "ConfigDescription.h"
+#include "ResourceTable.h"
+#include "util/Util.h"
namespace aapt {
using ConfigClaimedMap = std::unordered_map<ResourceConfigValue*, bool>;
-using ConfigDensityGroups = std::map<ConfigDescription, std::vector<ResourceConfigValue*>>;
+using ConfigDensityGroups =
+ std::map<ConfigDescription, std::vector<ResourceConfigValue*>>;
-static ConfigDescription copyWithoutDensity(const ConfigDescription& config) {
- ConfigDescription withoutDensity = config;
- withoutDensity.density = 0;
- return withoutDensity;
+static ConfigDescription CopyWithoutDensity(const ConfigDescription& config) {
+ ConfigDescription without_density = config;
+ without_density.density = 0;
+ return without_density;
}
/**
* Selects values that match exactly the constraints given.
*/
class SplitValueSelector {
-public:
- explicit SplitValueSelector(const SplitConstraints& constraints) {
- for (const ConfigDescription& config : constraints.configs) {
- if (config.density == 0) {
- mDensityIndependentConfigs.insert(config);
- } else {
- mDensityDependentConfigToDensityMap[copyWithoutDensity(config)] = config.density;
- }
+ public:
+ explicit SplitValueSelector(const SplitConstraints& constraints) {
+ for (const ConfigDescription& config : constraints.configs) {
+ if (config.density == 0) {
+ density_independent_configs_.insert(config);
+ } else {
+ density_dependent_config_to_density_map_[CopyWithoutDensity(config)] =
+ config.density;
+ }
+ }
+ }
+
+ std::vector<ResourceConfigValue*> SelectValues(
+ const ConfigDensityGroups& density_groups,
+ ConfigClaimedMap* claimed_values) {
+ std::vector<ResourceConfigValue*> selected;
+
+ // Select the regular values.
+ for (auto& entry : *claimed_values) {
+ // Check if the entry has a density.
+ ResourceConfigValue* config_value = entry.first;
+ if (config_value->config.density == 0 && !entry.second) {
+ // This is still available.
+ if (density_independent_configs_.find(config_value->config) !=
+ density_independent_configs_.end()) {
+ selected.push_back(config_value);
+
+ // Mark the entry as taken.
+ entry.second = true;
}
+ }
}
- std::vector<ResourceConfigValue*> selectValues(const ConfigDensityGroups& densityGroups,
- ConfigClaimedMap* claimedValues) {
- std::vector<ResourceConfigValue*> selected;
+ // Now examine the densities
+ for (auto& entry : density_groups) {
+ // We do not care if the value is claimed, since density values can be
+ // in multiple splits.
+ const ConfigDescription& config = entry.first;
+ const std::vector<ResourceConfigValue*>& related_values = entry.second;
+ auto density_value_iter =
+ density_dependent_config_to_density_map_.find(config);
+ if (density_value_iter !=
+ density_dependent_config_to_density_map_.end()) {
+ // Select the best one!
+ ConfigDescription target_density = config;
+ target_density.density = density_value_iter->second;
- // Select the regular values.
- for (auto& entry : *claimedValues) {
- // Check if the entry has a density.
- ResourceConfigValue* configValue = entry.first;
- if (configValue->config.density == 0 && !entry.second) {
- // This is still available.
- if (mDensityIndependentConfigs.find(configValue->config) !=
- mDensityIndependentConfigs.end()) {
- selected.push_back(configValue);
-
- // Mark the entry as taken.
- entry.second = true;
- }
- }
+ ResourceConfigValue* best_value = nullptr;
+ for (ResourceConfigValue* this_value : related_values) {
+ if (!best_value ||
+ this_value->config.isBetterThan(best_value->config,
+ &target_density)) {
+ best_value = this_value;
+ }
}
+ CHECK(best_value != nullptr);
- // Now examine the densities
- for (auto& entry : densityGroups) {
- // We do not care if the value is claimed, since density values can be
- // 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!
- ConfigDescription targetDensity = config;
- targetDensity.density = densityValueIter->second;
-
- ResourceConfigValue* bestValue = nullptr;
- for (ResourceConfigValue* thisValue : relatedValues) {
- if (!bestValue ||
- thisValue->config.isBetterThan(bestValue->config, &targetDensity)) {
- bestValue = thisValue;
- }
- }
- 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);
- }
- }
- return selected;
+ // When we select one of these, they are all claimed such that the base
+ // doesn't include any anymore.
+ (*claimed_values)[best_value] = true;
+ selected.push_back(best_value);
+ }
}
+ return selected;
+ }
-private:
- std::set<ConfigDescription> mDensityIndependentConfigs;
- std::map<ConfigDescription, uint16_t> mDensityDependentConfigToDensityMap;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SplitValueSelector);
+
+ std::set<ConfigDescription> density_independent_configs_;
+ std::map<ConfigDescription, uint16_t>
+ density_dependent_config_to_density_map_;
};
/**
- * Marking non-preferred densities as claimed will make sure the base doesn't include them,
+ * Marking non-preferred densities as claimed will make sure the base doesn't
+ * include them,
* leaving only the preferred density behind.
*/
-static void markNonPreferredDensitiesAsClaimed(uint16_t preferredDensity,
- const ConfigDensityGroups& densityGroups,
- ConfigClaimedMap* configClaimedMap) {
- for (auto& entry : densityGroups) {
- const ConfigDescription& config = entry.first;
- const std::vector<ResourceConfigValue*>& relatedValues = entry.second;
+static void MarkNonPreferredDensitiesAsClaimed(
+ uint16_t preferred_density, const ConfigDensityGroups& density_groups,
+ ConfigClaimedMap* config_claimed_map) {
+ for (auto& entry : density_groups) {
+ const ConfigDescription& config = entry.first;
+ const std::vector<ResourceConfigValue*>& related_values = entry.second;
- ConfigDescription targetDensity = config;
- targetDensity.density = preferredDensity;
- ResourceConfigValue* bestValue = nullptr;
- for (ResourceConfigValue* thisValue : relatedValues) {
- if (!bestValue) {
- bestValue = thisValue;
- } else if (thisValue->config.isBetterThan(bestValue->config, &targetDensity)) {
- // Claim the previous value so that it is not included in the base.
- (*configClaimedMap)[bestValue] = true;
- bestValue = thisValue;
- } else {
- // Claim this value so that it is not included in the base.
- (*configClaimedMap)[thisValue] = true;
- }
- }
- assert(bestValue);
+ ConfigDescription target_density = config;
+ target_density.density = preferred_density;
+ ResourceConfigValue* best_value = nullptr;
+ for (ResourceConfigValue* this_value : related_values) {
+ if (!best_value) {
+ best_value = this_value;
+ } else if (this_value->config.isBetterThan(best_value->config,
+ &target_density)) {
+ // Claim the previous value so that it is not included in the base.
+ (*config_claimed_map)[best_value] = true;
+ best_value = this_value;
+ } else {
+ // Claim this value so that it is not included in the base.
+ (*config_claimed_map)[this_value] = true;
+ }
}
+ CHECK(best_value != nullptr);
+ }
}
-bool TableSplitter::verifySplitConstraints(IAaptContext* context) {
- bool error = false;
- for (size_t i = 0; i < mSplitConstraints.size(); i++) {
- for (size_t j = i + 1; j < mSplitConstraints.size(); j++) {
- for (const ConfigDescription& config : mSplitConstraints[i].configs) {
- if (mSplitConstraints[j].configs.find(config) !=
- mSplitConstraints[j].configs.end()) {
- context->getDiagnostics()->error(DiagMessage() << "config '" << config
- << "' appears in multiple splits, "
- << "target split ambiguous");
- error = true;
- }
- }
+bool TableSplitter::VerifySplitConstraints(IAaptContext* context) {
+ bool error = false;
+ for (size_t i = 0; i < split_constraints_.size(); i++) {
+ for (size_t j = i + 1; j < split_constraints_.size(); j++) {
+ for (const ConfigDescription& config : split_constraints_[i].configs) {
+ if (split_constraints_[j].configs.find(config) !=
+ split_constraints_[j].configs.end()) {
+ context->GetDiagnostics()->Error(DiagMessage()
+ << "config '" << config
+ << "' appears in multiple splits, "
+ << "target split ambiguous");
+ error = true;
}
+ }
}
- return !error;
+ }
+ return !error;
}
-void TableSplitter::splitTable(ResourceTable* originalTable) {
- const size_t splitCount = mSplitConstraints.size();
- for (auto& pkg : originalTable->packages) {
- // Initialize all packages for splits.
- for (size_t idx = 0; idx < splitCount; idx++) {
- ResourceTable* splitTable = mSplits[idx].get();
- splitTable->createPackage(pkg->name, pkg->id);
- }
-
- for (auto& type : pkg->types) {
- if (type->type == ResourceType::kMipmap) {
- // Always keep mipmaps.
- continue;
- }
-
- for (auto& entry : type->entries) {
- if (mConfigFilter) {
- // First eliminate any resource that we definitely don't want.
- for (std::unique_ptr<ResourceConfigValue>& configValue : entry->values) {
- if (!mConfigFilter->match(configValue->config)) {
- // null out the entry. We will clean up and remove nulls at the end
- // for performance reasons.
- configValue.reset();
- }
- }
- }
-
- // Organize the values into two separate buckets. Those that are density-dependent
- // and those that are density-independent.
- // One density technically matches all density, it's just that some densities
- // match better. So we need to be aware of the full set of densities to make this
- // decision.
- ConfigDensityGroups densityGroups;
- ConfigClaimedMap configClaimedMap;
- for (const std::unique_ptr<ResourceConfigValue>& configValue : entry->values) {
- if (configValue) {
- configClaimedMap[configValue.get()] = false;
-
- if (configValue->config.density != 0) {
- // Create a bucket for this density-dependent config.
- densityGroups[copyWithoutDensity(configValue->config)]
- .push_back(configValue.get());
- }
- }
- }
-
- // First we check all the splits. If it doesn't match one of the splits, we
- // leave it in the base.
- for (size_t idx = 0; idx < splitCount; idx++) {
- const SplitConstraints& splitConstraint = mSplitConstraints[idx];
- ResourceTable* splitTable = mSplits[idx].get();
-
- // Select the values we want from this entry for this split.
- SplitValueSelector selector(splitConstraint);
- std::vector<ResourceConfigValue*> selectedValues =
- selector.selectValues(densityGroups, &configClaimedMap);
-
- // No need to do any work if we selected nothing.
- if (!selectedValues.empty()) {
- // Create the same resource structure in the split. We do this lazily
- // because we might not have actual values for each type/entry.
- ResourceTablePackage* splitPkg = splitTable->findPackage(pkg->name);
- ResourceTableType* splitType = splitPkg->findOrCreateType(type->type);
- if (!splitType->id) {
- splitType->id = type->id;
- splitType->symbolStatus = type->symbolStatus;
- }
-
- ResourceEntry* splitEntry = splitType->findOrCreateEntry(entry->name);
- if (!splitEntry->id) {
- splitEntry->id = entry->id;
- splitEntry->symbolStatus = entry->symbolStatus;
- }
-
- // Copy the selected values into the new Split Entry.
- for (ResourceConfigValue* configValue : selectedValues) {
- ResourceConfigValue* newConfigValue = splitEntry->findOrCreateValue(
- configValue->config, configValue->product);
- newConfigValue->value = std::unique_ptr<Value>(
- configValue->value->clone(&splitTable->stringPool));
- }
- }
- }
-
- if (mPreferredDensity) {
- markNonPreferredDensitiesAsClaimed(mPreferredDensity.value(),
- densityGroups,
- &configClaimedMap);
- }
-
- // All splits are handled, now check to see what wasn't claimed and remove
- // whatever exists in other splits.
- for (std::unique_ptr<ResourceConfigValue>& configValue : entry->values) {
- if (configValue && configClaimedMap[configValue.get()]) {
- // Claimed, remove from base.
- configValue.reset();
- }
- }
-
- // Now erase all nullptrs.
- entry->values.erase(
- std::remove(entry->values.begin(), entry->values.end(), nullptr),
- entry->values.end());
- }
- }
+void TableSplitter::SplitTable(ResourceTable* original_table) {
+ const size_t split_count = split_constraints_.size();
+ for (auto& pkg : original_table->packages) {
+ // Initialize all packages for splits.
+ for (size_t idx = 0; idx < split_count; idx++) {
+ ResourceTable* split_table = splits_[idx].get();
+ split_table->CreatePackage(pkg->name, pkg->id);
}
+
+ for (auto& type : pkg->types) {
+ if (type->type == ResourceType::kMipmap) {
+ // Always keep mipmaps.
+ continue;
+ }
+
+ for (auto& entry : type->entries) {
+ if (options_.config_filter) {
+ // First eliminate any resource that we definitely don't want.
+ for (std::unique_ptr<ResourceConfigValue>& config_value :
+ entry->values) {
+ if (!options_.config_filter->Match(config_value->config)) {
+ // null out the entry. We will clean up and remove nulls at the
+ // end for performance reasons.
+ config_value.reset();
+ }
+ }
+ }
+
+ // Organize the values into two separate buckets. Those that are
+ // density-dependent
+ // and those that are density-independent.
+ // One density technically matches all density, it's just that some
+ // densities
+ // match better. So we need to be aware of the full set of densities to
+ // make this
+ // decision.
+ ConfigDensityGroups density_groups;
+ ConfigClaimedMap config_claimed_map;
+ for (const std::unique_ptr<ResourceConfigValue>& config_value :
+ entry->values) {
+ if (config_value) {
+ config_claimed_map[config_value.get()] = false;
+
+ if (config_value->config.density != 0) {
+ // Create a bucket for this density-dependent config.
+ density_groups[CopyWithoutDensity(config_value->config)]
+ .push_back(config_value.get());
+ }
+ }
+ }
+
+ // First we check all the splits. If it doesn't match one of the splits,
+ // we
+ // leave it in the base.
+ for (size_t idx = 0; idx < split_count; idx++) {
+ const SplitConstraints& split_constraint = split_constraints_[idx];
+ ResourceTable* split_table = splits_[idx].get();
+
+ // Select the values we want from this entry for this split.
+ SplitValueSelector selector(split_constraint);
+ std::vector<ResourceConfigValue*> selected_values =
+ selector.SelectValues(density_groups, &config_claimed_map);
+
+ // No need to do any work if we selected nothing.
+ if (!selected_values.empty()) {
+ // Create the same resource structure in the split. We do this
+ // lazily because we might not have actual values for each
+ // type/entry.
+ ResourceTablePackage* split_pkg =
+ split_table->FindPackage(pkg->name);
+ ResourceTableType* split_type =
+ split_pkg->FindOrCreateType(type->type);
+ if (!split_type->id) {
+ split_type->id = type->id;
+ split_type->symbol_status = type->symbol_status;
+ }
+
+ ResourceEntry* split_entry =
+ split_type->FindOrCreateEntry(entry->name);
+ if (!split_entry->id) {
+ split_entry->id = entry->id;
+ split_entry->symbol_status = entry->symbol_status;
+ }
+
+ // Copy the selected values into the new Split Entry.
+ for (ResourceConfigValue* config_value : selected_values) {
+ ResourceConfigValue* new_config_value =
+ split_entry->FindOrCreateValue(config_value->config,
+ config_value->product);
+ new_config_value->value = std::unique_ptr<Value>(
+ config_value->value->Clone(&split_table->string_pool));
+ }
+ }
+ }
+
+ if (options_.preferred_density) {
+ MarkNonPreferredDensitiesAsClaimed(options_.preferred_density.value(),
+ density_groups,
+ &config_claimed_map);
+ }
+
+ // All splits are handled, now check to see what wasn't claimed and
+ // remove
+ // whatever exists in other splits.
+ for (std::unique_ptr<ResourceConfigValue>& config_value :
+ entry->values) {
+ if (config_value && config_claimed_map[config_value.get()]) {
+ // Claimed, remove from base.
+ config_value.reset();
+ }
+ }
+
+ // Now erase all nullptrs.
+ entry->values.erase(
+ std::remove(entry->values.begin(), entry->values.end(), nullptr),
+ entry->values.end());
+ }
+ }
+ }
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/split/TableSplitter.h b/tools/aapt2/split/TableSplitter.h
index 2fa5c47..1ae3271 100644
--- a/tools/aapt2/split/TableSplitter.h
+++ b/tools/aapt2/split/TableSplitter.h
@@ -17,62 +17,58 @@
#ifndef AAPT_SPLIT_TABLESPLITTER_H
#define AAPT_SPLIT_TABLESPLITTER_H
+#include <set>
+#include <vector>
+#include "android-base/macros.h"
+
#include "ConfigDescription.h"
#include "ResourceTable.h"
#include "filter/ConfigFilter.h"
#include "process/IResourceTableConsumer.h"
-#include <android-base/macros.h>
-#include <set>
-#include <vector>
-
namespace aapt {
struct SplitConstraints {
- std::set<ConfigDescription> configs;
+ std::set<ConfigDescription> configs;
};
struct TableSplitterOptions {
- /**
- * The preferred density to keep in the table, stripping out all others.
- */
- Maybe<uint16_t> preferredDensity;
+ /**
+ * The preferred density to keep in the table, stripping out all others.
+ */
+ Maybe<uint16_t> preferred_density;
- /**
- * Configuration filter that determines which resource configuration values end up in
- * the final table.
- */
- IConfigFilter* configFilter = nullptr;
+ /**
+ * Configuration filter that determines which resource configuration values
+ * end up in
+ * the final table.
+ */
+ IConfigFilter* config_filter = nullptr;
};
class TableSplitter {
-public:
- TableSplitter(const std::vector<SplitConstraints>& splits,
- const TableSplitterOptions& options) :
- mSplitConstraints(splits), mPreferredDensity(options.preferredDensity),
- mConfigFilter(options.configFilter) {
- for (size_t i = 0; i < mSplitConstraints.size(); i++) {
- mSplits.push_back(util::make_unique<ResourceTable>());
- }
+ public:
+ TableSplitter(const std::vector<SplitConstraints>& splits,
+ const TableSplitterOptions& options)
+ : split_constraints_(splits), options_(options) {
+ for (size_t i = 0; i < split_constraints_.size(); i++) {
+ splits_.push_back(util::make_unique<ResourceTable>());
}
+ }
- bool verifySplitConstraints(IAaptContext* context);
+ bool VerifySplitConstraints(IAaptContext* context);
- void splitTable(ResourceTable* originalTable);
+ void SplitTable(ResourceTable* original_table);
- std::vector<std::unique_ptr<ResourceTable>>& getSplits() {
- return mSplits;
- }
+ std::vector<std::unique_ptr<ResourceTable>>& splits() { return splits_; }
-private:
- std::vector<SplitConstraints> mSplitConstraints;
- std::vector<std::unique_ptr<ResourceTable>> mSplits;
- Maybe<uint16_t> mPreferredDensity;
- IConfigFilter* mConfigFilter;
+ private:
+ std::vector<SplitConstraints> split_constraints_;
+ std::vector<std::unique_ptr<ResourceTable>> splits_;
+ TableSplitterOptions options_;
- DISALLOW_COPY_AND_ASSIGN(TableSplitter);
+ DISALLOW_COPY_AND_ASSIGN(TableSplitter);
};
-
}
#endif /* AAPT_SPLIT_TABLESPLITTER_H */
diff --git a/tools/aapt2/split/TableSplitter_test.cpp b/tools/aapt2/split/TableSplitter_test.cpp
index 5150e82..088dac3 100644
--- a/tools/aapt2/split/TableSplitter_test.cpp
+++ b/tools/aapt2/split/TableSplitter_test.cpp
@@ -15,155 +15,192 @@
*/
#include "split/TableSplitter.h"
+
#include "test/Test.h"
namespace aapt {
TEST(TableSplitterTest, NoSplitPreferredDensity) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addFileReference("android:drawable/icon", "res/drawable-mdpi/icon.png",
- test::parseConfigOrDie("mdpi"))
- .addFileReference("android:drawable/icon", "res/drawable-hdpi/icon.png",
- test::parseConfigOrDie("hdpi"))
- .addFileReference("android:drawable/icon", "res/drawable-xhdpi/icon.png",
- test::parseConfigOrDie("xhdpi"))
- .addFileReference("android:drawable/icon", "res/drawable-xxhdpi/icon.png",
- test::parseConfigOrDie("xxhdpi"))
- .addSimple("android:string/one")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddFileReference("android:drawable/icon",
+ "res/drawable-mdpi/icon.png",
+ test::ParseConfigOrDie("mdpi"))
+ .AddFileReference("android:drawable/icon",
+ "res/drawable-hdpi/icon.png",
+ test::ParseConfigOrDie("hdpi"))
+ .AddFileReference("android:drawable/icon",
+ "res/drawable-xhdpi/icon.png",
+ test::ParseConfigOrDie("xhdpi"))
+ .AddFileReference("android:drawable/icon",
+ "res/drawable-xxhdpi/icon.png",
+ test::ParseConfigOrDie("xxhdpi"))
+ .AddSimple("android:string/one")
+ .Build();
- TableSplitterOptions options;
- options.preferredDensity = ConfigDescription::DENSITY_XHIGH;
- TableSplitter splitter({}, options);
- splitter.splitTable(table.get());
+ TableSplitterOptions options;
+ options.preferred_density = ConfigDescription::DENSITY_XHIGH;
+ TableSplitter splitter({}, options);
+ splitter.SplitTable(table.get());
- EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(),
- "android:drawable/icon",
- test::parseConfigOrDie("mdpi")));
- EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(),
- "android:drawable/icon",
- test::parseConfigOrDie("hdpi")));
- EXPECT_NE(nullptr, test::getValueForConfig<FileReference>(table.get(),
- "android:drawable/icon",
- test::parseConfigOrDie("xhdpi")));
- EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(),
- "android:drawable/icon",
- test::parseConfigOrDie("xxhdpi")));
- EXPECT_NE(nullptr, test::getValue<Id>(table.get(), "android:string/one"));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>(
+ table.get(), "android:drawable/icon",
+ test::ParseConfigOrDie("mdpi")));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>(
+ table.get(), "android:drawable/icon",
+ test::ParseConfigOrDie("hdpi")));
+ EXPECT_NE(nullptr, test::GetValueForConfig<FileReference>(
+ table.get(), "android:drawable/icon",
+ test::ParseConfigOrDie("xhdpi")));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>(
+ table.get(), "android:drawable/icon",
+ test::ParseConfigOrDie("xxhdpi")));
+ 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::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") } });
+ 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());
+ TableSplitter splitter(constraints, TableSplitterOptions{});
+ splitter.SplitTable(table.get());
- ASSERT_EQ(3u, splitter.getSplits().size());
+ ASSERT_EQ(3u, splitter.splits().size());
- ResourceTable* splitOne = splitter.getSplits()[0].get();
- ResourceTable* splitTwo = splitter.getSplits()[1].get();
- ResourceTable* splitThree = splitter.getSplits()[2].get();
+ ResourceTable* split_one = splitter.splits()[0].get();
+ ResourceTable* split_two = splitter.splits()[1].get();
+ ResourceTable* split_three = splitter.splits()[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")));
+ // 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")));
+ // Each split should have one and only one drawable.
+ EXPECT_NE(nullptr, test::GetValueForConfig<FileReference>(
+ split_one, "android:drawable/foo",
+ test::ParseConfigOrDie("mdpi")));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>(
+ split_one, "android:drawable/foo",
+ test::ParseConfigOrDie("hdpi")));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>(
+ split_one, "android:drawable/foo",
+ test::ParseConfigOrDie("xhdpi")));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>(
+ split_one, "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>(
+ split_two, "android:drawable/foo",
+ test::ParseConfigOrDie("mdpi")));
+ EXPECT_NE(nullptr, test::GetValueForConfig<FileReference>(
+ split_two, "android:drawable/foo",
+ test::ParseConfigOrDie("hdpi")));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>(
+ split_two, "android:drawable/foo",
+ test::ParseConfigOrDie("xhdpi")));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>(
+ split_two, "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")));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>(
+ split_three, "android:drawable/foo",
+ test::ParseConfigOrDie("mdpi")));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>(
+ split_three, "android:drawable/foo",
+ test::ParseConfigOrDie("hdpi")));
+ EXPECT_NE(nullptr, test::GetValueForConfig<FileReference>(
+ split_three, "android:drawable/foo",
+ test::ParseConfigOrDie("xhdpi")));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>(
+ split_three, "android:drawable/foo",
+ test::ParseConfigOrDie("xxhdpi")));
}
TEST(TableSplitterTest, SplitTableByConfigAndDensity) {
- ResourceTable table;
+ ResourceTable table;
- const ResourceName foo = test::parseNameOrDie("android:string/foo");
- ASSERT_TRUE(table.addResource(foo, test::parseConfigOrDie("land-hdpi"), {},
- util::make_unique<Id>(),
- test::getDiagnostics()));
- ASSERT_TRUE(table.addResource(foo, test::parseConfigOrDie("land-xhdpi"), {},
- util::make_unique<Id>(),
- test::getDiagnostics()));
- ASSERT_TRUE(table.addResource(foo, test::parseConfigOrDie("land-xxhdpi"), {},
- util::make_unique<Id>(),
- test::getDiagnostics()));
+ const ResourceName foo = test::ParseNameOrDie("android:string/foo");
+ ASSERT_TRUE(table.AddResource(foo, test::ParseConfigOrDie("land-hdpi"), {},
+ util::make_unique<Id>(),
+ test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(foo, test::ParseConfigOrDie("land-xhdpi"), {},
+ util::make_unique<Id>(),
+ test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(foo, test::ParseConfigOrDie("land-xxhdpi"), {},
+ util::make_unique<Id>(),
+ test::GetDiagnostics()));
- std::vector<SplitConstraints> constraints;
- constraints.push_back(SplitConstraints{ { test::parseConfigOrDie("land-mdpi") } });
- constraints.push_back(SplitConstraints{ { test::parseConfigOrDie("land-xhdpi") } });
+ std::vector<SplitConstraints> constraints;
+ constraints.push_back(
+ SplitConstraints{{test::ParseConfigOrDie("land-mdpi")}});
+ constraints.push_back(
+ SplitConstraints{{test::ParseConfigOrDie("land-xhdpi")}});
- TableSplitter splitter(constraints, TableSplitterOptions{});
- splitter.splitTable(&table);
+ TableSplitter splitter(constraints, TableSplitterOptions{});
+ splitter.SplitTable(&table);
- ASSERT_EQ(2u, splitter.getSplits().size());
+ ASSERT_EQ(2u, splitter.splits().size());
- ResourceTable* splitOne = splitter.getSplits()[0].get();
- ResourceTable* splitTwo = splitter.getSplits()[1].get();
+ ResourceTable* split_one = splitter.splits()[0].get();
+ ResourceTable* split_two = splitter.splits()[1].get();
- // 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_NE(nullptr, test::getValueForConfig<Id>(&table, "android:string/foo",
- test::parseConfigOrDie("land-xxhdpi")));
+ // 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_NE(nullptr,
+ test::GetValueForConfig<Id>(&table, "android:string/foo",
+ test::ParseConfigOrDie("land-xxhdpi")));
- EXPECT_NE(nullptr, test::getValueForConfig<Id>(splitOne, "android:string/foo",
- test::parseConfigOrDie("land-hdpi")));
- EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitOne, "android:string/foo",
- test::parseConfigOrDie("land-xhdpi")));
- EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitOne, "android:string/foo",
- test::parseConfigOrDie("land-xxhdpi")));
+ EXPECT_NE(nullptr,
+ test::GetValueForConfig<Id>(split_one, "android:string/foo",
+ test::ParseConfigOrDie("land-hdpi")));
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfig<Id>(split_one, "android:string/foo",
+ test::ParseConfigOrDie("land-xhdpi")));
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfig<Id>(split_one, "android:string/foo",
+ test::ParseConfigOrDie("land-xxhdpi")));
- EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitTwo, "android:string/foo",
- test::parseConfigOrDie("land-hdpi")));
- EXPECT_NE(nullptr, test::getValueForConfig<Id>(splitTwo, "android:string/foo",
- test::parseConfigOrDie("land-xhdpi")));
- EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitTwo, "android:string/foo",
- test::parseConfigOrDie("land-xxhdpi")));
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfig<Id>(split_two, "android:string/foo",
+ test::ParseConfigOrDie("land-hdpi")));
+ EXPECT_NE(nullptr,
+ test::GetValueForConfig<Id>(split_two, "android:string/foo",
+ test::ParseConfigOrDie("land-xhdpi")));
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfig<Id>(split_two, "android:string/foo",
+ test::ParseConfigOrDie("land-xxhdpi")));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 637e991..9377306 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -17,244 +17,269 @@
#ifndef AAPT_TEST_BUILDERS_H
#define AAPT_TEST_BUILDERS_H
+#include <memory>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "test/Common.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
-#include <memory>
-
namespace aapt {
namespace test {
class ResourceTableBuilder {
-private:
- DummyDiagnosticsImpl mDiagnostics;
- std::unique_ptr<ResourceTable> mTable = util::make_unique<ResourceTable>();
+ public:
+ ResourceTableBuilder() = default;
-public:
- ResourceTableBuilder() = default;
+ StringPool* string_pool() { return &table_->string_pool; }
- StringPool* getStringPool() {
- return &mTable->stringPool;
- }
+ ResourceTableBuilder& SetPackageId(const StringPiece& package_name,
+ uint8_t id) {
+ ResourceTablePackage* package = table_->CreatePackage(package_name, id);
+ CHECK(package != nullptr);
+ return *this;
+ }
- ResourceTableBuilder& setPackageId(const StringPiece& packageName, uint8_t id) {
- ResourceTablePackage* package = mTable->createPackage(packageName, id);
- assert(package);
- return *this;
- }
+ ResourceTableBuilder& AddSimple(const StringPiece& name,
+ const ResourceId& id = {}) {
+ return AddValue(name, id, util::make_unique<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 = {}) {
+ return AddValue(name, config, id, util::make_unique<Id>());
+ }
- ResourceTableBuilder& addSimple(const StringPiece& name, const ConfigDescription& config,
- const ResourceId& id = {}) {
- return addValue(name, config, id, util::make_unique<Id>());
- }
+ ResourceTableBuilder& AddReference(const StringPiece& name,
+ const StringPiece& ref) {
+ return AddReference(name, {}, ref);
+ }
- ResourceTableBuilder& addReference(const StringPiece& name, const StringPiece& ref) {
- return addReference(name, {}, ref);
- }
+ ResourceTableBuilder& AddReference(const StringPiece& name,
+ const ResourceId& id,
+ const StringPiece& ref) {
+ return AddValue(name, id,
+ util::make_unique<Reference>(ParseNameOrDie(ref)));
+ }
- ResourceTableBuilder& addReference(const StringPiece& name, const ResourceId& id,
- const StringPiece& ref) {
- return addValue(name, id, util::make_unique<Reference>(parseNameOrDie(ref)));
- }
+ ResourceTableBuilder& AddString(const StringPiece& name,
+ const StringPiece& str) {
+ return AddString(name, {}, str);
+ }
- ResourceTableBuilder& addString(const StringPiece& name, const StringPiece& str) {
- return addString(name, {}, str);
- }
+ ResourceTableBuilder& AddString(const StringPiece& name, const ResourceId& id,
+ const StringPiece& str) {
+ return AddValue(
+ name, id, util::make_unique<String>(table_->string_pool.MakeRef(str)));
+ }
- 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,
+ const ConfigDescription& config,
+ const StringPiece& str) {
+ return AddValue(name, config, id, util::make_unique<String>(
+ table_->string_pool.MakeRef(str)));
+ }
- 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)));
- }
+ ResourceTableBuilder& AddFileReference(const StringPiece& name,
+ const StringPiece& path) {
+ return AddFileReference(name, {}, path);
+ }
- ResourceTableBuilder& addFileReference(const StringPiece& name, const StringPiece& path) {
- return addFileReference(name, {}, path);
- }
+ ResourceTableBuilder& AddFileReference(const StringPiece& name,
+ const ResourceId& id,
+ const StringPiece& path) {
+ return AddValue(name, id, util::make_unique<FileReference>(
+ table_->string_pool.MakeRef(path)));
+ }
- ResourceTableBuilder& addFileReference(const StringPiece& name, const ResourceId& id,
- const StringPiece& path) {
- return addValue(name, id,
- util::make_unique<FileReference>(mTable->stringPool.makeRef(path)));
- }
+ ResourceTableBuilder& AddFileReference(const StringPiece& name,
+ const StringPiece& path,
+ const ConfigDescription& config) {
+ return AddValue(name, config, {}, util::make_unique<FileReference>(
+ table_->string_pool.MakeRef(path)));
+ }
- ResourceTableBuilder& addFileReference(const StringPiece& name, const StringPiece& path,
- const ConfigDescription& config) {
- return addValue(name, config, {},
- util::make_unique<FileReference>(mTable->stringPool.makeRef(path)));
- }
+ ResourceTableBuilder& AddValue(const StringPiece& name,
+ std::unique_ptr<Value> value) {
+ return AddValue(name, {}, std::move(value));
+ }
- ResourceTableBuilder& addValue(const StringPiece& name,
- std::unique_ptr<Value> value) {
- return addValue(name, {}, std::move(value));
- }
+ 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 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) {
+ ResourceName res_name = ParseNameOrDie(name);
+ CHECK(table_->AddResourceAllowMangled(res_name, id, config, {},
+ std::move(value), &diagnostics_));
+ return *this;
+ }
- ResourceTableBuilder& addValue(const StringPiece& name, const ConfigDescription& config,
- const ResourceId& id, std::unique_ptr<Value> value) {
- ResourceName resName = parseNameOrDie(name);
- bool result = mTable->addResourceAllowMangled(resName, id, config, {},
- std::move(value), &mDiagnostics);
- assert(result);
- return *this;
- }
+ ResourceTableBuilder& SetSymbolState(const StringPiece& name,
+ const ResourceId& id,
+ SymbolState state) {
+ ResourceName res_name = ParseNameOrDie(name);
+ Symbol symbol;
+ symbol.state = state;
+ CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol,
+ &diagnostics_));
+ return *this;
+ }
- ResourceTableBuilder& setSymbolState(const StringPiece& name, const ResourceId& id,
- SymbolState state) {
- ResourceName resName = parseNameOrDie(name);
- Symbol symbol;
- symbol.state = state;
- bool result = mTable->setSymbolStateAllowMangled(resName, id, symbol, &mDiagnostics);
- assert(result);
- return *this;
- }
+ std::unique_ptr<ResourceTable> Build() { return std::move(table_); }
- std::unique_ptr<ResourceTable> build() {
- return std::move(mTable);
- }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceTableBuilder);
+
+ DummyDiagnosticsImpl diagnostics_;
+ std::unique_ptr<ResourceTable> table_ = util::make_unique<ResourceTable>();
};
-inline std::unique_ptr<Reference> buildReference(const StringPiece& ref,
- const Maybe<ResourceId>& id = {}) {
- std::unique_ptr<Reference> reference = util::make_unique<Reference>(parseNameOrDie(ref));
- reference->id = id;
- return reference;
+inline std::unique_ptr<Reference> BuildReference(
+ const StringPiece& ref, const Maybe<ResourceId>& id = {}) {
+ std::unique_ptr<Reference> reference =
+ util::make_unique<Reference>(ParseNameOrDie(ref));
+ reference->id = id;
+ return reference;
}
-inline std::unique_ptr<BinaryPrimitive> buildPrimitive(uint8_t type, uint32_t data) {
- android::Res_value value = {};
- value.size = sizeof(value);
- value.dataType = type;
- value.data = data;
- return util::make_unique<BinaryPrimitive>(value);
+inline std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type,
+ uint32_t data) {
+ android::Res_value value = {};
+ value.size = sizeof(value);
+ value.dataType = type;
+ value.data = data;
+ return util::make_unique<BinaryPrimitive>(value);
}
template <typename T>
class ValueBuilder {
-private:
- std::unique_ptr<Value> mValue;
+ public:
+ template <typename... Args>
+ explicit ValueBuilder(Args&&... args)
+ : value_(new T{std::forward<Args>(args)...}) {}
-public:
- template <typename... Args>
- explicit ValueBuilder(Args&&... args) : mValue(new T{ std::forward<Args>(args)... }) {
- }
+ template <typename... Args>
+ ValueBuilder& SetSource(Args&&... args) {
+ value_->SetSource(Source{std::forward<Args>(args)...});
+ return *this;
+ }
- template <typename... Args>
- ValueBuilder& setSource(Args&&... args) {
- mValue->setSource(Source{ std::forward<Args>(args)... });
- return *this;
- }
+ ValueBuilder& SetComment(const StringPiece& str) {
+ value_->SetComment(str);
+ return *this;
+ }
- ValueBuilder& setComment(const StringPiece& str) {
- mValue->setComment(str);
- return *this;
- }
+ std::unique_ptr<Value> Build() { return std::move(value_); }
- std::unique_ptr<Value> build() {
- return std::move(mValue);
- }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ValueBuilder);
+
+ std::unique_ptr<Value> value_;
};
class AttributeBuilder {
-private:
- std::unique_ptr<Attribute> mAttr;
+ public:
+ explicit AttributeBuilder(bool weak = false)
+ : attr_(util::make_unique<Attribute>(weak)) {
+ attr_->type_mask = android::ResTable_map::TYPE_ANY;
+ }
-public:
- explicit AttributeBuilder(bool weak = false) : mAttr(util::make_unique<Attribute>(weak)) {
- mAttr->typeMask = android::ResTable_map::TYPE_ANY;
- }
+ AttributeBuilder& SetTypeMask(uint32_t typeMask) {
+ attr_->type_mask = typeMask;
+ return *this;
+ }
- AttributeBuilder& setTypeMask(uint32_t typeMask) {
- mAttr->typeMask = typeMask;
- return *this;
- }
+ AttributeBuilder& AddItem(const StringPiece& name, uint32_t value) {
+ attr_->symbols.push_back(Attribute::Symbol{
+ Reference(ResourceName({}, ResourceType::kId, name)), value});
+ return *this;
+ }
- AttributeBuilder& addItem(const StringPiece& name, uint32_t value) {
- mAttr->symbols.push_back(Attribute::Symbol{
- Reference(ResourceName({}, ResourceType::kId, name)),
- value});
- return *this;
- }
+ std::unique_ptr<Attribute> Build() { return std::move(attr_); }
- std::unique_ptr<Attribute> build() {
- return std::move(mAttr);
- }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AttributeBuilder);
+
+ std::unique_ptr<Attribute> attr_;
};
class StyleBuilder {
-private:
- std::unique_ptr<Style> mStyle = util::make_unique<Style>();
+ public:
+ StyleBuilder() = default;
-public:
- StyleBuilder& setParent(const StringPiece& str) {
- mStyle->parent = Reference(parseNameOrDie(str));
- return *this;
- }
+ StyleBuilder& SetParent(const StringPiece& str) {
+ style_->parent = Reference(ParseNameOrDie(str));
+ return *this;
+ }
- StyleBuilder& addItem(const StringPiece& str, std::unique_ptr<Item> value) {
- mStyle->entries.push_back(Style::Entry{ Reference(parseNameOrDie(str)), std::move(value) });
- return *this;
- }
+ StyleBuilder& AddItem(const StringPiece& str, std::unique_ptr<Item> value) {
+ style_->entries.push_back(
+ Style::Entry{Reference(ParseNameOrDie(str)), std::move(value)});
+ return *this;
+ }
- 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;
- }
+ StyleBuilder& AddItem(const StringPiece& str, const ResourceId& id,
+ std::unique_ptr<Item> value) {
+ AddItem(str, std::move(value));
+ style_->entries.back().key.id = id;
+ return *this;
+ }
- std::unique_ptr<Style> build() {
- return std::move(mStyle);
- }
+ std::unique_ptr<Style> Build() { return std::move(style_); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StyleBuilder);
+
+ std::unique_ptr<Style> style_ = util::make_unique<Style>();
};
class StyleableBuilder {
-private:
- std::unique_ptr<Styleable> mStyleable = util::make_unique<Styleable>();
+ public:
+ StyleableBuilder() = default;
-public:
- StyleableBuilder& addItem(const StringPiece& str, const Maybe<ResourceId>& id = {}) {
- mStyleable->entries.push_back(Reference(parseNameOrDie(str)));
- mStyleable->entries.back().id = id;
- return *this;
- }
+ StyleableBuilder& AddItem(const StringPiece& str,
+ const Maybe<ResourceId>& id = {}) {
+ styleable_->entries.push_back(Reference(ParseNameOrDie(str)));
+ styleable_->entries.back().id = id;
+ return *this;
+ }
- std::unique_ptr<Styleable> build() {
- return std::move(mStyleable);
- }
+ std::unique_ptr<Styleable> Build() { return std::move(styleable_); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StyleableBuilder);
+
+ std::unique_ptr<Styleable> styleable_ = util::make_unique<Styleable>();
};
-inline std::unique_ptr<xml::XmlResource> buildXmlDom(const StringPiece& str) {
- std::stringstream in;
- in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
- StdErrDiagnostics diag;
- std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, Source("test.xml"));
- assert(doc);
- return doc;
+inline std::unique_ptr<xml::XmlResource> BuildXmlDom(const StringPiece& str) {
+ std::stringstream in;
+ in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
+ StdErrDiagnostics diag;
+ std::unique_ptr<xml::XmlResource> doc =
+ xml::Inflate(&in, &diag, Source("test.xml"));
+ CHECK(doc != nullptr) << "failed to parse inline XML string";
+ return doc;
}
-inline std::unique_ptr<xml::XmlResource> buildXmlDomForPackageName(IAaptContext* context,
- const StringPiece& str) {
- std::unique_ptr<xml::XmlResource> doc = buildXmlDom(str);
- doc->file.name.package = context->getCompilationPackage();
- return doc;
+inline std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(
+ IAaptContext* context, const StringPiece& str) {
+ std::unique_ptr<xml::XmlResource> doc = BuildXmlDom(str);
+ doc->file.name.package = context->GetCompilationPackage();
+ return doc;
}
-} // namespace test
-} // namespace aapt
+} // namespace test
+} // namespace aapt
#endif /* AAPT_TEST_BUILDERS_H */
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 7fafcbe..3689201 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -17,6 +17,12 @@
#ifndef AAPT_TEST_COMMON_H
#define AAPT_TEST_COMMON_H
+#include <iostream>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "gtest/gtest.h"
+
#include "ConfigDescription.h"
#include "Debug.h"
#include "ResourceTable.h"
@@ -26,11 +32,9 @@
#include "process/IResourceTableConsumer.h"
#include "util/StringPiece.h"
-#include <gtest/gtest.h>
-#include <iostream>
-
//
-// GTEST 1.7 doesn't explicitly cast to bool, which causes explicit operators to fail to compile.
+// GTEST 1.7 doesn't explicitly cast to bool, which causes explicit operators to
+// fail to compile.
//
#define AAPT_ASSERT_TRUE(v) ASSERT_TRUE(bool(v))
#define AAPT_ASSERT_FALSE(v) ASSERT_FALSE(bool(v))
@@ -41,81 +45,84 @@
namespace test {
struct DummyDiagnosticsImpl : public IDiagnostics {
- void log(Level level, DiagMessageActual& actualMsg) override {
- switch (level) {
- case Level::Note:
- return;
+ void Log(Level level, DiagMessageActual& actual_msg) override {
+ switch (level) {
+ case Level::Note:
+ return;
- case Level::Warn:
- std::cerr << actualMsg.source << ": warn: " << actualMsg.message << "." << std::endl;
- break;
+ case Level::Warn:
+ std::cerr << actual_msg.source << ": warn: " << actual_msg.message
+ << "." << std::endl;
+ break;
- case Level::Error:
- std::cerr << actualMsg.source << ": error: " << actualMsg.message << "." << std::endl;
- break;
- }
+ case Level::Error:
+ std::cerr << actual_msg.source << ": error: " << actual_msg.message
+ << "." << std::endl;
+ break;
}
+ }
};
-inline IDiagnostics* getDiagnostics() {
- static DummyDiagnosticsImpl diag;
- return &diag;
+inline IDiagnostics* GetDiagnostics() {
+ static DummyDiagnosticsImpl diag;
+ return &diag;
}
-inline ResourceName parseNameOrDie(const StringPiece& str) {
- ResourceNameRef ref;
- bool result = ResourceUtils::parseResourceName(str, &ref);
- assert(result && "invalid resource name");
- return ref.toResourceName();
+inline ResourceName ParseNameOrDie(const StringPiece& str) {
+ ResourceNameRef ref;
+ CHECK(ResourceUtils::ParseResourceName(str, &ref)) << "invalid resource name";
+ return ref.ToResourceName();
}
-inline ConfigDescription parseConfigOrDie(const StringPiece& str) {
- ConfigDescription config;
- bool result = ConfigDescription::parse(str, &config);
- assert(result && "invalid configuration");
- return config;
+inline ConfigDescription ParseConfigOrDie(const StringPiece& str) {
+ ConfigDescription config;
+ CHECK(ConfigDescription::Parse(str, &config)) << "invalid configuration";
+ return config;
}
-template <typename T> T* getValueForConfigAndProduct(ResourceTable* table,
- const StringPiece& resName,
- const ConfigDescription& config,
- const StringPiece& product) {
- Maybe<ResourceTable::SearchResult> result = table->findResource(parseNameOrDie(resName));
- if (result) {
- ResourceConfigValue* configValue = result.value().entry->findValue(config, product);
- if (configValue) {
- return valueCast<T>(configValue->value.get());
- }
+template <typename T>
+T* GetValueForConfigAndProduct(ResourceTable* table,
+ const StringPiece& res_name,
+ const ConfigDescription& config,
+ const StringPiece& product) {
+ Maybe<ResourceTable::SearchResult> result =
+ table->FindResource(ParseNameOrDie(res_name));
+ if (result) {
+ ResourceConfigValue* config_value =
+ result.value().entry->FindValue(config, product);
+ if (config_value) {
+ return ValueCast<T>(config_value->value.get());
}
- return nullptr;
+ }
+ return nullptr;
}
-template <typename T> T* getValueForConfig(ResourceTable* table, const StringPiece& resName,
- const ConfigDescription& config) {
- return getValueForConfigAndProduct<T>(table, resName, config, {});
+template <typename T>
+T* GetValueForConfig(ResourceTable* table, const StringPiece& res_name,
+ const ConfigDescription& config) {
+ return GetValueForConfigAndProduct<T>(table, res_name, config, {});
}
-template <typename T> T* getValue(ResourceTable* table, const StringPiece& resName) {
- return getValueForConfig<T>(table, resName, {});
+template <typename T>
+T* GetValue(ResourceTable* table, const StringPiece& res_name) {
+ return GetValueForConfig<T>(table, res_name, {});
}
class TestFile : public io::IFile {
-private:
- Source mSource;
+ public:
+ explicit TestFile(const StringPiece& path) : source_(path) {}
-public:
- explicit TestFile(const StringPiece& path) : mSource(path) {}
+ std::unique_ptr<io::IData> OpenAsData() override { return {}; }
- std::unique_ptr<io::IData> openAsData() override {
- return {};
- }
+ const Source& GetSource() const override { return source_; }
- const Source& getSource() const override {
- return mSource;
- }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestFile);
+
+ Source source_;
};
-} // namespace test
-} // namespace aapt
+} // namespace test
+} // namespace aapt
#endif /* AAPT_TEST_COMMON_H */
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 54f16db..7986329 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -17,165 +17,162 @@
#ifndef AAPT_TEST_CONTEXT_H
#define AAPT_TEST_CONTEXT_H
+#include <list>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+
#include "NameMangler.h"
-#include "util/Util.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
#include "test/Common.h"
-
-#include <cassert>
-#include <list>
+#include "util/Util.h"
namespace aapt {
namespace test {
class Context : public IAaptContext {
-public:
- SymbolTable* getExternalSymbols() override {
- return &mSymbols;
- }
+ public:
+ Context() = default;
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ SymbolTable* GetExternalSymbols() override { return &symbols_; }
- const std::string& getCompilationPackage() override {
- assert(mCompilationPackage && "package name not set");
- return mCompilationPackage.value();
- }
+ IDiagnostics* GetDiagnostics() override { return &diagnostics_; }
- uint8_t getPackageId() override {
- assert(mPackageId && "package ID not set");
- return mPackageId.value();
- }
+ const std::string& GetCompilationPackage() override {
+ CHECK(bool(compilation_package_)) << "package name not set";
+ return compilation_package_.value();
+ }
- NameMangler* getNameMangler() override {
- return &mNameMangler;
- }
+ uint8_t GetPackageId() override {
+ CHECK(bool(package_id_)) << "package ID not set";
+ return package_id_.value();
+ }
- bool verbose() override {
- return false;
- }
+ NameMangler* GetNameMangler() override { return &name_mangler_; }
- int getMinSdkVersion() override {
- return mMinSdkVersion;
- }
+ bool IsVerbose() override { return false; }
-private:
- friend class ContextBuilder;
+ int GetMinSdkVersion() override { return min_sdk_version_; }
- Maybe<std::string> mCompilationPackage;
- Maybe<uint8_t> mPackageId;
- StdErrDiagnostics mDiagnostics;
- SymbolTable mSymbols;
- NameMangler mNameMangler = NameMangler({});
- int mMinSdkVersion = 0;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Context);
+
+ friend class ContextBuilder;
+
+ Maybe<std::string> compilation_package_;
+ Maybe<uint8_t> package_id_;
+ StdErrDiagnostics diagnostics_;
+ SymbolTable symbols_;
+ NameMangler name_mangler_ = NameMangler({});
+ int min_sdk_version_ = 0;
};
class ContextBuilder {
-private:
- std::unique_ptr<Context> mContext = std::unique_ptr<Context>(new Context());
+ public:
+ ContextBuilder& SetCompilationPackage(const StringPiece& package) {
+ context_->compilation_package_ = package.ToString();
+ return *this;
+ }
-public:
- ContextBuilder& setCompilationPackage(const StringPiece& package) {
- mContext->mCompilationPackage = package.toString();
- return *this;
- }
+ ContextBuilder& SetPackageId(uint8_t id) {
+ context_->package_id_ = id;
+ return *this;
+ }
- ContextBuilder& setPackageId(uint8_t id) {
- mContext->mPackageId = id;
- return *this;
- }
+ ContextBuilder& SetNameManglerPolicy(const NameManglerPolicy& policy) {
+ context_->name_mangler_ = NameMangler(policy);
+ return *this;
+ }
- ContextBuilder& setNameManglerPolicy(const NameManglerPolicy& policy) {
- mContext->mNameMangler = NameMangler(policy);
- return *this;
- }
+ ContextBuilder& AddSymbolSource(std::unique_ptr<ISymbolSource> src) {
+ context_->GetExternalSymbols()->AppendSource(std::move(src));
+ return *this;
+ }
- ContextBuilder& addSymbolSource(std::unique_ptr<ISymbolSource> src) {
- mContext->getExternalSymbols()->appendSource(std::move(src));
- return *this;
- }
+ ContextBuilder& SetMinSdkVersion(int min_sdk) {
+ context_->min_sdk_version_ = min_sdk;
+ return *this;
+ }
- ContextBuilder& setMinSdkVersion(int minSdk) {
- mContext->mMinSdkVersion = minSdk;
- return *this;
- }
+ std::unique_ptr<Context> Build() { return std::move(context_); }
- std::unique_ptr<Context> build() {
- return std::move(mContext);
- }
+ private:
+ std::unique_ptr<Context> context_ = std::unique_ptr<Context>(new Context());
};
class StaticSymbolSourceBuilder {
-public:
- StaticSymbolSourceBuilder& addPublicSymbol(const StringPiece& name, ResourceId id,
- std::unique_ptr<Attribute> attr = {}) {
- std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(
- id, std::move(attr), true);
- mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
- mSymbolSource->mIdMap[id] = symbol.get();
- mSymbolSource->mSymbols.push_back(std::move(symbol));
- return *this;
+ public:
+ StaticSymbolSourceBuilder& AddPublicSymbol(
+ const StringPiece& name, ResourceId id,
+ std::unique_ptr<Attribute> attr = {}) {
+ std::unique_ptr<SymbolTable::Symbol> symbol =
+ util::make_unique<SymbolTable::Symbol>(id, std::move(attr), true);
+ symbol_source_->name_map_[ParseNameOrDie(name)] = symbol.get();
+ symbol_source_->id_map_[id] = symbol.get();
+ symbol_source_->symbols_.push_back(std::move(symbol));
+ return *this;
+ }
+
+ StaticSymbolSourceBuilder& AddSymbol(const StringPiece& name, ResourceId id,
+ std::unique_ptr<Attribute> attr = {}) {
+ std::unique_ptr<SymbolTable::Symbol> symbol =
+ util::make_unique<SymbolTable::Symbol>(id, std::move(attr), false);
+ symbol_source_->name_map_[ParseNameOrDie(name)] = symbol.get();
+ symbol_source_->id_map_[id] = symbol.get();
+ symbol_source_->symbols_.push_back(std::move(symbol));
+ return *this;
+ }
+
+ std::unique_ptr<ISymbolSource> Build() { return std::move(symbol_source_); }
+
+ private:
+ class StaticSymbolSource : public ISymbolSource {
+ public:
+ StaticSymbolSource() = default;
+
+ std::unique_ptr<SymbolTable::Symbol> FindByName(
+ const ResourceName& name) override {
+ auto iter = name_map_.find(name);
+ if (iter != name_map_.end()) {
+ return CloneSymbol(iter->second);
+ }
+ return nullptr;
}
- StaticSymbolSourceBuilder& addSymbol(const StringPiece& name, ResourceId id,
- std::unique_ptr<Attribute> attr = {}) {
- std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(
- id, std::move(attr), false);
- mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
- mSymbolSource->mIdMap[id] = symbol.get();
- mSymbolSource->mSymbols.push_back(std::move(symbol));
- return *this;
+ std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override {
+ auto iter = id_map_.find(id);
+ if (iter != id_map_.end()) {
+ return CloneSymbol(iter->second);
+ }
+ return nullptr;
}
- std::unique_ptr<ISymbolSource> build() {
- return std::move(mSymbolSource);
+ std::list<std::unique_ptr<SymbolTable::Symbol>> symbols_;
+ std::map<ResourceName, SymbolTable::Symbol*> name_map_;
+ std::map<ResourceId, SymbolTable::Symbol*> id_map_;
+
+ private:
+ std::unique_ptr<SymbolTable::Symbol> CloneSymbol(SymbolTable::Symbol* sym) {
+ std::unique_ptr<SymbolTable::Symbol> clone =
+ util::make_unique<SymbolTable::Symbol>();
+ clone->id = sym->id;
+ if (sym->attribute) {
+ clone->attribute =
+ std::unique_ptr<Attribute>(sym->attribute->Clone(nullptr));
+ }
+ clone->is_public = sym->is_public;
+ return clone;
}
-private:
- class StaticSymbolSource : public ISymbolSource {
- public:
- StaticSymbolSource() = default;
+ DISALLOW_COPY_AND_ASSIGN(StaticSymbolSource);
+ };
- std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override {
- auto iter = mNameMap.find(name);
- if (iter != mNameMap.end()) {
- return cloneSymbol(iter->second);
- }
- return nullptr;
- }
-
- std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
- auto iter = mIdMap.find(id);
- if (iter != mIdMap.end()) {
- return cloneSymbol(iter->second);
- }
- return nullptr;
- }
-
- std::list<std::unique_ptr<SymbolTable::Symbol>> mSymbols;
- std::map<ResourceName, SymbolTable::Symbol*> mNameMap;
- std::map<ResourceId, SymbolTable::Symbol*> mIdMap;
-
- private:
- std::unique_ptr<SymbolTable::Symbol> cloneSymbol(SymbolTable::Symbol* sym) {
- std::unique_ptr<SymbolTable::Symbol> clone = util::make_unique<SymbolTable::Symbol>();
- clone->id = sym->id;
- if (sym->attribute) {
- clone->attribute = std::unique_ptr<Attribute>(sym->attribute->clone(nullptr));
- }
- clone->isPublic = sym->isPublic;
- return clone;
- }
-
- DISALLOW_COPY_AND_ASSIGN(StaticSymbolSource);
- };
-
- std::unique_ptr<StaticSymbolSource> mSymbolSource = util::make_unique<StaticSymbolSource>();
+ std::unique_ptr<StaticSymbolSource> symbol_source_ =
+ util::make_unique<StaticSymbolSource>();
};
-} // namespace test
-} // namespace aapt
+} // namespace test
+} // namespace aapt
#endif /* AAPT_TEST_CONTEXT_H */
diff --git a/tools/aapt2/test/Test.h b/tools/aapt2/test/Test.h
index d4845cf..ec07432 100644
--- a/tools/aapt2/test/Test.h
+++ b/tools/aapt2/test/Test.h
@@ -17,16 +17,10 @@
#ifndef AAPT_TEST_TEST_H
#define AAPT_TEST_TEST_H
+#include "gtest/gtest.h"
+
#include "test/Builders.h"
#include "test/Common.h"
#include "test/Context.h"
-#include <gtest/gtest.h>
-
-namespace aapt {
-namespace test {
-
-} // namespace test
-} // namespace aapt
-
-#endif // AAPT_TEST_TEST_H
+#endif // AAPT_TEST_TEST_H
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 4fd77c8..aeabcff 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -14,22 +14,25 @@
* limitations under the License.
*/
+#include "unflatten/BinaryResourceParser.h"
+
+#include <algorithm>
+#include <map>
+#include <string>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/TypeWrappers.h"
+
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "Source.h"
#include "ValueVisitor.h"
-#include "unflatten/BinaryResourceParser.h"
#include "unflatten/ResChunkPullParser.h"
#include "util/Util.h"
-#include <androidfw/ResourceTypes.h>
-#include <androidfw/TypeWrappers.h>
-#include <android-base/macros.h>
-#include <algorithm>
-#include <map>
-#include <string>
-
namespace aapt {
using namespace android;
@@ -41,533 +44,552 @@
* given a mapping from resource ID to resource name.
*/
class ReferenceIdToNameVisitor : public ValueVisitor {
-private:
- const std::map<ResourceId, ResourceName>* mMapping;
+ public:
+ using ValueVisitor::Visit;
-public:
- using ValueVisitor::visit;
+ explicit ReferenceIdToNameVisitor(
+ const std::map<ResourceId, ResourceName>* mapping)
+ : mapping_(mapping) {
+ CHECK(mapping_ != nullptr);
+ }
- explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping) :
- mMapping(mapping) {
- assert(mMapping);
+ void Visit(Reference* reference) override {
+ if (!reference->id || !reference->id.value().is_valid()) {
+ return;
}
- void visit(Reference* reference) override {
- if (!reference->id || !reference->id.value().isValid()) {
- return;
- }
-
- ResourceId id = reference->id.value();
- auto cacheIter = mMapping->find(id);
- if (cacheIter != mMapping->end()) {
- reference->name = cacheIter->second;
- }
+ ResourceId id = reference->id.value();
+ auto cache_iter = mapping_->find(id);
+ if (cache_iter != mapping_->end()) {
+ reference->name = cache_iter->second;
}
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReferenceIdToNameVisitor);
+
+ const std::map<ResourceId, ResourceName>* mapping_;
};
-} // namespace
+} // namespace
-BinaryResourceParser::BinaryResourceParser(IAaptContext* context, ResourceTable* table,
- const Source& source, const void* data, size_t len) :
- mContext(context), mTable(table), mSource(source), mData(data), mDataLen(len) {
-}
+BinaryResourceParser::BinaryResourceParser(IAaptContext* context,
+ ResourceTable* table,
+ const Source& source,
+ const void* data, size_t len)
+ : context_(context),
+ table_(table),
+ source_(source),
+ data_(data),
+ data_len_(len) {}
-bool BinaryResourceParser::parse() {
- ResChunkPullParser parser(mData, mDataLen);
+bool BinaryResourceParser::Parse() {
+ ResChunkPullParser parser(data_, data_len_);
- bool error = false;
- while(ResChunkPullParser::isGoodEvent(parser.next())) {
- if (parser.getChunk()->type != android::RES_TABLE_TYPE) {
- mContext->getDiagnostics()->warn(DiagMessage(mSource)
- << "unknown chunk of type '"
- << (int) parser.getChunk()->type << "'");
- continue;
- }
-
- if (!parseTable(parser.getChunk())) {
- error = true;
- }
+ bool error = false;
+ while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
+ if (parser.chunk()->type != android::RES_TABLE_TYPE) {
+ context_->GetDiagnostics()->Warn(DiagMessage(source_)
+ << "unknown chunk of type '"
+ << (int)parser.chunk()->type << "'");
+ continue;
}
- if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt resource table: "
- << parser.getLastError());
- return false;
+ if (!ParseTable(parser.chunk())) {
+ error = true;
}
- return !error;
+ }
+
+ if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(source_) << "corrupt resource table: " << parser.error());
+ return false;
+ }
+ return !error;
}
/**
- * Parses the resource table, which contains all the packages, types, and entries.
+ * Parses the resource table, which contains all the packages, types, and
+ * entries.
*/
-bool BinaryResourceParser::parseTable(const ResChunk_header* chunk) {
- const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk);
- if (!tableHeader) {
- mContext->getDiagnostics()->error(DiagMessage(mSource) << "corrupt ResTable_header chunk");
- return false;
- }
+bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) {
+ const ResTable_header* table_header = ConvertTo<ResTable_header>(chunk);
+ if (!table_header) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt ResTable_header chunk");
+ return false;
+ }
- ResChunkPullParser parser(getChunkData(&tableHeader->header),
- getChunkDataLen(&tableHeader->header));
- while (ResChunkPullParser::isGoodEvent(parser.next())) {
- switch (util::deviceToHost16(parser.getChunk()->type)) {
- case android::RES_STRING_POOL_TYPE:
- if (mValuePool.getError() == NO_INIT) {
- status_t err = mValuePool.setTo(parser.getChunk(),
- util::deviceToHost32(parser.getChunk()->size));
- if (err != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt string pool in ResTable: "
- << mValuePool.getError());
- return false;
- }
+ ResChunkPullParser parser(GetChunkData(&table_header->header),
+ GetChunkDataLen(&table_header->header));
+ while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
+ switch (util::DeviceToHost16(parser.chunk()->type)) {
+ case android::RES_STRING_POOL_TYPE:
+ if (value_pool_.getError() == NO_INIT) {
+ status_t err = value_pool_.setTo(
+ parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+ if (err != NO_ERROR) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(source_) << "corrupt string pool in ResTable: "
+ << value_pool_.getError());
+ return false;
+ }
- // Reserve some space for the strings we are going to add.
- mTable->stringPool.hintWillAdd(mValuePool.size(), mValuePool.styleCount());
- } else {
- mContext->getDiagnostics()->warn(DiagMessage(mSource)
- << "unexpected string pool in ResTable");
- }
- break;
-
- case android::RES_TABLE_PACKAGE_TYPE:
- if (!parsePackage(parser.getChunk())) {
- return false;
- }
- break;
-
- default:
- mContext->getDiagnostics()
- ->warn(DiagMessage(mSource)
- << "unexpected chunk type "
- << (int) util::deviceToHost16(parser.getChunk()->type));
- break;
+ // Reserve some space for the strings we are going to add.
+ table_->string_pool.HintWillAdd(value_pool_.size(),
+ value_pool_.styleCount());
+ } else {
+ context_->GetDiagnostics()->Warn(
+ DiagMessage(source_) << "unexpected string pool in ResTable");
}
- }
+ break;
- if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt resource table: " << parser.getLastError());
- return false;
- }
- return true;
-}
-
-
-bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) {
- const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk);
- if (!packageHeader) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt ResTable_package chunk");
- return false;
- }
-
- uint32_t packageId = util::deviceToHost32(packageHeader->id);
- if (packageId > std::numeric_limits<uint8_t>::max()) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "package ID is too big (" << packageId << ")");
- return false;
- }
-
- // Extract the package name.
- size_t len = strnlen16((const char16_t*) packageHeader->name, arraysize(packageHeader->name));
- std::u16string packageName;
- packageName.resize(len);
- for (size_t i = 0; i < len; i++) {
- packageName[i] = util::deviceToHost16(packageHeader->name[i]);
- }
-
- ResourceTablePackage* package = mTable->createPackage(util::utf16ToUtf8(packageName),
- static_cast<uint8_t>(packageId));
- if (!package) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "incompatible package '" << packageName
- << "' with ID " << packageId);
- return false;
- }
-
- // There can be multiple packages in a table, so
- // clear the type and key pool in case they were set from a previous package.
- mTypePool.uninit();
- mKeyPool.uninit();
-
- ResChunkPullParser parser(getChunkData(&packageHeader->header),
- getChunkDataLen(&packageHeader->header));
- while (ResChunkPullParser::isGoodEvent(parser.next())) {
- switch (util::deviceToHost16(parser.getChunk()->type)) {
- case android::RES_STRING_POOL_TYPE:
- if (mTypePool.getError() == NO_INIT) {
- status_t err = mTypePool.setTo(parser.getChunk(),
- util::deviceToHost32(parser.getChunk()->size));
- if (err != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt type string pool in "
- << "ResTable_package: "
- << mTypePool.getError());
- return false;
- }
- } else if (mKeyPool.getError() == NO_INIT) {
- status_t err = mKeyPool.setTo(parser.getChunk(),
- util::deviceToHost32(parser.getChunk()->size));
- if (err != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt key string pool in "
- << "ResTable_package: "
- << mKeyPool.getError());
- return false;
- }
- } else {
- mContext->getDiagnostics()->warn(DiagMessage(mSource) << "unexpected string pool");
- }
- break;
-
- case android::RES_TABLE_TYPE_SPEC_TYPE:
- if (!parseTypeSpec(parser.getChunk())) {
- return false;
- }
- break;
-
- case android::RES_TABLE_TYPE_TYPE:
- if (!parseType(package, parser.getChunk())) {
- return false;
- }
- break;
-
- default:
- mContext->getDiagnostics()
- ->warn(DiagMessage(mSource)
- << "unexpected chunk type "
- << (int) util::deviceToHost16(parser.getChunk()->type));
- break;
+ case android::RES_TABLE_PACKAGE_TYPE:
+ if (!ParsePackage(parser.chunk())) {
+ return false;
}
- }
+ break;
- if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt ResTable_package: "
- << parser.getLastError());
- return false;
+ default:
+ context_->GetDiagnostics()->Warn(
+ DiagMessage(source_)
+ << "unexpected chunk type "
+ << (int)util::DeviceToHost16(parser.chunk()->type));
+ break;
}
+ }
- // Now go through the table and change local resource ID references to
- // symbolic references.
- ReferenceIdToNameVisitor visitor(&mIdIndex);
- visitAllValuesInTable(mTable, &visitor);
- return true;
+ if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(source_) << "corrupt resource table: " << parser.error());
+ return false;
+ }
+ return true;
}
-bool BinaryResourceParser::parseTypeSpec(const ResChunk_header* chunk) {
- if (mTypePool.getError() != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "missing type string pool");
- return false;
- }
+bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
+ const ResTable_package* package_header = ConvertTo<ResTable_package>(chunk);
+ if (!package_header) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt ResTable_package chunk");
+ return false;
+ }
- const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk);
- if (!typeSpec) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt ResTable_typeSpec chunk");
- return false;
- }
+ uint32_t package_id = util::DeviceToHost32(package_header->id);
+ if (package_id > std::numeric_limits<uint8_t>::max()) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(source_) << "package ID is too big (" << package_id << ")");
+ return false;
+ }
- if (typeSpec->id == 0) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "ResTable_typeSpec has invalid id: " << typeSpec->id);
- return false;
+ // Extract the package name.
+ size_t len = strnlen16((const char16_t*)package_header->name,
+ arraysize(package_header->name));
+ std::u16string package_name;
+ package_name.resize(len);
+ for (size_t i = 0; i < len; i++) {
+ package_name[i] = util::DeviceToHost16(package_header->name[i]);
+ }
+
+ ResourceTablePackage* package = table_->CreatePackage(
+ util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
+ if (!package) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(source_) << "incompatible package '" << package_name
+ << "' with ID " << package_id);
+ return false;
+ }
+
+ // There can be multiple packages in a table, so
+ // clear the type and key pool in case they were set from a previous package.
+ type_pool_.uninit();
+ key_pool_.uninit();
+
+ ResChunkPullParser parser(GetChunkData(&package_header->header),
+ GetChunkDataLen(&package_header->header));
+ while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
+ switch (util::DeviceToHost16(parser.chunk()->type)) {
+ case android::RES_STRING_POOL_TYPE:
+ if (type_pool_.getError() == NO_INIT) {
+ status_t err = type_pool_.setTo(
+ parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+ if (err != NO_ERROR) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt type string pool in "
+ << "ResTable_package: "
+ << type_pool_.getError());
+ return false;
+ }
+ } else if (key_pool_.getError() == NO_INIT) {
+ status_t err = key_pool_.setTo(
+ parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+ if (err != NO_ERROR) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt key string pool in "
+ << "ResTable_package: "
+ << key_pool_.getError());
+ return false;
+ }
+ } else {
+ context_->GetDiagnostics()->Warn(DiagMessage(source_)
+ << "unexpected string pool");
+ }
+ break;
+
+ case android::RES_TABLE_TYPE_SPEC_TYPE:
+ if (!ParseTypeSpec(parser.chunk())) {
+ return false;
+ }
+ break;
+
+ case android::RES_TABLE_TYPE_TYPE:
+ if (!ParseType(package, parser.chunk())) {
+ return false;
+ }
+ break;
+
+ default:
+ context_->GetDiagnostics()->Warn(
+ DiagMessage(source_)
+ << "unexpected chunk type "
+ << (int)util::DeviceToHost16(parser.chunk()->type));
+ break;
}
- return true;
+ }
+
+ if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(source_) << "corrupt ResTable_package: " << parser.error());
+ return false;
+ }
+
+ // Now go through the table and change local resource ID references to
+ // symbolic references.
+ ReferenceIdToNameVisitor visitor(&id_index_);
+ VisitAllValuesInTable(table_, &visitor);
+ return true;
}
-bool BinaryResourceParser::parseType(const ResourceTablePackage* package,
+bool BinaryResourceParser::ParseTypeSpec(const ResChunk_header* chunk) {
+ if (type_pool_.getError() != NO_ERROR) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "missing type string pool");
+ return false;
+ }
+
+ const ResTable_typeSpec* type_spec = ConvertTo<ResTable_typeSpec>(chunk);
+ if (!type_spec) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt ResTable_typeSpec chunk");
+ return false;
+ }
+
+ if (type_spec->id == 0) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "ResTable_typeSpec has invalid id: "
+ << type_spec->id);
+ return false;
+ }
+ return true;
+}
+
+bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
const ResChunk_header* chunk) {
- if (mTypePool.getError() != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "missing type string pool");
+ if (type_pool_.getError() != NO_ERROR) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "missing type string pool");
+ return false;
+ }
+
+ if (key_pool_.getError() != NO_ERROR) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "missing key string pool");
+ return false;
+ }
+
+ const ResTable_type* type = ConvertTo<ResTable_type>(chunk);
+ if (!type) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt ResTable_type chunk");
+ return false;
+ }
+
+ if (type->id == 0) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "ResTable_type has invalid id: "
+ << (int)type->id);
+ return false;
+ }
+
+ ConfigDescription config;
+ config.copyFromDtoH(type->config);
+
+ const std::string type_str = util::GetString(type_pool_, type->id - 1);
+
+ const ResourceType* parsed_type = ParseResourceType(type_str);
+ if (!parsed_type) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(source_) << "invalid type name '" << type_str
+ << "' for type with ID " << (int)type->id);
+ return false;
+ }
+
+ TypeVariant tv(type);
+ for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
+ const ResTable_entry* entry = *it;
+ if (!entry) {
+ continue;
+ }
+
+ const ResourceName name(
+ package->name, *parsed_type,
+ util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)));
+
+ const ResourceId res_id(package->id.value(), type->id,
+ static_cast<uint16_t>(it.index()));
+
+ std::unique_ptr<Value> resource_value;
+ if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
+ const ResTable_map_entry* mapEntry =
+ static_cast<const ResTable_map_entry*>(entry);
+
+ // TODO(adamlesinski): Check that the entry count is valid.
+ resource_value = ParseMapEntry(name, config, mapEntry);
+ } else {
+ const Res_value* value =
+ (const Res_value*)((const uint8_t*)entry +
+ util::DeviceToHost32(entry->size));
+ resource_value = ParseValue(name, config, value, entry->flags);
+ }
+
+ if (!resource_value) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage(source_) << "failed to parse value for resource " << name
+ << " (" << res_id << ") with configuration '"
+ << config << "'");
+ return false;
+ }
+
+ if (!table_->AddResourceAllowMangled(name, config, {},
+ std::move(resource_value),
+ context_->GetDiagnostics())) {
+ return false;
+ }
+
+ if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
+ Symbol symbol;
+ symbol.state = SymbolState::kPublic;
+ symbol.source = source_.WithLine(0);
+ if (!table_->SetSymbolStateAllowMangled(name, res_id, symbol,
+ context_->GetDiagnostics())) {
return false;
+ }
}
- if (mKeyPool.getError() != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "missing key string pool");
- return false;
+ // Add this resource name->id mapping to the index so
+ // that we can resolve all ID references to name references.
+ auto cache_iter = id_index_.find(res_id);
+ if (cache_iter == id_index_.end()) {
+ id_index_.insert({res_id, name});
}
-
- const ResTable_type* type = convertTo<ResTable_type>(chunk);
- if (!type) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt ResTable_type chunk");
- return false;
- }
-
- if (type->id == 0) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "ResTable_type has invalid id: " << (int) type->id);
- return false;
- }
-
- ConfigDescription config;
- config.copyFromDtoH(type->config);
-
- const std::string typeStr = util::getString(mTypePool, type->id - 1);
-
- const ResourceType* parsedType = parseResourceType(typeStr);
- if (!parsedType) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "invalid type name '" << typeStr
- << "' for type with ID " << (int) type->id);
- return false;
- }
-
- TypeVariant tv(type);
- for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
- const ResTable_entry* entry = *it;
- if (!entry) {
- continue;
- }
-
- const ResourceName name(package->name, *parsedType,
- util::getString(mKeyPool,
- util::deviceToHost32(entry->key.index)));
-
- const ResourceId resId(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
-
- std::unique_ptr<Value> resourceValue;
- if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
- const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
-
- // TODO(adamlesinski): Check that the entry count is valid.
- resourceValue = parseMapEntry(name, config, mapEntry);
- } else {
- const Res_value* value = (const Res_value*)(
- (const uint8_t*) entry + util::deviceToHost32(entry->size));
- resourceValue = parseValue(name, config, value, entry->flags);
- }
-
- if (!resourceValue) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "failed to parse value for resource " << name
- << " (" << resId << ") with configuration '"
- << config << "'");
- return false;
- }
-
- if (!mTable->addResourceAllowMangled(name, config, {}, std::move(resourceValue),
- mContext->getDiagnostics())) {
- return false;
- }
-
- if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
- Symbol symbol;
- symbol.state = SymbolState::kPublic;
- symbol.source = mSource.withLine(0);
- if (!mTable->setSymbolStateAllowMangled(name, resId, symbol,
- mContext->getDiagnostics())) {
- return false;
- }
- }
-
- // Add this resource name->id mapping to the index so
- // that we can resolve all ID references to name references.
- auto cacheIter = mIdIndex.find(resId);
- if (cacheIter == mIdIndex.end()) {
- mIdIndex.insert({ resId, name });
- }
- }
- return true;
+ }
+ return true;
}
-std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Res_value* value,
- uint16_t flags) {
- if (name.type == ResourceType::kId) {
- return util::make_unique<Id>();
+std::unique_ptr<Item> BinaryResourceParser::ParseValue(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const Res_value* value, uint16_t flags) {
+ if (name.type == ResourceType::kId) {
+ return util::make_unique<Id>();
+ }
+
+ const uint32_t data = util::DeviceToHost32(value->data);
+
+ if (value->dataType == Res_value::TYPE_STRING) {
+ const std::string str = util::GetString(value_pool_, data);
+
+ const ResStringPool_span* spans = value_pool_.styleAt(data);
+
+ // Check if the string has a valid style associated with it.
+ if (spans != nullptr && spans->name.index != ResStringPool_span::END) {
+ StyleString style_str = {str};
+ while (spans->name.index != ResStringPool_span::END) {
+ style_str.spans.push_back(
+ Span{util::GetString(value_pool_, spans->name.index),
+ spans->firstChar, spans->lastChar});
+ spans++;
+ }
+ return util::make_unique<StyledString>(table_->string_pool.MakeRef(
+ style_str,
+ StringPool::Context(StringPool::Context::kStylePriority, config)));
+ } else {
+ if (name.type != ResourceType::kString && util::StartsWith(str, "res/")) {
+ // This must be a FileReference.
+ return util::make_unique<FileReference>(table_->string_pool.MakeRef(
+ str,
+ StringPool::Context(StringPool::Context::kHighPriority, config)));
+ }
+
+ // There are no styles associated with this string, so treat it as
+ // a simple string.
+ return util::make_unique<String>(
+ table_->string_pool.MakeRef(str, StringPool::Context(config)));
+ }
+ }
+
+ if (value->dataType == Res_value::TYPE_REFERENCE ||
+ value->dataType == Res_value::TYPE_ATTRIBUTE) {
+ const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE)
+ ? Reference::Type::kResource
+ : Reference::Type::kAttribute;
+
+ if (data == 0) {
+ // A reference of 0, must be the magic @null reference.
+ Res_value null_type = {};
+ null_type.dataType = Res_value::TYPE_REFERENCE;
+ return util::make_unique<BinaryPrimitive>(null_type);
}
- const uint32_t data = util::deviceToHost32(value->data);
+ // This is a normal reference.
+ return util::make_unique<Reference>(data, type);
+ }
- if (value->dataType == Res_value::TYPE_STRING) {
- const std::string str = util::getString(mValuePool, data);
-
- const ResStringPool_span* spans = mValuePool.styleAt(data);
-
- // Check if the string has a valid style associated with it.
- if (spans != nullptr && spans->name.index != ResStringPool_span::END) {
- StyleString styleStr = { str };
- while (spans->name.index != ResStringPool_span::END) {
- styleStr.spans.push_back(Span{
- util::getString(mValuePool, spans->name.index),
- spans->firstChar,
- spans->lastChar
- });
- spans++;
- }
- return util::make_unique<StyledString>(mTable->stringPool.makeRef(
- styleStr, StringPool::Context{1, config}));
- } else {
- if (name.type != ResourceType::kString &&
- util::stringStartsWith(str, "res/")) {
- // This must be a FileReference.
- return util::make_unique<FileReference>(mTable->stringPool.makeRef(
- str, StringPool::Context{ 0, config }));
- }
-
- // There are no styles associated with this string, so treat it as
- // a simple string.
- return util::make_unique<String>(mTable->stringPool.makeRef(
- str, StringPool::Context{1, config}));
- }
- }
-
- if (value->dataType == Res_value::TYPE_REFERENCE ||
- value->dataType == Res_value::TYPE_ATTRIBUTE) {
- const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
- Reference::Type::kResource : Reference::Type::kAttribute;
-
- if (data == 0) {
- // A reference of 0, must be the magic @null reference.
- Res_value nullType = {};
- nullType.dataType = Res_value::TYPE_REFERENCE;
- return util::make_unique<BinaryPrimitive>(nullType);
- }
-
- // This is a normal reference.
- return util::make_unique<Reference>(data, type);
- }
-
- // Treat this as a raw binary primitive.
- return util::make_unique<BinaryPrimitive>(*value);
+ // Treat this as a raw binary primitive.
+ return util::make_unique<BinaryPrimitive>(*value);
}
-std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- switch (name.type) {
- case ResourceType::kStyle:
- return parseStyle(name, config, map);
- case ResourceType::kAttrPrivate:
- // fallthrough
- case ResourceType::kAttr:
- return parseAttr(name, config, map);
- case ResourceType::kArray:
- return parseArray(name, config, map);
- case ResourceType::kPlurals:
- return parsePlural(name, config, map);
- default:
- assert(false && "unknown map type");
- break;
- }
- return {};
+std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ switch (name.type) {
+ case ResourceType::kStyle:
+ return ParseStyle(name, config, map);
+ case ResourceType::kAttrPrivate:
+ // fallthrough
+ case ResourceType::kAttr:
+ return ParseAttr(name, config, map);
+ case ResourceType::kArray:
+ return ParseArray(name, config, map);
+ case ResourceType::kPlurals:
+ return ParsePlural(name, config, map);
+ default:
+ LOG(FATAL) << "unknown map type";
+ break;
+ }
+ return {};
}
-std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- std::unique_ptr<Style> style = util::make_unique<Style>();
- if (util::deviceToHost32(map->parent.ident) != 0) {
- // The parent is a regular reference to a resource.
- style->parent = Reference(util::deviceToHost32(map->parent.ident));
+std::unique_ptr<Style> BinaryResourceParser::ParseStyle(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Style> style = util::make_unique<Style>();
+ if (util::DeviceToHost32(map->parent.ident) != 0) {
+ // The parent is a regular reference to a resource.
+ style->parent = Reference(util::DeviceToHost32(map->parent.ident));
+ }
+
+ for (const ResTable_map& map_entry : map) {
+ if (Res_INTERNALID(util::DeviceToHost32(map_entry.name.ident))) {
+ continue;
}
- for (const ResTable_map& mapEntry : map) {
- if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
- continue;
- }
-
- Style::Entry styleEntry;
- styleEntry.key = Reference(util::deviceToHost32(mapEntry.name.ident));
- styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
- if (!styleEntry.value) {
- return {};
- }
- style->entries.push_back(std::move(styleEntry));
+ Style::Entry style_entry;
+ style_entry.key = Reference(util::DeviceToHost32(map_entry.name.ident));
+ style_entry.value = ParseValue(name, config, &map_entry.value, 0);
+ if (!style_entry.value) {
+ return {};
}
- return style;
+ style->entries.push_back(std::move(style_entry));
+ }
+ return style;
}
-std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- const bool isWeak = (util::deviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
+std::unique_ptr<Attribute> BinaryResourceParser::ParseAttr(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ const bool is_weak =
+ (util::DeviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(is_weak);
- // First we must discover what type of attribute this is. Find the type mask.
- auto typeMaskIter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
- return util::deviceToHost32(entry.name.ident) == ResTable_map::ATTR_TYPE;
- });
+ // First we must discover what type of attribute this is. Find the type mask.
+ auto type_mask_iter =
+ std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
+ return util::DeviceToHost32(entry.name.ident) ==
+ ResTable_map::ATTR_TYPE;
+ });
- if (typeMaskIter != end(map)) {
- attr->typeMask = util::deviceToHost32(typeMaskIter->value.data);
+ if (type_mask_iter != end(map)) {
+ attr->type_mask = util::DeviceToHost32(type_mask_iter->value.data);
+ }
+
+ for (const ResTable_map& map_entry : map) {
+ if (Res_INTERNALID(util::DeviceToHost32(map_entry.name.ident))) {
+ switch (util::DeviceToHost32(map_entry.name.ident)) {
+ case ResTable_map::ATTR_MIN:
+ attr->min_int = static_cast<int32_t>(map_entry.value.data);
+ break;
+ case ResTable_map::ATTR_MAX:
+ attr->max_int = static_cast<int32_t>(map_entry.value.data);
+ break;
+ }
+ continue;
}
- for (const ResTable_map& mapEntry : map) {
- if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
- switch (util::deviceToHost32(mapEntry.name.ident)) {
- case ResTable_map::ATTR_MIN:
- attr->minInt = static_cast<int32_t>(mapEntry.value.data);
- break;
- case ResTable_map::ATTR_MAX:
- attr->maxInt = static_cast<int32_t>(mapEntry.value.data);
- break;
- }
- continue;
- }
-
- if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
- Attribute::Symbol symbol;
- symbol.value = util::deviceToHost32(mapEntry.value.data);
- symbol.symbol = Reference(util::deviceToHost32(mapEntry.name.ident));
- attr->symbols.push_back(std::move(symbol));
- }
+ if (attr->type_mask &
+ (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
+ Attribute::Symbol symbol;
+ symbol.value = util::DeviceToHost32(map_entry.value.data);
+ symbol.symbol = Reference(util::DeviceToHost32(map_entry.name.ident));
+ attr->symbols.push_back(std::move(symbol));
}
+ }
- // TODO(adamlesinski): Find i80n, attributes.
- return attr;
+ // TODO(adamlesinski): Find i80n, attributes.
+ return attr;
}
-std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- std::unique_ptr<Array> array = util::make_unique<Array>();
- for (const ResTable_map& mapEntry : map) {
- array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
- }
- return array;
+std::unique_ptr<Array> BinaryResourceParser::ParseArray(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Array> array = util::make_unique<Array>();
+ for (const ResTable_map& map_entry : map) {
+ array->items.push_back(ParseValue(name, config, &map_entry.value, 0));
+ }
+ return array;
}
-std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- for (const ResTable_map& mapEntry : map) {
- std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
- if (!item) {
- return {};
- }
-
- switch (util::deviceToHost32(mapEntry.name.ident)) {
- case ResTable_map::ATTR_ZERO:
- plural->values[Plural::Zero] = std::move(item);
- break;
- case ResTable_map::ATTR_ONE:
- plural->values[Plural::One] = std::move(item);
- break;
- case ResTable_map::ATTR_TWO:
- plural->values[Plural::Two] = std::move(item);
- break;
- case ResTable_map::ATTR_FEW:
- plural->values[Plural::Few] = std::move(item);
- break;
- case ResTable_map::ATTR_MANY:
- plural->values[Plural::Many] = std::move(item);
- break;
- case ResTable_map::ATTR_OTHER:
- plural->values[Plural::Other] = std::move(item);
- break;
- }
+std::unique_ptr<Plural> BinaryResourceParser::ParsePlural(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ for (const ResTable_map& map_entry : map) {
+ std::unique_ptr<Item> item = ParseValue(name, config, &map_entry.value, 0);
+ if (!item) {
+ return {};
}
- return plural;
+
+ switch (util::DeviceToHost32(map_entry.name.ident)) {
+ case ResTable_map::ATTR_ZERO:
+ plural->values[Plural::Zero] = std::move(item);
+ break;
+ case ResTable_map::ATTR_ONE:
+ plural->values[Plural::One] = std::move(item);
+ break;
+ case ResTable_map::ATTR_TWO:
+ plural->values[Plural::Two] = std::move(item);
+ break;
+ case ResTable_map::ATTR_FEW:
+ plural->values[Plural::Few] = std::move(item);
+ break;
+ case ResTable_map::ATTR_MANY:
+ plural->values[Plural::Many] = std::move(item);
+ break;
+ case ResTable_map::ATTR_OTHER:
+ plural->values[Plural::Other] = std::move(item);
+ break;
+ }
+ }
+ return plural;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/unflatten/BinaryResourceParser.h
index 12bc13d..dc668fd 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.h
+++ b/tools/aapt2/unflatten/BinaryResourceParser.h
@@ -17,16 +17,17 @@
#ifndef AAPT_BINARY_RESOURCE_PARSER_H
#define AAPT_BINARY_RESOURCE_PARSER_H
+#include <string>
+
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "Source.h"
-
#include "process/IResourceTableConsumer.h"
#include "util/Util.h"
-#include <androidfw/ResourceTypes.h>
-#include <string>
-
namespace aapt {
struct SymbolTable_entry;
@@ -39,80 +40,86 @@
* chunks and types.
*/
class BinaryResourceParser {
-public:
- /*
- * Creates a parser, which will read `len` bytes from `data`, and
- * add any resources parsed to `table`. `source` is for logging purposes.
- */
- BinaryResourceParser(IAaptContext* context, ResourceTable* table, const Source& source,
- const void* data, size_t dataLen);
+ public:
+ /*
+ * Creates a parser, which will read `len` bytes from `data`, and
+ * add any resources parsed to `table`. `source` is for logging purposes.
+ */
+ BinaryResourceParser(IAaptContext* context, ResourceTable* table,
+ const Source& source, const void* data, size_t data_len);
- BinaryResourceParser(const BinaryResourceParser&) = delete; // No copy.
+ /*
+ * Parses the binary resource table and returns true if successful.
+ */
+ bool Parse();
- /*
- * Parses the binary resource table and returns true if successful.
- */
- bool parse();
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BinaryResourceParser);
-private:
- bool parseTable(const android::ResChunk_header* chunk);
- bool parsePackage(const android::ResChunk_header* chunk);
- bool parseTypeSpec(const android::ResChunk_header* chunk);
- bool parseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
+ bool ParseTable(const android::ResChunk_header* chunk);
+ bool ParsePackage(const android::ResChunk_header* chunk);
+ bool ParseTypeSpec(const android::ResChunk_header* chunk);
+ bool ParseType(const ResourceTablePackage* package,
+ const android::ResChunk_header* chunk);
- std::unique_ptr<Item> parseValue(const ResourceNameRef& name, const ConfigDescription& config,
- const android::Res_value* value, uint16_t flags);
+ std::unique_ptr<Item> ParseValue(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::Res_value* value,
+ uint16_t flags);
- std::unique_ptr<Value> parseMapEntry(const ResourceNameRef& name,
- const ConfigDescription& config,
- const android::ResTable_map_entry* map);
+ std::unique_ptr<Value> ParseMapEntry(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
- std::unique_ptr<Style> parseStyle(const ResourceNameRef& name, const ConfigDescription& config,
+ std::unique_ptr<Style> ParseStyle(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ std::unique_ptr<Attribute> ParseAttr(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ std::unique_ptr<Array> ParseArray(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ std::unique_ptr<Plural> ParsePlural(const ResourceNameRef& name,
+ const ConfigDescription& config,
const android::ResTable_map_entry* map);
- std::unique_ptr<Attribute> parseAttr(const ResourceNameRef& name,
- const ConfigDescription& config,
- const android::ResTable_map_entry* map);
+ /**
+ * If the mapEntry is a special type that denotes meta data (source, comment),
+ * then it is
+ * read and added to the Value.
+ * Returns true if the mapEntry was meta data.
+ */
+ bool CollectMetaData(const android::ResTable_map& map_entry, Value* value);
- std::unique_ptr<Array> parseArray(const ResourceNameRef& name, const ConfigDescription& config,
- const android::ResTable_map_entry* map);
+ IAaptContext* context_;
+ ResourceTable* table_;
- std::unique_ptr<Plural> parsePlural(const ResourceNameRef& name,
- const ConfigDescription& config,
- const android::ResTable_map_entry* map);
+ const Source source_;
- /**
- * If the mapEntry is a special type that denotes meta data (source, comment), then it is
- * read and added to the Value.
- * Returns true if the mapEntry was meta data.
- */
- bool collectMetaData(const android::ResTable_map& mapEntry, Value* value);
+ const void* data_;
+ const size_t data_len_;
- IAaptContext* mContext;
- ResourceTable* mTable;
+ // The standard value string pool for resource values.
+ android::ResStringPool value_pool_;
- const Source mSource;
+ // The string pool that holds the names of the types defined
+ // in this table.
+ android::ResStringPool type_pool_;
- const void* mData;
- const size_t mDataLen;
+ // The string pool that holds the names of the entries defined
+ // in this table.
+ android::ResStringPool key_pool_;
- // The standard value string pool for resource values.
- android::ResStringPool mValuePool;
-
- // The string pool that holds the names of the types defined
- // in this table.
- android::ResStringPool mTypePool;
-
- // The string pool that holds the names of the entries defined
- // in this table.
- android::ResStringPool mKeyPool;
-
- // A mapping of resource ID to resource name. When we finish parsing
- // we use this to convert all resource IDs to symbolic references.
- std::map<ResourceId, ResourceName> mIdIndex;
+ // A mapping of resource ID to resource name. When we finish parsing
+ // we use this to convert all resource IDs to symbolic references.
+ std::map<ResourceId, ResourceName> id_index_;
};
-} // namespace aapt
+} // namespace aapt
namespace android {
@@ -121,13 +128,14 @@
*/
inline const ResTable_map* begin(const ResTable_map_entry* map) {
- return (const ResTable_map*)((const uint8_t*) map + aapt::util::deviceToHost32(map->size));
+ return (const ResTable_map*)((const uint8_t*)map +
+ aapt::util::DeviceToHost32(map->size));
}
inline const ResTable_map* end(const ResTable_map_entry* map) {
- return begin(map) + aapt::util::deviceToHost32(map->count);
+ return begin(map) + aapt::util::DeviceToHost32(map->count);
}
-} // namespace android
+} // namespace android
-#endif // AAPT_BINARY_RESOURCE_PARSER_H
+#endif // AAPT_BINARY_RESOURCE_PARSER_H
diff --git a/tools/aapt2/unflatten/ResChunkPullParser.cpp b/tools/aapt2/unflatten/ResChunkPullParser.cpp
index 6f8bb1b..5d71ff3 100644
--- a/tools/aapt2/unflatten/ResChunkPullParser.cpp
+++ b/tools/aapt2/unflatten/ResChunkPullParser.cpp
@@ -15,55 +15,60 @@
*/
#include "unflatten/ResChunkPullParser.h"
-#include "util/Util.h"
-#include <androidfw/ResourceTypes.h>
#include <cstddef>
+#include "android-base/logging.h"
+#include "androidfw/ResourceTypes.h"
+
+#include "util/Util.h"
+
namespace aapt {
using android::ResChunk_header;
-ResChunkPullParser::Event ResChunkPullParser::next() {
- if (!isGoodEvent(mEvent)) {
- return mEvent;
- }
+ResChunkPullParser::Event ResChunkPullParser::Next() {
+ if (!IsGoodEvent(event_)) {
+ return event_;
+ }
- if (mEvent == Event::StartDocument) {
- mCurrentChunk = mData;
- } else {
- mCurrentChunk = (const ResChunk_header*)
- (((const char*) mCurrentChunk) + util::deviceToHost32(mCurrentChunk->size));
- }
+ if (event_ == Event::kStartDocument) {
+ current_chunk_ = data_;
+ } else {
+ current_chunk_ =
+ (const ResChunk_header*)(((const char*)current_chunk_) +
+ util::DeviceToHost32(current_chunk_->size));
+ }
- const std::ptrdiff_t diff = (const char*) mCurrentChunk - (const char*) mData;
- assert(diff >= 0 && "diff is negative");
- const size_t offset = static_cast<const size_t>(diff);
+ const std::ptrdiff_t diff = (const char*)current_chunk_ - (const char*)data_;
+ CHECK(diff >= 0) << "diff is negative";
+ const size_t offset = static_cast<const size_t>(diff);
- if (offset == mLen) {
- mCurrentChunk = nullptr;
- return (mEvent = Event::EndDocument);
- } else if (offset + sizeof(ResChunk_header) > mLen) {
- mLastError = "chunk is past the end of the document";
- mCurrentChunk = nullptr;
- return (mEvent = Event::BadDocument);
- }
+ if (offset == len_) {
+ current_chunk_ = nullptr;
+ return (event_ = Event::kEndDocument);
+ } else if (offset + sizeof(ResChunk_header) > len_) {
+ error_ = "chunk is past the end of the document";
+ current_chunk_ = nullptr;
+ return (event_ = Event::kBadDocument);
+ }
- if (util::deviceToHost16(mCurrentChunk->headerSize) < sizeof(ResChunk_header)) {
- mLastError = "chunk has too small header";
- mCurrentChunk = nullptr;
- return (mEvent = Event::BadDocument);
- } else if (util::deviceToHost32(mCurrentChunk->size) <
- util::deviceToHost16(mCurrentChunk->headerSize)) {
- mLastError = "chunk's total size is smaller than header";
- mCurrentChunk = nullptr;
- return (mEvent = Event::BadDocument);
- } else if (offset + util::deviceToHost32(mCurrentChunk->size) > mLen) {
- mLastError = "chunk's data extends past the end of the document";
- mCurrentChunk = nullptr;
- return (mEvent = Event::BadDocument);
- }
- return (mEvent = Event::Chunk);
+ if (util::DeviceToHost16(current_chunk_->headerSize) <
+ sizeof(ResChunk_header)) {
+ error_ = "chunk has too small header";
+ current_chunk_ = nullptr;
+ return (event_ = Event::kBadDocument);
+ } else if (util::DeviceToHost32(current_chunk_->size) <
+ util::DeviceToHost16(current_chunk_->headerSize)) {
+ error_ = "chunk's total size is smaller than header";
+ current_chunk_ = nullptr;
+ return (event_ = Event::kBadDocument);
+ } else if (offset + util::DeviceToHost32(current_chunk_->size) > len_) {
+ error_ = "chunk's data extends past the end of the document";
+ current_chunk_ = nullptr;
+ return (event_ = Event::kBadDocument);
+ }
+ return (event_ = Event::kChunk);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/unflatten/ResChunkPullParser.h b/tools/aapt2/unflatten/ResChunkPullParser.h
index a51d5bf..437fc5c 100644
--- a/tools/aapt2/unflatten/ResChunkPullParser.h
+++ b/tools/aapt2/unflatten/ResChunkPullParser.h
@@ -17,11 +17,13 @@
#ifndef AAPT_RES_CHUNK_PULL_PARSER_H
#define AAPT_RES_CHUNK_PULL_PARSER_H
-#include "util/Util.h"
-
-#include <androidfw/ResourceTypes.h>
#include <string>
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+
+#include "util/Util.h"
+
namespace aapt {
/**
@@ -37,88 +39,88 @@
* pointing to the data portion of a chunk.
*/
class ResChunkPullParser {
-public:
- enum class Event {
- StartDocument,
- EndDocument,
- BadDocument,
+ public:
+ enum class Event {
+ kStartDocument,
+ kEndDocument,
+ kBadDocument,
- Chunk,
- };
+ kChunk,
+ };
- /**
- * Returns false if the event is EndDocument or BadDocument.
- */
- static bool isGoodEvent(Event event);
+ /**
+ * Returns false if the event is EndDocument or BadDocument.
+ */
+ static bool IsGoodEvent(Event event);
- /**
- * Create a ResChunkPullParser to read android::ResChunk_headers
- * from the memory pointed to by data, of len bytes.
- */
- ResChunkPullParser(const void* data, size_t len);
+ /**
+ * Create a ResChunkPullParser to read android::ResChunk_headers
+ * from the memory pointed to by data, of len bytes.
+ */
+ ResChunkPullParser(const void* data, size_t len);
- ResChunkPullParser(const ResChunkPullParser&) = delete;
+ Event event() const;
+ const std::string& error() const;
+ const android::ResChunk_header* chunk() const;
- Event getEvent() const;
- const std::string& getLastError() const;
- const android::ResChunk_header* getChunk() const;
+ /**
+ * Move to the next android::ResChunk_header.
+ */
+ Event Next();
- /**
- * Move to the next android::ResChunk_header.
- */
- Event next();
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResChunkPullParser);
-private:
- Event mEvent;
- const android::ResChunk_header* mData;
- size_t mLen;
- const android::ResChunk_header* mCurrentChunk;
- std::string mLastError;
+ Event event_;
+ const android::ResChunk_header* data_;
+ size_t len_;
+ const android::ResChunk_header* current_chunk_;
+ std::string error_;
};
template <typename T>
-inline static const T* convertTo(const android::ResChunk_header* chunk) {
- if (util::deviceToHost16(chunk->headerSize) < sizeof(T)) {
- return nullptr;
- }
- return reinterpret_cast<const T*>(chunk);
+inline static const T* ConvertTo(const android::ResChunk_header* chunk) {
+ if (util::DeviceToHost16(chunk->headerSize) < sizeof(T)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const T*>(chunk);
}
-inline static const uint8_t* getChunkData(const android::ResChunk_header* chunk) {
- return reinterpret_cast<const uint8_t*>(chunk) + util::deviceToHost16(chunk->headerSize);
+inline static const uint8_t* GetChunkData(
+ const android::ResChunk_header* chunk) {
+ return reinterpret_cast<const uint8_t*>(chunk) +
+ util::DeviceToHost16(chunk->headerSize);
}
-inline static uint32_t getChunkDataLen(const android::ResChunk_header* chunk) {
- return util::deviceToHost32(chunk->size) - util::deviceToHost16(chunk->headerSize);
+inline static uint32_t GetChunkDataLen(const android::ResChunk_header* chunk) {
+ return util::DeviceToHost32(chunk->size) -
+ util::DeviceToHost16(chunk->headerSize);
}
//
// Implementation
//
-inline bool ResChunkPullParser::isGoodEvent(ResChunkPullParser::Event event) {
- return event != Event::EndDocument && event != Event::BadDocument;
+inline bool ResChunkPullParser::IsGoodEvent(ResChunkPullParser::Event event) {
+ return event != Event::kEndDocument && event != Event::kBadDocument;
}
-inline ResChunkPullParser::ResChunkPullParser(const void* data, size_t len) :
- mEvent(Event::StartDocument),
- mData(reinterpret_cast<const android::ResChunk_header*>(data)),
- mLen(len),
- mCurrentChunk(nullptr) {
+inline ResChunkPullParser::ResChunkPullParser(const void* data, size_t len)
+ : event_(Event::kStartDocument),
+ data_(reinterpret_cast<const android::ResChunk_header*>(data)),
+ len_(len),
+ current_chunk_(nullptr) {}
+
+inline ResChunkPullParser::Event ResChunkPullParser::event() const {
+ return event_;
}
-inline ResChunkPullParser::Event ResChunkPullParser::getEvent() const {
- return mEvent;
+inline const std::string& ResChunkPullParser::error() const { return error_; }
+
+inline const android::ResChunk_header* ResChunkPullParser::chunk() const {
+ return current_chunk_;
}
-inline const std::string& ResChunkPullParser::getLastError() const {
- return mLastError;
-}
+} // namespace aapt
-inline const android::ResChunk_header* ResChunkPullParser::getChunk() const {
- return mCurrentChunk;
-}
-
-} // namespace aapt
-
-#endif // AAPT_RES_CHUNK_PULL_PARSER_H
+#endif // AAPT_RES_CHUNK_PULL_PARSER_H
diff --git a/tools/aapt2/util/BigBuffer.cpp b/tools/aapt2/util/BigBuffer.cpp
index de4ecd2..ef99dca 100644
--- a/tools/aapt2/util/BigBuffer.cpp
+++ b/tools/aapt2/util/BigBuffer.cpp
@@ -20,58 +20,60 @@
#include <memory>
#include <vector>
+#include "android-base/logging.h"
+
namespace aapt {
-void* BigBuffer::nextBlockImpl(size_t size) {
- if (!mBlocks.empty()) {
- Block& block = mBlocks.back();
- if (block.mBlockSize - block.size >= size) {
- void* outBuffer = block.buffer.get() + block.size;
- block.size += size;
- mSize += size;
- return outBuffer;
- }
+void* BigBuffer::NextBlockImpl(size_t size) {
+ if (!blocks_.empty()) {
+ Block& block = blocks_.back();
+ if (block.block_size_ - block.size >= size) {
+ void* out_buffer = block.buffer.get() + block.size;
+ block.size += size;
+ size_ += size;
+ return out_buffer;
}
+ }
- const size_t actualSize = std::max(mBlockSize, size);
+ const size_t actual_size = std::max(block_size_, size);
- Block block = {};
+ Block block = {};
- // Zero-allocate the block's buffer.
- block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[actualSize]());
- assert(block.buffer);
+ // Zero-allocate the block's buffer.
+ block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[actual_size]());
+ CHECK(block.buffer);
- block.size = size;
- block.mBlockSize = actualSize;
+ block.size = size;
+ block.block_size_ = actual_size;
- mBlocks.push_back(std::move(block));
- mSize += size;
- return mBlocks.back().buffer.get();
+ blocks_.push_back(std::move(block));
+ size_ += size;
+ return blocks_.back().buffer.get();
}
-void* BigBuffer::nextBlock(size_t* outSize) {
- if (!mBlocks.empty()) {
- Block& block = mBlocks.back();
- if (block.size != block.mBlockSize) {
- void* outBuffer = block.buffer.get() + block.size;
- size_t size = block.mBlockSize - block.size;
- block.size = block.mBlockSize;
- mSize += size;
- *outSize = size;
- return outBuffer;
- }
+void* BigBuffer::NextBlock(size_t* out_size) {
+ if (!blocks_.empty()) {
+ Block& block = blocks_.back();
+ if (block.size != block.block_size_) {
+ void* out_buffer = block.buffer.get() + block.size;
+ size_t size = block.block_size_ - block.size;
+ block.size = block.block_size_;
+ size_ += size;
+ *out_size = size;
+ return out_buffer;
}
+ }
- // Zero-allocate the block's buffer.
- Block block = {};
- block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[mBlockSize]());
- assert(block.buffer);
- block.size = mBlockSize;
- block.mBlockSize = mBlockSize;
- mBlocks.push_back(std::move(block));
- mSize += mBlockSize;
- *outSize = mBlockSize;
- return mBlocks.back().buffer.get();
+ // Zero-allocate the block's buffer.
+ Block block = {};
+ block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[block_size_]());
+ CHECK(block.buffer);
+ block.size = block_size_;
+ block.block_size_ = block_size_;
+ blocks_.push_back(std::move(block));
+ size_ += block_size_;
+ *out_size = block_size_;
+ return blocks_.back().buffer.get();
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h
index 685614f..d23c41d 100644
--- a/tools/aapt2/util/BigBuffer.h
+++ b/tools/aapt2/util/BigBuffer.h
@@ -17,12 +17,14 @@
#ifndef AAPT_BIG_BUFFER_H
#define AAPT_BIG_BUFFER_H
-#include <cassert>
#include <cstring>
#include <memory>
#include <type_traits>
#include <vector>
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+
namespace aapt {
/**
@@ -32,156 +34,153 @@
* block is allocated and appended to the end of the list.
*/
class BigBuffer {
-public:
+ public:
+ /**
+ * A contiguous block of allocated memory.
+ */
+ struct Block {
/**
- * A contiguous block of allocated memory.
+ * Pointer to the memory.
*/
- struct Block {
- /**
- * Pointer to the memory.
- */
- std::unique_ptr<uint8_t[]> buffer;
-
- /**
- * Size of memory that is currently occupied. The actual
- * allocation may be larger.
- */
- size_t size;
-
- private:
- friend class BigBuffer;
-
- /**
- * The size of the memory block allocation.
- */
- size_t mBlockSize;
- };
-
- typedef std::vector<Block>::const_iterator const_iterator;
+ std::unique_ptr<uint8_t[]> buffer;
/**
- * Create a BigBuffer with block allocation sizes
- * of blockSize.
+ * Size of memory that is currently occupied. The actual
+ * allocation may be larger.
*/
- explicit BigBuffer(size_t blockSize);
+ size_t size;
- BigBuffer(const BigBuffer&) = delete; // No copying.
-
- BigBuffer(BigBuffer&& rhs);
+ private:
+ friend class BigBuffer;
/**
- * Number of occupied bytes in all the allocated blocks.
+ * The size of the memory block allocation.
*/
- size_t size() const;
+ size_t block_size_;
+ };
- /**
- * Returns a pointer to an array of T, where T is
- * a POD type. The elements are zero-initialized.
- */
- template <typename T>
- T* nextBlock(size_t count = 1);
+ typedef std::vector<Block>::const_iterator const_iterator;
- /**
- * Returns the next block available and puts the size in outCount.
- * This is useful for grabbing blocks where the size doesn't matter.
- * Use backUp() to give back any bytes that were not used.
- */
- void* nextBlock(size_t* outCount);
+ /**
+ * Create a BigBuffer with block allocation sizes
+ * of block_size.
+ */
+ explicit BigBuffer(size_t block_size);
- /**
- * Backs up count bytes. This must only be called after nextBlock()
- * and can not be larger than sizeof(T) * count of the last nextBlock()
- * call.
- */
- void backUp(size_t count);
+ BigBuffer(BigBuffer&& rhs);
- /**
- * Moves the specified BigBuffer into this one. When this method
- * returns, buffer is empty.
- */
- void appendBuffer(BigBuffer&& buffer);
+ /**
+ * Number of occupied bytes in all the allocated blocks.
+ */
+ size_t size() const;
- /**
- * Pads the block with 'bytes' bytes of zero values.
- */
- void pad(size_t bytes);
+ /**
+ * Returns a pointer to an array of T, where T is
+ * a POD type. The elements are zero-initialized.
+ */
+ template <typename T>
+ T* NextBlock(size_t count = 1);
- /**
- * Pads the block so that it aligns on a 4 byte boundary.
- */
- void align4();
+ /**
+ * Returns the next block available and puts the size in out_count.
+ * This is useful for grabbing blocks where the size doesn't matter.
+ * Use BackUp() to give back any bytes that were not used.
+ */
+ void* NextBlock(size_t* out_count);
- size_t getBlockSize() const;
+ /**
+ * Backs up count bytes. This must only be called after NextBlock()
+ * and can not be larger than sizeof(T) * count of the last NextBlock()
+ * call.
+ */
+ void BackUp(size_t count);
- const_iterator begin() const;
- const_iterator end() const;
+ /**
+ * Moves the specified BigBuffer into this one. When this method
+ * returns, buffer is empty.
+ */
+ void AppendBuffer(BigBuffer&& buffer);
-private:
- /**
- * Returns a pointer to a buffer of the requested size.
- * The buffer is zero-initialized.
- */
- void* nextBlockImpl(size_t size);
+ /**
+ * Pads the block with 'bytes' bytes of zero values.
+ */
+ void Pad(size_t bytes);
- size_t mBlockSize;
- size_t mSize;
- std::vector<Block> mBlocks;
+ /**
+ * Pads the block so that it aligns on a 4 byte boundary.
+ */
+ void Align4();
+
+ size_t block_size() const;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BigBuffer);
+
+ /**
+ * Returns a pointer to a buffer of the requested size.
+ * The buffer is zero-initialized.
+ */
+ void* NextBlockImpl(size_t size);
+
+ size_t block_size_;
+ size_t size_;
+ std::vector<Block> blocks_;
};
-inline BigBuffer::BigBuffer(size_t blockSize) : mBlockSize(blockSize), mSize(0) {
-}
+inline BigBuffer::BigBuffer(size_t block_size)
+ : block_size_(block_size), size_(0) {}
-inline BigBuffer::BigBuffer(BigBuffer&& rhs) :
- mBlockSize(rhs.mBlockSize), mSize(rhs.mSize), mBlocks(std::move(rhs.mBlocks)) {
-}
+inline BigBuffer::BigBuffer(BigBuffer&& rhs)
+ : block_size_(rhs.block_size_),
+ size_(rhs.size_),
+ blocks_(std::move(rhs.blocks_)) {}
-inline size_t BigBuffer::size() const {
- return mSize;
-}
+inline size_t BigBuffer::size() const { return size_; }
-inline size_t BigBuffer::getBlockSize() const {
- return mBlockSize;
-}
+inline size_t BigBuffer::block_size() const { return block_size_; }
template <typename T>
-inline T* BigBuffer::nextBlock(size_t count) {
- static_assert(std::is_standard_layout<T>::value, "T must be standard_layout type");
- assert(count != 0);
- return reinterpret_cast<T*>(nextBlockImpl(sizeof(T) * count));
+inline T* BigBuffer::NextBlock(size_t count) {
+ static_assert(std::is_standard_layout<T>::value,
+ "T must be standard_layout type");
+ CHECK(count != 0);
+ return reinterpret_cast<T*>(NextBlockImpl(sizeof(T) * count));
}
-inline void BigBuffer::backUp(size_t count) {
- Block& block = mBlocks.back();
- block.size -= count;
- mSize -= count;
+inline void BigBuffer::BackUp(size_t count) {
+ Block& block = blocks_.back();
+ block.size -= count;
+ size_ -= count;
}
-inline void BigBuffer::appendBuffer(BigBuffer&& buffer) {
- std::move(buffer.mBlocks.begin(), buffer.mBlocks.end(), std::back_inserter(mBlocks));
- mSize += buffer.mSize;
- buffer.mBlocks.clear();
- buffer.mSize = 0;
+inline void BigBuffer::AppendBuffer(BigBuffer&& buffer) {
+ std::move(buffer.blocks_.begin(), buffer.blocks_.end(),
+ std::back_inserter(blocks_));
+ size_ += buffer.size_;
+ buffer.blocks_.clear();
+ buffer.size_ = 0;
}
-inline void BigBuffer::pad(size_t bytes) {
- nextBlock<char>(bytes);
-}
+inline void BigBuffer::Pad(size_t bytes) { NextBlock<char>(bytes); }
-inline void BigBuffer::align4() {
- const size_t unaligned = mSize % 4;
- if (unaligned != 0) {
- pad(4 - unaligned);
- }
+inline void BigBuffer::Align4() {
+ const size_t unaligned = size_ % 4;
+ if (unaligned != 0) {
+ Pad(4 - unaligned);
+ }
}
inline BigBuffer::const_iterator BigBuffer::begin() const {
- return mBlocks.begin();
+ return blocks_.begin();
}
inline BigBuffer::const_iterator BigBuffer::end() const {
- return mBlocks.end();
+ return blocks_.end();
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_BIG_BUFFER_H
+#endif // AAPT_BIG_BUFFER_H
diff --git a/tools/aapt2/util/BigBuffer_test.cpp b/tools/aapt2/util/BigBuffer_test.cpp
index 2a24f12..12c0b3e 100644
--- a/tools/aapt2/util/BigBuffer_test.cpp
+++ b/tools/aapt2/util/BigBuffer_test.cpp
@@ -16,83 +16,83 @@
#include "util/BigBuffer.h"
-#include <gtest/gtest.h>
+#include "test/Test.h"
namespace aapt {
TEST(BigBufferTest, AllocateSingleBlock) {
- BigBuffer buffer(4);
+ BigBuffer buffer(4);
- EXPECT_NE(nullptr, buffer.nextBlock<char>(2));
- EXPECT_EQ(2u, buffer.size());
+ EXPECT_NE(nullptr, buffer.NextBlock<char>(2));
+ EXPECT_EQ(2u, buffer.size());
}
TEST(BigBufferTest, ReturnSameBlockIfNextAllocationFits) {
- BigBuffer buffer(16);
+ BigBuffer buffer(16);
- char* b1 = buffer.nextBlock<char>(8);
- EXPECT_NE(nullptr, b1);
+ char* b1 = buffer.NextBlock<char>(8);
+ EXPECT_NE(nullptr, b1);
- char* b2 = buffer.nextBlock<char>(4);
- EXPECT_NE(nullptr, b2);
+ char* b2 = buffer.NextBlock<char>(4);
+ EXPECT_NE(nullptr, b2);
- EXPECT_EQ(b1 + 8, b2);
+ EXPECT_EQ(b1 + 8, b2);
}
TEST(BigBufferTest, AllocateExactSizeBlockIfLargerThanBlockSize) {
- BigBuffer buffer(16);
+ BigBuffer buffer(16);
- EXPECT_NE(nullptr, buffer.nextBlock<char>(32));
- EXPECT_EQ(32u, buffer.size());
+ EXPECT_NE(nullptr, buffer.NextBlock<char>(32));
+ EXPECT_EQ(32u, buffer.size());
}
TEST(BigBufferTest, AppendAndMoveBlock) {
- BigBuffer buffer(16);
+ BigBuffer buffer(16);
- uint32_t* b1 = buffer.nextBlock<uint32_t>();
+ uint32_t* b1 = buffer.NextBlock<uint32_t>();
+ ASSERT_NE(nullptr, b1);
+ *b1 = 33;
+
+ {
+ BigBuffer buffer2(16);
+ b1 = buffer2.NextBlock<uint32_t>();
ASSERT_NE(nullptr, b1);
- *b1 = 33;
+ *b1 = 44;
- {
- BigBuffer buffer2(16);
- b1 = buffer2.nextBlock<uint32_t>();
- ASSERT_NE(nullptr, b1);
- *b1 = 44;
+ buffer.AppendBuffer(std::move(buffer2));
+ EXPECT_EQ(0u, buffer2.size());
+ EXPECT_EQ(buffer2.begin(), buffer2.end());
+ }
- buffer.appendBuffer(std::move(buffer2));
- EXPECT_EQ(0u, buffer2.size());
- EXPECT_EQ(buffer2.begin(), buffer2.end());
- }
+ EXPECT_EQ(2 * sizeof(uint32_t), buffer.size());
- EXPECT_EQ(2 * sizeof(uint32_t), buffer.size());
+ auto b = buffer.begin();
+ ASSERT_NE(b, buffer.end());
+ ASSERT_EQ(sizeof(uint32_t), b->size);
+ ASSERT_EQ(33u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
+ ++b;
- auto b = buffer.begin();
- ASSERT_NE(b, buffer.end());
- ASSERT_EQ(sizeof(uint32_t), b->size);
- ASSERT_EQ(33u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
- ++b;
+ ASSERT_NE(b, buffer.end());
+ ASSERT_EQ(sizeof(uint32_t), b->size);
+ ASSERT_EQ(44u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
+ ++b;
- ASSERT_NE(b, buffer.end());
- ASSERT_EQ(sizeof(uint32_t), b->size);
- ASSERT_EQ(44u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
- ++b;
-
- ASSERT_EQ(b, buffer.end());
+ ASSERT_EQ(b, buffer.end());
}
TEST(BigBufferTest, PadAndAlignProperly) {
- BigBuffer buffer(16);
+ BigBuffer buffer(16);
- ASSERT_NE(buffer.nextBlock<char>(2), nullptr);
- ASSERT_EQ(2u, buffer.size());
- buffer.pad(2);
- ASSERT_EQ(4u, buffer.size());
- buffer.align4();
- ASSERT_EQ(4u, buffer.size());
- buffer.pad(2);
- ASSERT_EQ(6u, buffer.size());
- buffer.align4();
- ASSERT_EQ(8u, buffer.size());
+ ASSERT_NE(buffer.NextBlock<char>(2), nullptr);
+ ASSERT_EQ(2u, buffer.size());
+ buffer.Pad(2);
+ ASSERT_EQ(4u, buffer.size());
+ buffer.Align4();
+ ASSERT_EQ(4u, buffer.size());
+ buffer.Pad(2);
+ ASSERT_EQ(6u, buffer.size());
+ buffer.Align4();
+ ASSERT_EQ(8u, buffer.size());
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index c9b3811..f034607 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -15,15 +15,20 @@
*/
#include "util/Files.h"
-#include "util/Util.h"
+
+#include <dirent.h>
+#include <sys/stat.h>
#include <algorithm>
-#include <android-base/file.h>
#include <cerrno>
#include <cstdio>
-#include <dirent.h>
#include <string>
-#include <sys/stat.h>
+
+#include "android-base/errors.h"
+#include "android-base/file.h"
+#include "android-base/logging.h"
+
+#include "util/Util.h"
#ifdef _WIN32
// Windows includes.
@@ -33,244 +38,230 @@
namespace aapt {
namespace file {
-FileType getFileType(const StringPiece& path) {
- struct stat sb;
- if (stat(path.data(), &sb) < 0) {
- if (errno == ENOENT || errno == ENOTDIR) {
- return FileType::kNonexistant;
- }
- return FileType::kUnknown;
+FileType GetFileType(const StringPiece& path) {
+ struct stat sb;
+ if (stat(path.data(), &sb) < 0) {
+ if (errno == ENOENT || errno == ENOTDIR) {
+ return FileType::kNonexistant;
}
+ return FileType::kUnknown;
+ }
- if (S_ISREG(sb.st_mode)) {
- return FileType::kRegular;
- } else if (S_ISDIR(sb.st_mode)) {
- return FileType::kDirectory;
- } else if (S_ISCHR(sb.st_mode)) {
- return FileType::kCharDev;
- } else if (S_ISBLK(sb.st_mode)) {
- return FileType::kBlockDev;
- } else if (S_ISFIFO(sb.st_mode)) {
- return FileType::kFifo;
+ if (S_ISREG(sb.st_mode)) {
+ return FileType::kRegular;
+ } else if (S_ISDIR(sb.st_mode)) {
+ return FileType::kDirectory;
+ } else if (S_ISCHR(sb.st_mode)) {
+ return FileType::kCharDev;
+ } else if (S_ISBLK(sb.st_mode)) {
+ return FileType::kBlockDev;
+ } else if (S_ISFIFO(sb.st_mode)) {
+ return FileType::kFifo;
#if defined(S_ISLNK)
- } else if (S_ISLNK(sb.st_mode)) {
- return FileType::kSymlink;
+ } else if (S_ISLNK(sb.st_mode)) {
+ return FileType::kSymlink;
#endif
#if defined(S_ISSOCK)
- } else if (S_ISSOCK(sb.st_mode)) {
- return FileType::kSocket;
+ } else if (S_ISSOCK(sb.st_mode)) {
+ return FileType::kSocket;
#endif
- } else {
- return FileType::kUnknown;
- }
+ } else {
+ return FileType::kUnknown;
+ }
}
-std::vector<std::string> listFiles(const StringPiece& root, std::string* outError) {
- DIR* dir = opendir(root.data());
- if (dir == nullptr) {
- if (outError) {
- std::stringstream errorStr;
- errorStr << "unable to open file: " << strerror(errno);
- *outError = errorStr.str();
- return {};
- }
- }
-
- std::vector<std::string> files;
- dirent* entry;
- while ((entry = readdir(dir))) {
- files.emplace_back(entry->d_name);
- }
-
- closedir(dir);
- return files;
-}
-
-inline static int mkdirImpl(const StringPiece& path) {
+inline static int MkdirImpl(const StringPiece& path) {
#ifdef _WIN32
- return _mkdir(path.toString().c_str());
+ return _mkdir(path.ToString().c_str());
#else
- return mkdir(path.toString().c_str(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
+ return mkdir(path.ToString().c_str(),
+ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP);
#endif
}
bool mkdirs(const StringPiece& path) {
- const char* start = path.begin();
- const char* end = path.end();
- for (const char* current = start; current != end; ++current) {
- if (*current == sDirSep && current != start) {
- StringPiece parentPath(start, current - start);
- int result = mkdirImpl(parentPath);
- if (result < 0 && errno != EEXIST) {
- return false;
- }
- }
- }
- return mkdirImpl(path) == 0 || errno == EEXIST;
-}
-
-StringPiece getStem(const StringPiece& path) {
- const char* start = path.begin();
- const char* end = path.end();
- for (const char* current = end - 1; current != start - 1; --current) {
- if (*current == sDirSep) {
- return StringPiece(start, current - start);
- }
- }
- return {};
-}
-
-StringPiece getFilename(const StringPiece& path) {
- const char* end = path.end();
- const char* lastDirSep = path.begin();
- for (const char* c = path.begin(); c != end; ++c) {
- if (*c == sDirSep) {
- lastDirSep = c + 1;
- }
- }
- return StringPiece(lastDirSep, end - lastDirSep);
-}
-
-StringPiece getExtension(const StringPiece& path) {
- StringPiece filename = getFilename(path);
- const char* const end = filename.end();
- const char* c = std::find(filename.begin(), end, '.');
- if (c != end) {
- return StringPiece(c, end - c);
- }
- return {};
-}
-
-void appendPath(std::string* base, StringPiece part) {
- assert(base);
- const bool baseHasTrailingSep = (!base->empty() && *(base->end() - 1) == sDirSep);
- const bool partHasLeadingSep = (!part.empty() && *(part.begin()) == sDirSep);
- if (baseHasTrailingSep && partHasLeadingSep) {
- // Remove the part's leading sep
- part = part.substr(1, part.size() - 1);
- } else if (!baseHasTrailingSep && !partHasLeadingSep) {
- // None of the pieces has a separator.
- *base += sDirSep;
- }
- base->append(part.data(), part.size());
-}
-
-std::string packageToPath(const StringPiece& package) {
- std::string outPath;
- for (StringPiece part : util::tokenize(package, '.')) {
- appendPath(&outPath, part);
- }
- return outPath;
-}
-
-Maybe<android::FileMap> mmapPath(const StringPiece& path, std::string* outError) {
- std::unique_ptr<FILE, decltype(fclose)*> f = { fopen(path.data(), "rb"), fclose };
- if (!f) {
- if (outError) *outError = strerror(errno);
- return {};
- }
-
- int fd = fileno(f.get());
-
- struct stat fileStats = {};
- if (fstat(fd, &fileStats) != 0) {
- if (outError) *outError = strerror(errno);
- return {};
- }
-
- android::FileMap fileMap;
- if (fileStats.st_size == 0) {
- // mmap doesn't like a length of 0. Instead we return an empty FileMap.
- return std::move(fileMap);
- }
-
- if (!fileMap.create(path.data(), fd, 0, fileStats.st_size, true)) {
- if (outError) *outError = strerror(errno);
- return {};
- }
- return std::move(fileMap);
-}
-
-bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outArgList,
- std::string* outError) {
- std::string contents;
- if (!android::base::ReadFileToString(path.toString(), &contents)) {
- if (outError) *outError = "failed to read argument-list file";
+ const char* start = path.begin();
+ const char* end = path.end();
+ for (const char* current = start; current != end; ++current) {
+ if (*current == sDirSep && current != start) {
+ StringPiece parent_path(start, current - start);
+ int result = MkdirImpl(parent_path);
+ if (result < 0 && errno != EEXIST) {
return false;
+ }
}
-
- for (StringPiece line : util::tokenize(contents, ' ')) {
- line = util::trimWhitespace(line);
- if (!line.empty()) {
- outArgList->push_back(line.toString());
- }
- }
- return true;
+ }
+ return MkdirImpl(path) == 0 || errno == EEXIST;
}
-bool FileFilter::setPattern(const StringPiece& pattern) {
- mPatternTokens = util::splitAndLowercase(pattern, ':');
- return true;
+StringPiece GetStem(const StringPiece& path) {
+ const char* start = path.begin();
+ const char* end = path.end();
+ for (const char* current = end - 1; current != start - 1; --current) {
+ if (*current == sDirSep) {
+ return StringPiece(start, current - start);
+ }
+ }
+ return {};
+}
+
+StringPiece GetFilename(const StringPiece& path) {
+ const char* end = path.end();
+ const char* last_dir_sep = path.begin();
+ for (const char* c = path.begin(); c != end; ++c) {
+ if (*c == sDirSep) {
+ last_dir_sep = c + 1;
+ }
+ }
+ return StringPiece(last_dir_sep, end - last_dir_sep);
+}
+
+StringPiece GetExtension(const StringPiece& path) {
+ StringPiece filename = GetFilename(path);
+ const char* const end = filename.end();
+ const char* c = std::find(filename.begin(), end, '.');
+ if (c != end) {
+ return StringPiece(c, end - c);
+ }
+ return {};
+}
+
+void AppendPath(std::string* base, StringPiece part) {
+ CHECK(base != nullptr);
+ const bool base_has_trailing_sep =
+ (!base->empty() && *(base->end() - 1) == sDirSep);
+ const bool part_has_leading_sep =
+ (!part.empty() && *(part.begin()) == sDirSep);
+ if (base_has_trailing_sep && part_has_leading_sep) {
+ // Remove the part's leading sep
+ part = part.substr(1, part.size() - 1);
+ } else if (!base_has_trailing_sep && !part_has_leading_sep) {
+ // None of the pieces has a separator.
+ *base += sDirSep;
+ }
+ base->append(part.data(), part.size());
+}
+
+std::string PackageToPath(const StringPiece& package) {
+ std::string out_path;
+ for (StringPiece part : util::Tokenize(package, '.')) {
+ AppendPath(&out_path, part);
+ }
+ return out_path;
+}
+
+Maybe<android::FileMap> MmapPath(const StringPiece& path,
+ std::string* out_error) {
+ std::unique_ptr<FILE, decltype(fclose)*> f = {fopen(path.data(), "rb"),
+ fclose};
+ if (!f) {
+ if (out_error) *out_error = android::base::SystemErrorCodeToString(errno);
+ return {};
+ }
+
+ int fd = fileno(f.get());
+
+ struct stat filestats = {};
+ if (fstat(fd, &filestats) != 0) {
+ if (out_error) *out_error = android::base::SystemErrorCodeToString(errno);
+ return {};
+ }
+
+ android::FileMap filemap;
+ if (filestats.st_size == 0) {
+ // mmap doesn't like a length of 0. Instead we return an empty FileMap.
+ return std::move(filemap);
+ }
+
+ if (!filemap.create(path.data(), fd, 0, filestats.st_size, true)) {
+ if (out_error) *out_error = android::base::SystemErrorCodeToString(errno);
+ return {};
+ }
+ return std::move(filemap);
+}
+
+bool AppendArgsFromFile(const StringPiece& path,
+ std::vector<std::string>* out_arglist,
+ std::string* out_error) {
+ std::string contents;
+ if (!android::base::ReadFileToString(path.ToString(), &contents)) {
+ if (out_error) *out_error = "failed to read argument-list file";
+ return false;
+ }
+
+ for (StringPiece line : util::Tokenize(contents, ' ')) {
+ line = util::TrimWhitespace(line);
+ if (!line.empty()) {
+ out_arglist->push_back(line.ToString());
+ }
+ }
+ return true;
+}
+
+bool FileFilter::SetPattern(const StringPiece& pattern) {
+ pattern_tokens_ = util::SplitAndLowercase(pattern, ':');
+ return true;
}
bool FileFilter::operator()(const std::string& filename, FileType type) const {
- if (filename == "." || filename == "..") {
- return false;
+ if (filename == "." || filename == "..") {
+ return false;
+ }
+
+ const char kDir[] = "dir";
+ const char kFile[] = "file";
+ const size_t filename_len = filename.length();
+ bool chatty = true;
+ for (const std::string& token : pattern_tokens_) {
+ const char* token_str = token.c_str();
+ if (*token_str == '!') {
+ chatty = false;
+ token_str++;
}
- const char kDir[] = "dir";
- const char kFile[] = "file";
- const size_t filenameLen = filename.length();
- bool chatty = true;
- for (const std::string& token : mPatternTokens) {
- const char* tokenStr = token.c_str();
- if (*tokenStr == '!') {
- chatty = false;
- tokenStr++;
- }
-
- if (strncasecmp(tokenStr, kDir, sizeof(kDir)) == 0) {
- if (type != FileType::kDirectory) {
- continue;
- }
- tokenStr += sizeof(kDir);
- }
-
- if (strncasecmp(tokenStr, kFile, sizeof(kFile)) == 0) {
- if (type != FileType::kRegular) {
- continue;
- }
- tokenStr += sizeof(kFile);
- }
-
- bool ignore = false;
- size_t n = strlen(tokenStr);
- if (*tokenStr == '*') {
- // Math suffix.
- tokenStr++;
- n--;
- if (n <= filenameLen) {
- ignore = strncasecmp(tokenStr, filename.c_str() + filenameLen - n, n) == 0;
- }
- } else if (n > 1 && tokenStr[n - 1] == '*') {
- // Match prefix.
- ignore = strncasecmp(tokenStr, filename.c_str(), n - 1) == 0;
- } else {
- ignore = strcasecmp(tokenStr, filename.c_str()) == 0;
- }
-
- if (ignore) {
- if (chatty) {
- mDiag->warn(DiagMessage() << "skipping "
- << (type == FileType::kDirectory ? "dir '" : "file '")
- << filename << "' due to ignore pattern '"
- << token << "'");
- }
- return false;
- }
+ if (strncasecmp(token_str, kDir, sizeof(kDir)) == 0) {
+ if (type != FileType::kDirectory) {
+ continue;
+ }
+ token_str += sizeof(kDir);
}
- return true;
+
+ if (strncasecmp(token_str, kFile, sizeof(kFile)) == 0) {
+ if (type != FileType::kRegular) {
+ continue;
+ }
+ token_str += sizeof(kFile);
+ }
+
+ bool ignore = false;
+ size_t n = strlen(token_str);
+ if (*token_str == '*') {
+ // Math suffix.
+ token_str++;
+ n--;
+ if (n <= filename_len) {
+ ignore =
+ strncasecmp(token_str, filename.c_str() + filename_len - n, n) == 0;
+ }
+ } else if (n > 1 && token_str[n - 1] == '*') {
+ // Match prefix.
+ ignore = strncasecmp(token_str, filename.c_str(), n - 1) == 0;
+ } else {
+ ignore = strcasecmp(token_str, filename.c_str()) == 0;
+ }
+
+ if (ignore) {
+ if (chatty) {
+ diag_->Warn(DiagMessage()
+ << "skipping "
+ << (type == FileType::kDirectory ? "dir '" : "file '")
+ << filename << "' due to ignore pattern '" << token << "'");
+ }
+ return false;
+ }
+ }
+ return true;
}
-} // namespace file
-} // namespace aapt
+} // namespace file
+} // namespace aapt
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 52c2052..a157dbd 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -17,18 +17,18 @@
#ifndef AAPT_FILES_H
#define AAPT_FILES_H
-#include "Diagnostics.h"
-#include "Maybe.h"
-#include "Source.h"
-
-#include "util/StringPiece.h"
-
-#include <utils/FileMap.h>
-#include <cassert>
#include <memory>
#include <string>
#include <vector>
+#include "android-base/macros.h"
+#include "utils/FileMap.h"
+
+#include "Diagnostics.h"
+#include "Maybe.h"
+#include "Source.h"
+#include "util/StringPiece.h"
+
namespace aapt {
namespace file {
@@ -39,29 +39,23 @@
#endif
enum class FileType {
- kUnknown = 0,
- kNonexistant,
- kRegular,
- kDirectory,
- kCharDev,
- kBlockDev,
- kFifo,
- kSymlink,
- kSocket,
+ kUnknown = 0,
+ kNonexistant,
+ kRegular,
+ kDirectory,
+ kCharDev,
+ kBlockDev,
+ kFifo,
+ kSymlink,
+ kSocket,
};
-FileType getFileType(const StringPiece& path);
-
-/*
- * Lists files under the directory `root`. Files are listed
- * with just their leaf (filename) names.
- */
-std::vector<std::string> listFiles(const StringPiece& root);
+FileType GetFileType(const StringPiece& path);
/*
* Appends a path to `base`, separated by the directory separator.
*/
-void appendPath(std::string* base, StringPiece part);
+void AppendPath(std::string* base, StringPiece part);
/*
* Makes all the directories in `path`. The last element in the path
@@ -72,73 +66,75 @@
/**
* Returns all but the last part of the path.
*/
-StringPiece getStem(const StringPiece& path);
+StringPiece GetStem(const StringPiece& path);
/**
* Returns the last part of the path with extension.
*/
-StringPiece getFilename(const StringPiece& path);
+StringPiece GetFilename(const StringPiece& path);
/**
* Returns the extension of the path. This is the entire string after
* the first '.' of the last part of the path.
*/
-StringPiece getExtension(const StringPiece& path);
+StringPiece GetExtension(const StringPiece& path);
/**
* Converts a package name (com.android.app) to a path: com/android/app
*/
-std::string packageToPath(const StringPiece& package);
+std::string PackageToPath(const StringPiece& package);
/**
* Creates a FileMap for the file at path.
*/
-Maybe<android::FileMap> mmapPath(const StringPiece& path, std::string* outError);
+Maybe<android::FileMap> MmapPath(const StringPiece& path,
+ std::string* out_error);
/**
* Reads the file at path and appends each line to the outArgList vector.
*/
-bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outArgList,
- std::string* outError);
+bool AppendArgsFromFile(const StringPiece& path,
+ std::vector<std::string>* out_arglist,
+ std::string* out_error);
/*
* Filter that determines which resource files/directories are
* processed by AAPT. Takes a pattern string supplied by the user.
- * Pattern format is specified in the
- * FileFilter::setPattern(const std::string&) method.
+ * Pattern format is specified in the FileFilter::SetPattern() method.
*/
class FileFilter {
-public:
- explicit FileFilter(IDiagnostics* diag) : mDiag(diag) {
- }
+ public:
+ explicit FileFilter(IDiagnostics* diag) : diag_(diag) {}
- /*
- * Patterns syntax:
- * - Delimiter is :
- * - Entry can start with the flag ! to avoid printing a warning
- * about the file being ignored.
- * - Entry can have the flag "<dir>" to match only directories
- * or <file> to match only files. Default is to match both.
- * - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
- * where prefix/suffix must have at least 1 character (so that
- * we don't match a '*' catch-all pattern.)
- * - The special filenames "." and ".." are always ignored.
- * - Otherwise the full string is matched.
- * - match is not case-sensitive.
- */
- bool setPattern(const StringPiece& pattern);
+ /*
+ * Patterns syntax:
+ * - Delimiter is :
+ * - Entry can start with the flag ! to avoid printing a warning
+ * about the file being ignored.
+ * - Entry can have the flag "<dir>" to match only directories
+ * or <file> to match only files. Default is to match both.
+ * - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
+ * where prefix/suffix must have at least 1 character (so that
+ * we don't match a '*' catch-all pattern.)
+ * - The special filenames "." and ".." are always ignored.
+ * - Otherwise the full string is matched.
+ * - match is not case-sensitive.
+ */
+ bool SetPattern(const StringPiece& pattern);
- /**
- * Applies the filter, returning true for pass, false for fail.
- */
- bool operator()(const std::string& filename, FileType type) const;
+ /**
+ * Applies the filter, returning true for pass, false for fail.
+ */
+ bool operator()(const std::string& filename, FileType type) const;
-private:
- IDiagnostics* mDiag;
- std::vector<std::string> mPatternTokens;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FileFilter);
+
+ IDiagnostics* diag_;
+ std::vector<std::string> pattern_tokens_;
};
-} // namespace file
-} // namespace aapt
+} // namespace file
+} // namespace aapt
-#endif // AAPT_FILES_H
+#endif // AAPT_FILES_H
diff --git a/tools/aapt2/util/Files_test.cpp b/tools/aapt2/util/Files_test.cpp
index efb0459..219c183 100644
--- a/tools/aapt2/util/Files_test.cpp
+++ b/tools/aapt2/util/Files_test.cpp
@@ -14,45 +14,46 @@
* limitations under the License.
*/
-#include "test/Test.h"
#include "util/Files.h"
#include <sstream>
+#include "test/Test.h"
+
namespace aapt {
namespace file {
class FilesTest : public ::testing::Test {
-public:
- void SetUp() override {
- std::stringstream builder;
- builder << "hello" << sDirSep << "there";
- mExpectedPath = builder.str();
- }
+ public:
+ void SetUp() override {
+ std::stringstream builder;
+ builder << "hello" << sDirSep << "there";
+ expected_path_ = builder.str();
+ }
-protected:
- std::string mExpectedPath;
+ protected:
+ std::string expected_path_;
};
-TEST_F(FilesTest, appendPath) {
- std::string base = "hello";
- appendPath(&base, "there");
- EXPECT_EQ(mExpectedPath, base);
+TEST_F(FilesTest, AppendPath) {
+ std::string base = "hello";
+ AppendPath(&base, "there");
+ EXPECT_EQ(expected_path_, base);
}
-TEST_F(FilesTest, appendPathWithLeadingOrTrailingSeparators) {
- std::string base = "hello/";
- appendPath(&base, "there");
- EXPECT_EQ(mExpectedPath, base);
+TEST_F(FilesTest, AppendPathWithLeadingOrTrailingSeparators) {
+ std::string base = "hello/";
+ AppendPath(&base, "there");
+ EXPECT_EQ(expected_path_, base);
- base = "hello";
- appendPath(&base, "/there");
- EXPECT_EQ(mExpectedPath, base);
+ base = "hello";
+ AppendPath(&base, "/there");
+ EXPECT_EQ(expected_path_, base);
- base = "hello/";
- appendPath(&base, "/there");
- EXPECT_EQ(mExpectedPath, base);
+ base = "hello/";
+ AppendPath(&base, "/there");
+ EXPECT_EQ(expected_path_, base);
}
-} // namespace files
-} // namespace aapt
+} // namespace files
+} // namespace aapt
diff --git a/tools/aapt2/util/ImmutableMap.h b/tools/aapt2/util/ImmutableMap.h
index b1f9e9d..59858e4 100644
--- a/tools/aapt2/util/ImmutableMap.h
+++ b/tools/aapt2/util/ImmutableMap.h
@@ -17,68 +17,66 @@
#ifndef AAPT_UTIL_IMMUTABLEMAP_H
#define AAPT_UTIL_IMMUTABLEMAP_H
-#include "util/TypeTraits.h"
-
#include <utility>
#include <vector>
+#include "util/TypeTraits.h"
+
namespace aapt {
template <typename TKey, typename TValue>
class ImmutableMap {
- static_assert(is_comparable<TKey, TKey>::value, "key is not comparable");
+ static_assert(is_comparable<TKey, TKey>::value, "key is not comparable");
-private:
- std::vector<std::pair<TKey, TValue>> mData;
+ public:
+ using const_iterator =
+ typename std::vector<std::pair<TKey, TValue>>::const_iterator;
- explicit ImmutableMap(std::vector<std::pair<TKey, TValue>> data) : mData(std::move(data)) {
+ ImmutableMap(ImmutableMap&&) = default;
+ ImmutableMap& operator=(ImmutableMap&&) = default;
+
+ static ImmutableMap<TKey, TValue> CreatePreSorted(
+ std::initializer_list<std::pair<TKey, TValue>> list) {
+ return ImmutableMap(
+ std::vector<std::pair<TKey, TValue>>(list.begin(), list.end()));
+ }
+
+ static ImmutableMap<TKey, TValue> CreateAndSort(
+ std::initializer_list<std::pair<TKey, TValue>> list) {
+ std::vector<std::pair<TKey, TValue>> data(list.begin(), list.end());
+ std::sort(data.begin(), data.end());
+ return ImmutableMap(std::move(data));
+ }
+
+ template <typename TKey2, typename = typename std::enable_if<
+ is_comparable<TKey, TKey2>::value>::type>
+ const_iterator find(const TKey2& key) const {
+ auto cmp = [](const std::pair<TKey, TValue>& candidate,
+ const TKey2& target) -> bool {
+ return candidate.first < target;
+ };
+
+ const_iterator end_iter = end();
+ auto iter = std::lower_bound(data_.begin(), end_iter, key, cmp);
+ if (iter == end_iter || iter->first == key) {
+ return iter;
}
+ return end_iter;
+ }
-public:
- using const_iterator = typename decltype(mData)::const_iterator;
+ const_iterator begin() const { return data_.begin(); }
- ImmutableMap(ImmutableMap&&) = default;
- ImmutableMap& operator=(ImmutableMap&&) = default;
+ const_iterator end() const { return data_.end(); }
- ImmutableMap(const ImmutableMap&) = delete;
- ImmutableMap& operator=(const ImmutableMap&) = delete;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ImmutableMap);
- static ImmutableMap<TKey, TValue> createPreSorted(
- std::initializer_list<std::pair<TKey, TValue>> list) {
- return ImmutableMap(std::vector<std::pair<TKey, TValue>>(list.begin(), list.end()));
- }
+ explicit ImmutableMap(std::vector<std::pair<TKey, TValue>> data)
+ : data_(std::move(data)) {}
- static ImmutableMap<TKey, TValue> createAndSort(
- std::initializer_list<std::pair<TKey, TValue>> list) {
- std::vector<std::pair<TKey, TValue>> data(list.begin(), list.end());
- std::sort(data.begin(), data.end());
- return ImmutableMap(std::move(data));
- }
-
- template <typename TKey2,
- typename = typename std::enable_if<is_comparable<TKey, TKey2>::value>::type>
- const_iterator find(const TKey2& key) const {
- auto cmp = [](const std::pair<TKey, TValue>& candidate, const TKey2& target) -> bool {
- return candidate.first < target;
- };
-
- const_iterator endIter = end();
- auto iter = std::lower_bound(mData.begin(), endIter, key, cmp);
- if (iter == endIter || iter->first == key) {
- return iter;
- }
- return endIter;
- }
-
- const_iterator begin() const {
- return mData.begin();
- }
-
- const_iterator end() const {
- return mData.end();
- }
+ std::vector<std::pair<TKey, TValue>> data_;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_UTIL_IMMUTABLEMAP_H */
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index 129f6d9..b43f8e8 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -17,12 +17,13 @@
#ifndef AAPT_MAYBE_H
#define AAPT_MAYBE_H
-#include "util/TypeTraits.h"
-
-#include <cassert>
#include <type_traits>
#include <utility>
+#include "android-base/logging.h"
+
+#include "util/TypeTraits.h"
+
namespace aapt {
/**
@@ -32,303 +33,292 @@
*/
template <typename T>
class Maybe {
-public:
- /**
- * Construct Nothing.
- */
- Maybe();
+ public:
+ /**
+ * Construct Nothing.
+ */
+ Maybe();
- ~Maybe();
+ ~Maybe();
- Maybe(const Maybe& rhs);
+ Maybe(const Maybe& rhs);
- template <typename U>
- Maybe(const Maybe<U>& rhs); // NOLINT(implicit)
+ template <typename U>
+ Maybe(const Maybe<U>& rhs); // NOLINT(implicit)
- Maybe(Maybe&& rhs);
+ Maybe(Maybe&& rhs);
- template <typename U>
- Maybe(Maybe<U>&& rhs); // NOLINT(implicit)
+ template <typename U>
+ Maybe(Maybe<U>&& rhs); // NOLINT(implicit)
- Maybe& operator=(const Maybe& rhs);
+ Maybe& operator=(const Maybe& rhs);
- template <typename U>
- Maybe& operator=(const Maybe<U>& rhs);
+ template <typename U>
+ Maybe& operator=(const Maybe<U>& rhs);
- Maybe& operator=(Maybe&& rhs);
+ Maybe& operator=(Maybe&& rhs);
- template <typename U>
- Maybe& operator=(Maybe<U>&& rhs);
+ template <typename U>
+ Maybe& operator=(Maybe<U>&& rhs);
- /**
- * Construct a Maybe holding a value.
- */
- Maybe(const T& value); // NOLINT(implicit)
+ /**
+ * Construct a Maybe holding a value.
+ */
+ Maybe(const T& value); // NOLINT(implicit)
- /**
- * Construct a Maybe holding a value.
- */
- Maybe(T&& value); // NOLINT(implicit)
+ /**
+ * Construct a Maybe holding a value.
+ */
+ Maybe(T&& value); // NOLINT(implicit)
- /**
- * True if this holds a value, false if
- * it holds Nothing.
- */
- explicit operator bool() const;
+ /**
+ * True if this holds a value, false if
+ * it holds Nothing.
+ */
+ explicit operator bool() const;
- /**
- * Gets the value if one exists, or else
- * panics.
- */
- T& value();
+ /**
+ * Gets the value if one exists, or else
+ * panics.
+ */
+ T& value();
- /**
- * Gets the value if one exists, or else
- * panics.
- */
- const T& value() const;
+ /**
+ * Gets the value if one exists, or else
+ * panics.
+ */
+ const T& value() const;
- T valueOrDefault(const T& def) const;
+ T value_or_default(const T& def) const;
-private:
- template <typename U>
- friend class Maybe;
+ private:
+ template <typename U>
+ friend class Maybe;
- template <typename U>
- Maybe& copy(const Maybe<U>& rhs);
+ template <typename U>
+ Maybe& copy(const Maybe<U>& rhs);
- template <typename U>
- Maybe& move(Maybe<U>&& rhs);
+ template <typename U>
+ Maybe& move(Maybe<U>&& rhs);
- void destroy();
+ void destroy();
- bool mNothing;
+ bool nothing_;
- typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
+ typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
};
template <typename T>
-Maybe<T>::Maybe()
-: mNothing(true) {
-}
+Maybe<T>::Maybe() : nothing_(true) {}
template <typename T>
Maybe<T>::~Maybe() {
- if (!mNothing) {
- destroy();
- }
+ if (!nothing_) {
+ destroy();
+ }
}
template <typename T>
-Maybe<T>::Maybe(const Maybe& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- new (&mStorage) T(reinterpret_cast<const T&>(rhs.mStorage));
- }
+Maybe<T>::Maybe(const Maybe& rhs) : nothing_(rhs.nothing_) {
+ if (!rhs.nothing_) {
+ new (&storage_) T(reinterpret_cast<const T&>(rhs.storage_));
+ }
}
template <typename T>
template <typename U>
-Maybe<T>::Maybe(const Maybe<U>& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
- }
+Maybe<T>::Maybe(const Maybe<U>& rhs) : nothing_(rhs.nothing_) {
+ if (!rhs.nothing_) {
+ new (&storage_) T(reinterpret_cast<const U&>(rhs.storage_));
+ }
}
template <typename T>
-Maybe<T>::Maybe(Maybe&& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- rhs.mNothing = true;
+Maybe<T>::Maybe(Maybe&& rhs) : nothing_(rhs.nothing_) {
+ if (!rhs.nothing_) {
+ rhs.nothing_ = true;
- // Move the value from rhs.
- new (&mStorage) T(std::move(reinterpret_cast<T&>(rhs.mStorage)));
- rhs.destroy();
- }
+ // Move the value from rhs.
+ new (&storage_) T(std::move(reinterpret_cast<T&>(rhs.storage_)));
+ rhs.destroy();
+ }
}
template <typename T>
template <typename U>
-Maybe<T>::Maybe(Maybe<U>&& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- rhs.mNothing = true;
+Maybe<T>::Maybe(Maybe<U>&& rhs) : nothing_(rhs.nothing_) {
+ if (!rhs.nothing_) {
+ rhs.nothing_ = true;
- // Move the value from rhs.
- new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
- rhs.destroy();
- }
+ // Move the value from rhs.
+ new (&storage_) T(std::move(reinterpret_cast<U&>(rhs.storage_)));
+ rhs.destroy();
+ }
}
template <typename T>
inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
- // Delegate to the actual assignment.
- return copy(rhs);
+ // Delegate to the actual assignment.
+ return copy(rhs);
}
template <typename T>
template <typename U>
inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
- return copy(rhs);
+ return copy(rhs);
}
template <typename T>
template <typename U>
Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
- if (mNothing && rhs.mNothing) {
- // Both are nothing, nothing to do.
- return *this;
- } else if (!mNothing && !rhs.mNothing) {
- // We both are something, so assign rhs to us.
- reinterpret_cast<T&>(mStorage) = reinterpret_cast<const U&>(rhs.mStorage);
- } else if (mNothing) {
- // We are nothing but rhs is something.
- mNothing = rhs.mNothing;
-
- // Copy the value from rhs.
- new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
- } else {
- // We are something but rhs is nothing, so destroy our value.
- mNothing = rhs.mNothing;
- destroy();
- }
+ if (nothing_ && rhs.nothing_) {
+ // Both are nothing, nothing to do.
return *this;
+ } else if (!nothing_ && !rhs.nothing_) {
+ // We both are something, so assign rhs to us.
+ reinterpret_cast<T&>(storage_) = reinterpret_cast<const U&>(rhs.storage_);
+ } else if (nothing_) {
+ // We are nothing but rhs is something.
+ nothing_ = rhs.nothing_;
+
+ // Copy the value from rhs.
+ new (&storage_) T(reinterpret_cast<const U&>(rhs.storage_));
+ } else {
+ // We are something but rhs is nothing, so destroy our value.
+ nothing_ = rhs.nothing_;
+ destroy();
+ }
+ return *this;
}
template <typename T>
inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
- // Delegate to the actual assignment.
- return move(std::forward<Maybe<T>>(rhs));
+ // Delegate to the actual assignment.
+ return move(std::forward<Maybe<T>>(rhs));
}
template <typename T>
template <typename U>
inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
- return move(std::forward<Maybe<U>>(rhs));
+ return move(std::forward<Maybe<U>>(rhs));
}
template <typename T>
template <typename U>
Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
- if (mNothing && rhs.mNothing) {
- // Both are nothing, nothing to do.
- return *this;
- } else if (!mNothing && !rhs.mNothing) {
- // We both are something, so move assign rhs to us.
- rhs.mNothing = true;
- reinterpret_cast<T&>(mStorage) = std::move(reinterpret_cast<U&>(rhs.mStorage));
- rhs.destroy();
- } else if (mNothing) {
- // We are nothing but rhs is something.
- mNothing = false;
- rhs.mNothing = true;
-
- // Move the value from rhs.
- new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
- rhs.destroy();
- } else {
- // We are something but rhs is nothing, so destroy our value.
- mNothing = true;
- destroy();
- }
+ if (nothing_ && rhs.nothing_) {
+ // Both are nothing, nothing to do.
return *this;
+ } else if (!nothing_ && !rhs.nothing_) {
+ // We both are something, so move assign rhs to us.
+ rhs.nothing_ = true;
+ reinterpret_cast<T&>(storage_) =
+ std::move(reinterpret_cast<U&>(rhs.storage_));
+ rhs.destroy();
+ } else if (nothing_) {
+ // We are nothing but rhs is something.
+ nothing_ = false;
+ rhs.nothing_ = true;
+
+ // Move the value from rhs.
+ new (&storage_) T(std::move(reinterpret_cast<U&>(rhs.storage_)));
+ rhs.destroy();
+ } else {
+ // We are something but rhs is nothing, so destroy our value.
+ nothing_ = true;
+ destroy();
+ }
+ return *this;
}
template <typename T>
-Maybe<T>::Maybe(const T& value)
-: mNothing(false) {
- new (&mStorage) T(value);
+Maybe<T>::Maybe(const T& value) : nothing_(false) {
+ new (&storage_) T(value);
}
template <typename T>
-Maybe<T>::Maybe(T&& value)
-: mNothing(false) {
- new (&mStorage) T(std::forward<T>(value));
+Maybe<T>::Maybe(T&& value) : nothing_(false) {
+ new (&storage_) T(std::forward<T>(value));
}
template <typename T>
Maybe<T>::operator bool() const {
- return !mNothing;
+ return !nothing_;
}
template <typename T>
T& Maybe<T>::value() {
- assert(!mNothing && "Maybe<T>::value() called on Nothing");
- return reinterpret_cast<T&>(mStorage);
+ CHECK(!nothing_) << "Maybe<T>::value() called on Nothing";
+ return reinterpret_cast<T&>(storage_);
}
template <typename T>
const T& Maybe<T>::value() const {
- assert(!mNothing && "Maybe<T>::value() called on Nothing");
- return reinterpret_cast<const T&>(mStorage);
+ CHECK(!nothing_) << "Maybe<T>::value() called on Nothing";
+ return reinterpret_cast<const T&>(storage_);
}
template <typename T>
-T Maybe<T>::valueOrDefault(const T& def) const {
- if (mNothing) {
- return def;
- }
- return reinterpret_cast<const T&>(mStorage);
+T Maybe<T>::value_or_default(const T& def) const {
+ if (nothing_) {
+ return def;
+ }
+ return reinterpret_cast<const T&>(storage_);
}
template <typename T>
void Maybe<T>::destroy() {
- reinterpret_cast<T&>(mStorage).~T();
+ reinterpret_cast<T&>(storage_).~T();
}
template <typename T>
inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
- return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
+ return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
}
template <typename T>
inline Maybe<T> make_nothing() {
- return Maybe<T>();
+ return Maybe<T>();
}
/**
- * Define the == operator between Maybe<T> and Maybe<U> only if the operator T == U is defined.
- * That way the compiler will show an error at the callsite when comparing two Maybe<> objects
+ * Define the == operator between Maybe<T> and Maybe<U> only if the operator T
+ * == U is defined.
+ * That way the compiler will show an error at the callsite when comparing two
+ * Maybe<> objects
* whose inner types can't be compared.
*/
template <typename T, typename U>
-typename std::enable_if<
- has_eq_op<T, U>::value,
- bool
->::type operator==(const Maybe<T>& a, const Maybe<U>& b) {
- if (a && b) {
- return a.value() == b.value();
- } else if (!a && !b) {
- return true;
- }
- return false;
+typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(
+ const Maybe<T>& a, const Maybe<U>& b) {
+ if (a && b) {
+ return a.value() == b.value();
+ } else if (!a && !b) {
+ return true;
+ }
+ return false;
}
/**
* Same as operator== but negated.
*/
template <typename T, typename U>
-typename std::enable_if<
- has_eq_op<T, U>::value,
- bool
->::type operator!=(const Maybe<T>& a, const Maybe<U>& b) {
- return !(a == b);
+typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator!=(
+ const Maybe<T>& a, const Maybe<U>& b) {
+ return !(a == b);
}
template <typename T, typename U>
-typename std::enable_if<
- has_lt_op<T, U>::value,
- bool
->::type operator<(const Maybe<T>& a, const Maybe<U>& b) {
- if (a && b) {
- return a.value() < b.value();
- } else if (!a && !b) {
- return false;
- }
- return !a;
+typename std::enable_if<has_lt_op<T, U>::value, bool>::type operator<(
+ const Maybe<T>& a, const Maybe<U>& b) {
+ if (a && b) {
+ return a.value() < b.value();
+ } else if (!a && !b) {
+ return false;
+ }
+ return !a;
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_MAYBE_H
+#endif // AAPT_MAYBE_H
diff --git a/tools/aapt2/util/Maybe_test.cpp b/tools/aapt2/util/Maybe_test.cpp
index 5d42dc3..ca14793 100644
--- a/tools/aapt2/util/Maybe_test.cpp
+++ b/tools/aapt2/util/Maybe_test.cpp
@@ -14,122 +14,116 @@
* limitations under the License.
*/
-#include "test/Common.h"
#include "util/Maybe.h"
-#include <gtest/gtest.h>
#include <string>
+#include "test/Test.h"
+
namespace aapt {
struct Dummy {
- Dummy() {
- data = new int;
- *data = 1;
- std::cerr << "Construct Dummy{0x" << (void *) this
- << "} with data=0x" << (void*) data
- << std::endl;
+ Dummy() {
+ data = new int;
+ *data = 1;
+ std::cerr << "Construct Dummy{0x" << (void*)this << "} with data=0x"
+ << (void*)data << std::endl;
+ }
+
+ Dummy(const Dummy& rhs) {
+ data = nullptr;
+ if (rhs.data) {
+ data = new int;
+ *data = *rhs.data;
}
+ std::cerr << "CopyConstruct Dummy{0x" << (void*)this << "} from Dummy{0x"
+ << (const void*)&rhs << "}" << std::endl;
+ }
- Dummy(const Dummy& rhs) {
- data = nullptr;
- if (rhs.data) {
- data = new int;
- *data = *rhs.data;
- }
- std::cerr << "CopyConstruct Dummy{0x" << (void *) this
- << "} from Dummy{0x" << (const void*) &rhs
- << "}" << std::endl;
+ Dummy(Dummy&& rhs) {
+ data = rhs.data;
+ rhs.data = nullptr;
+ std::cerr << "MoveConstruct Dummy{0x" << (void*)this << "} from Dummy{0x"
+ << (const void*)&rhs << "}" << std::endl;
+ }
+
+ Dummy& operator=(const Dummy& rhs) {
+ delete data;
+ data = nullptr;
+
+ if (rhs.data) {
+ data = new int;
+ *data = *rhs.data;
}
+ std::cerr << "CopyAssign Dummy{0x" << (void*)this << "} from Dummy{0x"
+ << (const void*)&rhs << "}" << std::endl;
+ return *this;
+ }
- Dummy(Dummy&& rhs) {
- data = rhs.data;
- rhs.data = nullptr;
- std::cerr << "MoveConstruct Dummy{0x" << (void *) this
- << "} from Dummy{0x" << (const void*) &rhs
- << "}" << std::endl;
- }
+ Dummy& operator=(Dummy&& rhs) {
+ delete data;
+ data = rhs.data;
+ rhs.data = nullptr;
+ std::cerr << "MoveAssign Dummy{0x" << (void*)this << "} from Dummy{0x"
+ << (const void*)&rhs << "}" << std::endl;
+ return *this;
+ }
- Dummy& operator=(const Dummy& rhs) {
- delete data;
- data = nullptr;
+ ~Dummy() {
+ std::cerr << "Destruct Dummy{0x" << (void*)this << "} with data=0x"
+ << (void*)data << std::endl;
+ delete data;
+ }
- if (rhs.data) {
- data = new int;
- *data = *rhs.data;
- }
- std::cerr << "CopyAssign Dummy{0x" << (void *) this
- << "} from Dummy{0x" << (const void*) &rhs
- << "}" << std::endl;
- return *this;
- }
-
- Dummy& operator=(Dummy&& rhs) {
- delete data;
- data = rhs.data;
- rhs.data = nullptr;
- std::cerr << "MoveAssign Dummy{0x" << (void *) this
- << "} from Dummy{0x" << (const void*) &rhs
- << "}" << std::endl;
- return *this;
- }
-
- ~Dummy() {
- std::cerr << "Destruct Dummy{0x" << (void *) this
- << "} with data=0x" << (void*) data
- << std::endl;
- delete data;
- }
-
- int* data;
+ int* data;
};
TEST(MaybeTest, MakeNothing) {
- Maybe<int> val = make_nothing<int>();
- AAPT_EXPECT_FALSE(val);
+ Maybe<int> val = make_nothing<int>();
+ AAPT_EXPECT_FALSE(val);
- Maybe<std::string> val2 = make_nothing<std::string>();
- AAPT_EXPECT_FALSE(val2);
+ Maybe<std::string> val2 = make_nothing<std::string>();
+ AAPT_EXPECT_FALSE(val2);
- val2 = make_nothing<std::string>();
- AAPT_EXPECT_FALSE(val2);
+ val2 = make_nothing<std::string>();
+ AAPT_EXPECT_FALSE(val2);
}
TEST(MaybeTest, MakeSomething) {
- Maybe<int> val = make_value(23);
- AAPT_ASSERT_TRUE(val);
- EXPECT_EQ(23, val.value());
+ Maybe<int> val = make_value(23);
+ AAPT_ASSERT_TRUE(val);
+ EXPECT_EQ(23, val.value());
- Maybe<std::string> val2 = make_value(std::string("hey"));
- AAPT_ASSERT_TRUE(val2);
- EXPECT_EQ(std::string("hey"), val2.value());
+ Maybe<std::string> val2 = make_value(std::string("hey"));
+ AAPT_ASSERT_TRUE(val2);
+ EXPECT_EQ(std::string("hey"), val2.value());
}
TEST(MaybeTest, Lifecycle) {
- Maybe<Dummy> val = make_nothing<Dummy>();
+ Maybe<Dummy> val = make_nothing<Dummy>();
- Maybe<Dummy> val2 = make_value(Dummy());
+ Maybe<Dummy> val2 = make_value(Dummy());
}
TEST(MaybeTest, MoveAssign) {
- Maybe<Dummy> val;
- {
- Maybe<Dummy> val2 = Dummy();
- val = std::move(val2);
- }
+ Maybe<Dummy> val;
+ {
+ Maybe<Dummy> val2 = Dummy();
+ val = std::move(val2);
+ }
}
TEST(MaybeTest, Equality) {
- Maybe<int> a = 1;
- Maybe<int> b = 1;
- Maybe<int> c;
+ Maybe<int> a = 1;
+ Maybe<int> b = 1;
+ Maybe<int> c;
- Maybe<int> emptyA, emptyB;
+ Maybe<int> emptyA, emptyB;
- EXPECT_EQ(a, b);
- EXPECT_EQ(b, a);
- EXPECT_NE(a, c);
- EXPECT_EQ(emptyA, emptyB);
+ EXPECT_EQ(a, b);
+ EXPECT_EQ(b, a);
+ EXPECT_NE(a, c);
+ EXPECT_EQ(emptyA, emptyB);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/util/StringPiece.h b/tools/aapt2/util/StringPiece.h
index 266c003..5144b1f 100644
--- a/tools/aapt2/util/StringPiece.h
+++ b/tools/aapt2/util/StringPiece.h
@@ -19,9 +19,10 @@
#include <ostream>
#include <string>
-#include <utils/JenkinsHash.h>
-#include <utils/String8.h>
-#include <utils/Unicode.h>
+
+#include "utils/JenkinsHash.h"
+#include "utils/String8.h"
+#include "utils/Unicode.h"
namespace aapt {
@@ -35,45 +36,46 @@
*/
template <typename TChar>
class BasicStringPiece {
-public:
- using const_iterator = const TChar*;
- using difference_type = size_t;
+ public:
+ using const_iterator = const TChar*;
+ using difference_type = size_t;
- // End of string marker.
- constexpr static const size_t npos = static_cast<size_t>(-1);
+ // End of string marker.
+ constexpr static const size_t npos = static_cast<size_t>(-1);
- BasicStringPiece();
- BasicStringPiece(const BasicStringPiece<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();
+ BasicStringPiece(const BasicStringPiece<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);
- BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
+ BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
+ BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
- BasicStringPiece<TChar> substr(size_t start, size_t len = npos) const;
- BasicStringPiece<TChar> substr(BasicStringPiece<TChar>::const_iterator begin,
- BasicStringPiece<TChar>::const_iterator end) const;
+ BasicStringPiece<TChar> substr(size_t start, size_t len = npos) const;
+ BasicStringPiece<TChar> substr(
+ BasicStringPiece<TChar>::const_iterator begin,
+ BasicStringPiece<TChar>::const_iterator end) const;
- const TChar* data() const;
- size_t length() const;
- size_t size() const;
- bool empty() const;
- std::basic_string<TChar> toString() const;
+ const TChar* data() const;
+ size_t length() const;
+ size_t size() const;
+ bool empty() const;
+ std::basic_string<TChar> ToString() const;
- bool contains(const BasicStringPiece<TChar>& rhs) const;
- int compare(const BasicStringPiece<TChar>& rhs) const;
- bool operator<(const BasicStringPiece<TChar>& rhs) const;
- bool operator>(const BasicStringPiece<TChar>& rhs) const;
- bool operator==(const BasicStringPiece<TChar>& rhs) const;
- bool operator!=(const BasicStringPiece<TChar>& rhs) const;
+ bool contains(const BasicStringPiece<TChar>& rhs) const;
+ int compare(const BasicStringPiece<TChar>& rhs) const;
+ bool operator<(const BasicStringPiece<TChar>& rhs) const;
+ bool operator>(const BasicStringPiece<TChar>& rhs) const;
+ bool operator==(const BasicStringPiece<TChar>& rhs) const;
+ bool operator!=(const BasicStringPiece<TChar>& rhs) const;
- const_iterator begin() const;
- const_iterator end() const;
+ const_iterator begin() const;
+ const_iterator end() const;
-private:
- const TChar* mData;
- size_t mLength;
+ private:
+ const TChar* data_;
+ size_t length_;
};
using StringPiece = BasicStringPiece<char>;
@@ -87,198 +89,210 @@
constexpr const size_t BasicStringPiece<TChar>::npos;
template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece() : mData(nullptr) , mLength(0) {
-}
+inline BasicStringPiece<TChar>::BasicStringPiece()
+ : data_(nullptr), length_(0) {}
template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const BasicStringPiece<TChar>& str) :
- mData(str.mData), mLength(str.mLength) {
-}
+inline BasicStringPiece<TChar>::BasicStringPiece(
+ const BasicStringPiece<TChar>& str)
+ : data_(str.data_), length_(str.length_) {}
template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const std::basic_string<TChar>& str) :
- mData(str.data()), mLength(str.length()) {
-}
+inline BasicStringPiece<TChar>::BasicStringPiece(
+ const std::basic_string<TChar>& str)
+ : data_(str.data()), length_(str.length()) {}
template <>
-inline BasicStringPiece<char>::BasicStringPiece(const char* str) :
- mData(str), mLength(str != nullptr ? strlen(str) : 0) {
-}
+inline BasicStringPiece<char>::BasicStringPiece(const char* str)
+ : data_(str), length_(str != nullptr ? strlen(str) : 0) {}
template <>
-inline BasicStringPiece<char16_t>::BasicStringPiece(const char16_t* str) :
- mData(str), mLength(str != nullptr ? strlen16(str) : 0) {
-}
+inline BasicStringPiece<char16_t>::BasicStringPiece(const char16_t* str)
+ : data_(str), length_(str != nullptr ? strlen16(str) : 0) {}
template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const TChar* str, size_t len) :
- mData(str), mLength(len) {
-}
+inline BasicStringPiece<TChar>::BasicStringPiece(const TChar* str, size_t len)
+ : data_(str), length_(len) {}
template <typename TChar>
inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::operator=(
- const BasicStringPiece<TChar>& rhs) {
- mData = rhs.mData;
- mLength = rhs.mLength;
- return *this;
+ const BasicStringPiece<TChar>& rhs) {
+ data_ = rhs.data_;
+ length_ = rhs.length_;
+ return *this;
}
template <typename TChar>
-inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::assign(const TChar* str, size_t len) {
- mData = str;
- mLength = len;
- return *this;
-}
-
-
-template <typename TChar>
-inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(size_t start, size_t len) const {
- if (len == npos) {
- len = mLength - start;
- }
-
- if (start > mLength || start + len > mLength) {
- return BasicStringPiece<TChar>();
- }
- return BasicStringPiece<TChar>(mData + start, len);
+inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::assign(
+ const TChar* str, size_t len) {
+ data_ = str;
+ length_ = len;
+ return *this;
}
template <typename TChar>
inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(
- BasicStringPiece<TChar>::const_iterator begin,
- BasicStringPiece<TChar>::const_iterator end) const {
- return BasicStringPiece<TChar>(begin, end - begin);
+ size_t start, size_t len) const {
+ if (len == npos) {
+ len = length_ - start;
+ }
+
+ if (start > length_ || start + len > length_) {
+ return BasicStringPiece<TChar>();
+ }
+ return BasicStringPiece<TChar>(data_ + start, len);
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(
+ BasicStringPiece<TChar>::const_iterator begin,
+ BasicStringPiece<TChar>::const_iterator end) const {
+ return BasicStringPiece<TChar>(begin, end - begin);
}
template <typename TChar>
inline const TChar* BasicStringPiece<TChar>::data() const {
- return mData;
+ return data_;
}
template <typename TChar>
inline size_t BasicStringPiece<TChar>::length() const {
- return mLength;
+ return length_;
}
template <typename TChar>
inline size_t BasicStringPiece<TChar>::size() const {
- return mLength;
+ return length_;
}
template <typename TChar>
inline bool BasicStringPiece<TChar>::empty() const {
- return mLength == 0;
+ return length_ == 0;
}
template <typename TChar>
-inline std::basic_string<TChar> BasicStringPiece<TChar>::toString() const {
- return std::basic_string<TChar>(mData, mLength);
+inline std::basic_string<TChar> BasicStringPiece<TChar>::ToString() const {
+ return std::basic_string<TChar>(data_, length_);
}
template <>
-inline bool BasicStringPiece<char>::contains(const BasicStringPiece<char>& rhs) const {
- if (!mData || !rhs.mData) {
- return false;
- }
- if (rhs.mLength > mLength) {
- return false;
- }
- return strstr(mData, rhs.mData) != nullptr;
+inline bool BasicStringPiece<char>::contains(
+ const BasicStringPiece<char>& rhs) const {
+ if (!data_ || !rhs.data_) {
+ return false;
+ }
+ if (rhs.length_ > length_) {
+ return false;
+ }
+ return strstr(data_, rhs.data_) != nullptr;
}
template <>
-inline int BasicStringPiece<char>::compare(const BasicStringPiece<char>& rhs) const {
- const char nullStr = '\0';
- const char* b1 = mData != nullptr ? mData : &nullStr;
- const char* e1 = b1 + mLength;
- const char* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
- const char* e2 = b2 + rhs.mLength;
+inline int BasicStringPiece<char>::compare(
+ const BasicStringPiece<char>& rhs) const {
+ const char nullStr = '\0';
+ const char* b1 = data_ != nullptr ? data_ : &nullStr;
+ const char* e1 = b1 + length_;
+ const char* b2 = rhs.data_ != nullptr ? rhs.data_ : &nullStr;
+ const char* e2 = b2 + rhs.length_;
- while (b1 < e1 && b2 < e2) {
- const int d = static_cast<int>(*b1++) - static_cast<int>(*b2++);
- if (d) {
- return d;
- }
+ while (b1 < e1 && b2 < e2) {
+ const int d = static_cast<int>(*b1++) - static_cast<int>(*b2++);
+ if (d) {
+ return d;
}
- return static_cast<int>(mLength - rhs.mLength);
+ }
+ return static_cast<int>(length_ - rhs.length_);
}
-inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char16_t>& str) {
- android::String8 utf8(str.data(), str.size());
- return out.write(utf8.string(), utf8.size());
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const BasicStringPiece<char16_t>& str) {
+ android::String8 utf8(str.data(), str.size());
+ return out.write(utf8.string(), utf8.size());
}
template <>
-inline bool BasicStringPiece<char16_t>::contains(const BasicStringPiece<char16_t>& rhs) const {
- if (!mData || !rhs.mData) {
- return false;
- }
- if (rhs.mLength > mLength) {
- return false;
- }
- return strstr16(mData, rhs.mData) != nullptr;
+inline bool BasicStringPiece<char16_t>::contains(
+ const BasicStringPiece<char16_t>& rhs) const {
+ if (!data_ || !rhs.data_) {
+ return false;
+ }
+ if (rhs.length_ > length_) {
+ return false;
+ }
+ return strstr16(data_, rhs.data_) != nullptr;
}
template <>
-inline int BasicStringPiece<char16_t>::compare(const BasicStringPiece<char16_t>& rhs) const {
- const char16_t nullStr = u'\0';
- const char16_t* b1 = mData != nullptr ? mData : &nullStr;
- const char16_t* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
- return strzcmp16(b1, mLength, b2, rhs.mLength);
+inline int BasicStringPiece<char16_t>::compare(
+ const BasicStringPiece<char16_t>& rhs) const {
+ const char16_t nullStr = u'\0';
+ const char16_t* b1 = data_ != nullptr ? data_ : &nullStr;
+ const char16_t* b2 = rhs.data_ != nullptr ? rhs.data_ : &nullStr;
+ return strzcmp16(b1, length_, b2, rhs.length_);
}
template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator<(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) < 0;
+inline bool BasicStringPiece<TChar>::operator<(
+ const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) < 0;
}
template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator>(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) > 0;
+inline bool BasicStringPiece<TChar>::operator>(
+ const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) > 0;
}
template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator==(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) == 0;
+inline bool BasicStringPiece<TChar>::operator==(
+ const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) == 0;
}
template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator!=(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) != 0;
+inline bool BasicStringPiece<TChar>::operator!=(
+ const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) != 0;
}
template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::begin() const {
- return mData;
+inline typename BasicStringPiece<TChar>::const_iterator
+BasicStringPiece<TChar>::begin() const {
+ return data_;
}
template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::end() const {
- return mData + mLength;
+inline typename BasicStringPiece<TChar>::const_iterator
+BasicStringPiece<TChar>::end() const {
+ return data_ + length_;
}
-inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char>& str) {
- return out.write(str.data(), str.size());
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const BasicStringPiece<char>& str) {
+ return out.write(str.data(), str.size());
}
-} // namespace aapt
+} // namespace aapt
-inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str) {
- android::String8 utf8(str.data(), str.size());
- return out.write(utf8.string(), utf8.size());
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const std::u16string& str) {
+ android::String8 utf8(str.data(), str.size());
+ return out.write(utf8.string(), utf8.size());
}
namespace std {
template <typename TChar>
struct hash<aapt::BasicStringPiece<TChar>> {
- size_t operator()(const aapt::BasicStringPiece<TChar>& str) const {
- uint32_t hashCode = android::JenkinsHashMixBytes(
- 0, reinterpret_cast<const uint8_t*>(str.data()), sizeof(TChar) * str.size());
- return static_cast<size_t>(hashCode);
- }
+ size_t operator()(const aapt::BasicStringPiece<TChar>& str) const {
+ uint32_t hashCode = android::JenkinsHashMixBytes(
+ 0, reinterpret_cast<const uint8_t*>(str.data()),
+ sizeof(TChar) * str.size());
+ return static_cast<size_t>(hashCode);
+ }
};
-} // namespace std
+} // namespace std
-#endif // AAPT_STRING_PIECE_H
+#endif // AAPT_STRING_PIECE_H
diff --git a/tools/aapt2/util/StringPiece_test.cpp b/tools/aapt2/util/StringPiece_test.cpp
index a87065a..048961d 100644
--- a/tools/aapt2/util/StringPiece_test.cpp
+++ b/tools/aapt2/util/StringPiece_test.cpp
@@ -14,81 +14,82 @@
* limitations under the License.
*/
+#include "util/StringPiece.h"
+
#include <algorithm>
-#include <gtest/gtest.h>
#include <string>
#include <vector>
-#include "util/StringPiece.h"
+#include "test/Test.h"
namespace aapt {
TEST(StringPieceTest, CompareNonNullTerminatedPiece) {
- StringPiece a("hello world", 5);
- StringPiece b("hello moon", 5);
- EXPECT_EQ(a, b);
+ StringPiece a("hello world", 5);
+ StringPiece b("hello moon", 5);
+ EXPECT_EQ(a, b);
- StringPiece16 a16(u"hello world", 5);
- StringPiece16 b16(u"hello moon", 5);
- EXPECT_EQ(a16, b16);
+ StringPiece16 a16(u"hello world", 5);
+ StringPiece16 b16(u"hello moon", 5);
+ EXPECT_EQ(a16, b16);
}
TEST(StringPieceTest, PiecesHaveCorrectSortOrder) {
- std::string testing("testing");
- std::string banana("banana");
- std::string car("car");
+ std::string testing("testing");
+ std::string banana("banana");
+ std::string car("car");
- EXPECT_TRUE(StringPiece(testing) > banana);
- EXPECT_TRUE(StringPiece(testing) > car);
- EXPECT_TRUE(StringPiece(banana) < testing);
- EXPECT_TRUE(StringPiece(banana) < car);
- EXPECT_TRUE(StringPiece(car) < testing);
- EXPECT_TRUE(StringPiece(car) > banana);
+ EXPECT_TRUE(StringPiece(testing) > banana);
+ EXPECT_TRUE(StringPiece(testing) > car);
+ EXPECT_TRUE(StringPiece(banana) < testing);
+ EXPECT_TRUE(StringPiece(banana) < car);
+ EXPECT_TRUE(StringPiece(car) < testing);
+ EXPECT_TRUE(StringPiece(car) > banana);
}
TEST(StringPieceTest, PiecesHaveCorrectSortOrderUtf8) {
- std::string testing("testing");
- std::string banana("banana");
- std::string car("car");
+ std::string testing("testing");
+ std::string banana("banana");
+ std::string car("car");
- EXPECT_TRUE(StringPiece(testing) > banana);
- EXPECT_TRUE(StringPiece(testing) > car);
- EXPECT_TRUE(StringPiece(banana) < testing);
- EXPECT_TRUE(StringPiece(banana) < car);
- EXPECT_TRUE(StringPiece(car) < testing);
- EXPECT_TRUE(StringPiece(car) > banana);
+ EXPECT_TRUE(StringPiece(testing) > banana);
+ EXPECT_TRUE(StringPiece(testing) > car);
+ EXPECT_TRUE(StringPiece(banana) < testing);
+ EXPECT_TRUE(StringPiece(banana) < car);
+ EXPECT_TRUE(StringPiece(car) < testing);
+ EXPECT_TRUE(StringPiece(car) > banana);
}
TEST(StringPieceTest, ContainsOtherStringPiece) {
- StringPiece text("I am a leaf on the wind.");
- StringPiece startNeedle("I am");
- StringPiece endNeedle("wind.");
- StringPiece middleNeedle("leaf");
- StringPiece emptyNeedle("");
- StringPiece missingNeedle("soar");
- StringPiece longNeedle("This string is longer than the text.");
+ StringPiece text("I am a leaf on the wind.");
+ StringPiece start_needle("I am");
+ StringPiece end_needle("wind.");
+ StringPiece middle_needle("leaf");
+ StringPiece empty_needle("");
+ StringPiece missing_needle("soar");
+ StringPiece long_needle("This string is longer than the text.");
- EXPECT_TRUE(text.contains(startNeedle));
- EXPECT_TRUE(text.contains(endNeedle));
- EXPECT_TRUE(text.contains(middleNeedle));
- EXPECT_TRUE(text.contains(emptyNeedle));
- EXPECT_FALSE(text.contains(missingNeedle));
- EXPECT_FALSE(text.contains(longNeedle));
+ EXPECT_TRUE(text.contains(start_needle));
+ EXPECT_TRUE(text.contains(end_needle));
+ EXPECT_TRUE(text.contains(middle_needle));
+ EXPECT_TRUE(text.contains(empty_needle));
+ EXPECT_FALSE(text.contains(missing_needle));
+ EXPECT_FALSE(text.contains(long_needle));
- StringPiece16 text16(u"I am a leaf on the wind.");
- StringPiece16 startNeedle16(u"I am");
- StringPiece16 endNeedle16(u"wind.");
- StringPiece16 middleNeedle16(u"leaf");
- StringPiece16 emptyNeedle16(u"");
- StringPiece16 missingNeedle16(u"soar");
- StringPiece16 longNeedle16(u"This string is longer than the text.");
+ StringPiece16 text16(u"I am a leaf on the wind.");
+ StringPiece16 start_needle16(u"I am");
+ StringPiece16 end_needle16(u"wind.");
+ StringPiece16 middle_needle16(u"leaf");
+ StringPiece16 empty_needle16(u"");
+ StringPiece16 missing_needle16(u"soar");
+ StringPiece16 long_needle16(u"This string is longer than the text.");
- EXPECT_TRUE(text16.contains(startNeedle16));
- EXPECT_TRUE(text16.contains(endNeedle16));
- EXPECT_TRUE(text16.contains(middleNeedle16));
- EXPECT_TRUE(text16.contains(emptyNeedle16));
- EXPECT_FALSE(text16.contains(missingNeedle16));
- EXPECT_FALSE(text16.contains(longNeedle16));
+ EXPECT_TRUE(text16.contains(start_needle16));
+ EXPECT_TRUE(text16.contains(end_needle16));
+ EXPECT_TRUE(text16.contains(middle_needle16));
+ EXPECT_TRUE(text16.contains(empty_needle16));
+ EXPECT_FALSE(text16.contains(missing_needle16));
+ EXPECT_FALSE(text16.contains(long_needle16));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/util/TypeTraits.h b/tools/aapt2/util/TypeTraits.h
index 76c13d6..b6539ed 100644
--- a/tools/aapt2/util/TypeTraits.h
+++ b/tools/aapt2/util/TypeTraits.h
@@ -21,19 +21,20 @@
namespace aapt {
-#define DEFINE_HAS_BINARY_OP_TRAIT(name, op) \
- template <typename T, typename U> \
- struct name { \
- template <typename V, typename W> \
- static constexpr decltype(std::declval<V>() op std::declval<W>(), bool()) test(int) { \
- return true; \
- } \
- template <typename V, typename W> \
- static constexpr bool test(...) { \
- return false; \
- } \
- static constexpr bool value = test<T, U>(int()); \
-}
+#define DEFINE_HAS_BINARY_OP_TRAIT(name, op) \
+ template <typename T, typename U> \
+ struct name { \
+ template <typename V, typename W> \
+ static constexpr decltype(std::declval<V>() op std::declval<W>(), bool()) \
+ test(int) { \
+ return true; \
+ } \
+ template <typename V, typename W> \
+ static constexpr bool test(...) { \
+ return false; \
+ } \
+ static constexpr bool value = test<T, U>(int()); \
+ }
DEFINE_HAS_BINARY_OP_TRAIT(has_eq_op, ==);
DEFINE_HAS_BINARY_OP_TRAIT(has_lt_op, <);
@@ -43,9 +44,10 @@
*/
template <typename T, typename U>
struct is_comparable {
- static constexpr bool value = has_eq_op<T, U>::value && has_lt_op<T, U>::value;
+ static constexpr bool value =
+ has_eq_op<T, U>::value && has_lt_op<T, U>::value;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_UTIL_TYPETRAITS_H */
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index b0bec62..d5c0c8a 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -14,559 +14,558 @@
* limitations under the License.
*/
+#include "util/Util.h"
#include "util/BigBuffer.h"
#include "util/Maybe.h"
#include "util/StringPiece.h"
-#include "util/Util.h"
+#include <utils/Unicode.h>
#include <algorithm>
#include <ostream>
#include <string>
-#include <utils/Unicode.h>
#include <vector>
namespace aapt {
namespace util {
-static std::vector<std::string> splitAndTransform(const StringPiece& str, char sep,
- const std::function<char(char)>& f) {
- std::vector<std::string> parts;
- const StringPiece::const_iterator end = std::end(str);
- StringPiece::const_iterator start = std::begin(str);
- StringPiece::const_iterator current;
- do {
- current = std::find(start, end, sep);
- parts.emplace_back(str.substr(start, current).toString());
- if (f) {
- std::string& part = parts.back();
- std::transform(part.begin(), part.end(), part.begin(), f);
- }
- start = current + 1;
- } while (current != end);
- return parts;
+static std::vector<std::string> SplitAndTransform(
+ const StringPiece& str, char sep, const std::function<char(char)>& f) {
+ std::vector<std::string> parts;
+ const StringPiece::const_iterator end = std::end(str);
+ StringPiece::const_iterator start = std::begin(str);
+ StringPiece::const_iterator current;
+ do {
+ current = std::find(start, end, sep);
+ parts.emplace_back(str.substr(start, current).ToString());
+ if (f) {
+ std::string& part = parts.back();
+ std::transform(part.begin(), part.end(), part.begin(), f);
+ }
+ start = current + 1;
+ } while (current != end);
+ return parts;
}
-std::vector<std::string> split(const StringPiece& str, char sep) {
- return splitAndTransform(str, sep, nullptr);
+std::vector<std::string> Split(const StringPiece& str, char sep) {
+ return SplitAndTransform(str, sep, nullptr);
}
-std::vector<std::string> splitAndLowercase(const StringPiece& str, char sep) {
- return splitAndTransform(str, sep, ::tolower);
+std::vector<std::string> SplitAndLowercase(const StringPiece& str, char sep) {
+ return SplitAndTransform(str, sep, ::tolower);
}
-bool stringStartsWith(const StringPiece& str, const StringPiece& prefix) {
- if (str.size() < prefix.size()) {
- return false;
- }
- return str.substr(0, prefix.size()) == prefix;
+bool StartsWith(const StringPiece& str, const StringPiece& prefix) {
+ if (str.size() < prefix.size()) {
+ return false;
+ }
+ return str.substr(0, prefix.size()) == prefix;
}
-bool stringEndsWith(const StringPiece& str, const StringPiece& suffix) {
- if (str.size() < suffix.size()) {
- return false;
- }
- return str.substr(str.size() - suffix.size(), suffix.size()) == suffix;
+bool EndsWith(const StringPiece& str, const StringPiece& suffix) {
+ if (str.size() < suffix.size()) {
+ return false;
+ }
+ return str.substr(str.size() - suffix.size(), suffix.size()) == suffix;
}
-StringPiece trimWhitespace(const StringPiece& str) {
- if (str.size() == 0 || str.data() == nullptr) {
- return str;
- }
+StringPiece TrimWhitespace(const StringPiece& str) {
+ if (str.size() == 0 || str.data() == nullptr) {
+ return str;
+ }
- const char* start = str.data();
- const char* end = str.data() + str.length();
+ const char* start = str.data();
+ const char* end = str.data() + str.length();
- while (start != end && isspace(*start)) {
- start++;
- }
+ while (start != end && isspace(*start)) {
+ start++;
+ }
- while (end != start && isspace(*(end - 1))) {
- end--;
- }
+ while (end != start && isspace(*(end - 1))) {
+ end--;
+ }
- return StringPiece(start, end - start);
+ return StringPiece(start, end - start);
}
-StringPiece::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece& str,
- const StringPiece& allowedChars) {
- const auto endIter = str.end();
- for (auto iter = str.begin(); iter != endIter; ++iter) {
- char c = *iter;
- if ((c >= u'a' && c <= u'z') ||
- (c >= u'A' && c <= u'Z') ||
- (c >= u'0' && c <= u'9')) {
- continue;
- }
-
- bool match = false;
- for (char i : allowedChars) {
- if (c == i) {
- match = true;
- break;
- }
- }
-
- if (!match) {
- return iter;
- }
+StringPiece::const_iterator FindNonAlphaNumericAndNotInSet(
+ const StringPiece& str, const StringPiece& allowed_chars) {
+ const auto end_iter = str.end();
+ for (auto iter = str.begin(); iter != end_iter; ++iter) {
+ char c = *iter;
+ if ((c >= u'a' && c <= u'z') || (c >= u'A' && c <= u'Z') ||
+ (c >= u'0' && c <= u'9')) {
+ continue;
}
- return endIter;
+
+ bool match = false;
+ for (char i : allowed_chars) {
+ if (c == i) {
+ match = true;
+ break;
+ }
+ }
+
+ if (!match) {
+ return iter;
+ }
+ }
+ return end_iter;
}
-bool isJavaClassName(const StringPiece& str) {
- size_t pieces = 0;
- for (const StringPiece& piece : tokenize(str, '.')) {
- pieces++;
- if (piece.empty()) {
- return false;
- }
-
- // Can't have starting or trailing $ character.
- if (piece.data()[0] == '$' || piece.data()[piece.size() - 1] == '$') {
- return false;
- }
-
- if (findNonAlphaNumericAndNotInSet(piece, "$_") != piece.end()) {
- return false;
- }
+bool IsJavaClassName(const StringPiece& str) {
+ size_t pieces = 0;
+ for (const StringPiece& piece : Tokenize(str, '.')) {
+ pieces++;
+ if (piece.empty()) {
+ return false;
}
- return pieces >= 2;
+
+ // Can't have starting or trailing $ character.
+ if (piece.data()[0] == '$' || piece.data()[piece.size() - 1] == '$') {
+ return false;
+ }
+
+ if (FindNonAlphaNumericAndNotInSet(piece, "$_") != piece.end()) {
+ return false;
+ }
+ }
+ return pieces >= 2;
}
-bool isJavaPackageName(const StringPiece& str) {
- if (str.empty()) {
- return false;
+bool IsJavaPackageName(const StringPiece& str) {
+ if (str.empty()) {
+ return false;
+ }
+
+ size_t pieces = 0;
+ for (const StringPiece& piece : Tokenize(str, '.')) {
+ pieces++;
+ if (piece.empty()) {
+ return false;
}
- size_t pieces = 0;
- for (const StringPiece& piece : tokenize(str, '.')) {
- pieces++;
- if (piece.empty()) {
- return false;
- }
-
- if (piece.data()[0] == '_' || piece.data()[piece.size() - 1] == '_') {
- return false;
- }
-
- if (findNonAlphaNumericAndNotInSet(piece, "_") != piece.end()) {
- return false;
- }
+ if (piece.data()[0] == '_' || piece.data()[piece.size() - 1] == '_') {
+ return false;
}
- return pieces >= 1;
+
+ if (FindNonAlphaNumericAndNotInSet(piece, "_") != piece.end()) {
+ return false;
+ }
+ }
+ return pieces >= 1;
}
-Maybe<std::string> getFullyQualifiedClassName(const StringPiece& package,
- const StringPiece& className) {
- if (className.empty()) {
- return {};
- }
+Maybe<std::string> GetFullyQualifiedClassName(const StringPiece& package,
+ const StringPiece& classname) {
+ if (classname.empty()) {
+ return {};
+ }
- if (util::isJavaClassName(className)) {
- return className.toString();
- }
+ if (util::IsJavaClassName(classname)) {
+ return classname.ToString();
+ }
- if (package.empty()) {
- return {};
- }
+ if (package.empty()) {
+ return {};
+ }
- std::string result(package.data(), package.size());
- if (className.data()[0] != '.') {
- result += '.';
- }
+ std::string result(package.data(), package.size());
+ if (classname.data()[0] != '.') {
+ result += '.';
+ }
- result.append(className.data(), className.size());
- if (!isJavaClassName(result)) {
- return {};
- }
- return result;
+ result.append(classname.data(), classname.size());
+ if (!IsJavaClassName(result)) {
+ return {};
+ }
+ return result;
}
-static size_t consumeDigits(const char* start, const char* end) {
- const char* c = start;
- for (; c != end && *c >= '0' && *c <= '9'; c++) {}
- return static_cast<size_t>(c - start);
+static size_t ConsumeDigits(const char* start, const char* end) {
+ const char* c = start;
+ for (; c != end && *c >= '0' && *c <= '9'; c++) {
+ }
+ return static_cast<size_t>(c - start);
}
-bool verifyJavaStringFormat(const StringPiece& str) {
- const char* c = str.begin();
- const char* const end = str.end();
+bool VerifyJavaStringFormat(const StringPiece& str) {
+ const char* c = str.begin();
+ const char* const end = str.end();
- size_t argCount = 0;
- bool nonpositional = false;
- while (c != end) {
- if (*c == '%' && c + 1 < end) {
- c++;
+ size_t arg_count = 0;
+ bool nonpositional = false;
+ while (c != end) {
+ if (*c == '%' && c + 1 < end) {
+ c++;
- if (*c == '%') {
- c++;
- continue;
- }
+ if (*c == '%') {
+ c++;
+ continue;
+ }
- argCount++;
+ arg_count++;
- size_t numDigits = consumeDigits(c, end);
- if (numDigits > 0) {
- c += numDigits;
- if (c != end && *c != '$') {
- // The digits were a size, but not a positional argument.
- nonpositional = true;
- }
- } else if (*c == '<') {
- // Reusing last argument, bad idea since positions can be moved around
- // during translation.
- nonpositional = true;
-
- c++;
-
- // Optionally we can have a $ after
- if (c != end && *c == '$') {
- c++;
- }
- } else {
- nonpositional = true;
- }
-
- // Ignore size, width, flags, etc.
- while (c != end && (*c == '-' ||
- *c == '#' ||
- *c == '+' ||
- *c == ' ' ||
- *c == ',' ||
- *c == '(' ||
- (*c >= '0' && *c <= '9'))) {
- c++;
- }
-
- /*
- * This is a shortcut to detect strings that are going to Time.format()
- * instead of String.format()
- *
- * Comparison of String.format() and Time.format() args:
- *
- * String: ABC E GH ST X abcdefgh nost x
- * Time: DEFGHKMS W Za d hkm s w yz
- *
- * Therefore we know it's definitely Time if we have:
- * DFKMWZkmwyz
- */
- if (c != end) {
- switch (*c) {
- case 'D':
- case 'F':
- case 'K':
- case 'M':
- case 'W':
- case 'Z':
- case 'k':
- case 'm':
- case 'w':
- case 'y':
- case 'z':
- return true;
- }
- }
+ size_t num_digits = ConsumeDigits(c, end);
+ if (num_digits > 0) {
+ c += num_digits;
+ if (c != end && *c != '$') {
+ // The digits were a size, but not a positional argument.
+ nonpositional = true;
}
+ } else if (*c == '<') {
+ // Reusing last argument, bad idea since positions can be moved around
+ // during translation.
+ nonpositional = true;
- if (c != end) {
- c++;
+ c++;
+
+ // Optionally we can have a $ after
+ if (c != end && *c == '$') {
+ c++;
}
+ } else {
+ nonpositional = true;
+ }
+
+ // Ignore size, width, flags, etc.
+ while (c != end && (*c == '-' || *c == '#' || *c == '+' || *c == ' ' ||
+ *c == ',' || *c == '(' || (*c >= '0' && *c <= '9'))) {
+ c++;
+ }
+
+ /*
+ * This is a shortcut to detect strings that are going to Time.format()
+ * instead of String.format()
+ *
+ * Comparison of String.format() and Time.format() args:
+ *
+ * String: ABC E GH ST X abcdefgh nost x
+ * Time: DEFGHKMS W Za d hkm s w yz
+ *
+ * Therefore we know it's definitely Time if we have:
+ * DFKMWZkmwyz
+ */
+ if (c != end) {
+ switch (*c) {
+ case 'D':
+ case 'F':
+ case 'K':
+ case 'M':
+ case 'W':
+ case 'Z':
+ case 'k':
+ case 'm':
+ case 'w':
+ case 'y':
+ case 'z':
+ return true;
+ }
+ }
}
- if (argCount > 1 && nonpositional) {
- // Multiple arguments were specified, but some or all were non positional. Translated
- // strings may rearrange the order of the arguments, which will break the string.
- return false;
+ if (c != end) {
+ c++;
}
- return true;
+ }
+
+ if (arg_count > 1 && nonpositional) {
+ // Multiple arguments were specified, but some or all were non positional.
+ // Translated
+ // strings may rearrange the order of the arguments, which will break the
+ // string.
+ return false;
+ }
+ return true;
}
-static Maybe<std::string> parseUnicodeCodepoint(const char** start, const char* end) {
- char32_t code = 0;
- for (size_t i = 0; i < 4 && *start != end; i++, (*start)++) {
- char c = **start;
- char32_t a;
- if (c >= '0' && c <= '9') {
- a = c - '0';
- } else if (c >= 'a' && c <= 'f') {
- a = c - 'a' + 10;
- } else if (c >= 'A' && c <= 'F') {
- a = c - 'A' + 10;
- } else {
- return {};
- }
- code = (code << 4) | a;
+static Maybe<std::string> ParseUnicodeCodepoint(const char** start,
+ const char* end) {
+ char32_t code = 0;
+ for (size_t i = 0; i < 4 && *start != end; i++, (*start)++) {
+ char c = **start;
+ char32_t a;
+ if (c >= '0' && c <= '9') {
+ a = c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ a = c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ a = c - 'A' + 10;
+ } else {
+ return {};
}
+ code = (code << 4) | a;
+ }
- ssize_t len = utf32_to_utf8_length(&code, 1);
- if (len < 0) {
- return {};
- }
+ ssize_t len = utf32_to_utf8_length(&code, 1);
+ if (len < 0) {
+ return {};
+ }
- std::string resultUtf8;
- resultUtf8.resize(len);
- utf32_to_utf8(&code, 1, &*resultUtf8.begin(), len + 1);
- return resultUtf8;
+ std::string result_utf8;
+ result_utf8.resize(len);
+ utf32_to_utf8(&code, 1, &*result_utf8.begin(), len + 1);
+ return result_utf8;
}
-StringBuilder& StringBuilder::append(const StringPiece& str) {
- if (!mError.empty()) {
- return *this;
- }
-
- // Where the new data will be appended to.
- size_t newDataIndex = mStr.size();
-
- const char* const end = str.end();
- const char* start = str.begin();
- const char* current = start;
- while (current != end) {
- if (mLastCharWasEscape) {
- switch (*current) {
- case 't':
- mStr += '\t';
- break;
- case 'n':
- mStr += '\n';
- break;
- case '#':
- mStr += '#';
- break;
- case '@':
- mStr += '@';
- break;
- case '?':
- mStr += '?';
- break;
- case '"':
- mStr += '"';
- break;
- case '\'':
- mStr += '\'';
- break;
- case '\\':
- mStr += '\\';
- break;
- case 'u': {
- current++;
- Maybe<std::string> c = parseUnicodeCodepoint(¤t, end);
- if (!c) {
- mError = "invalid unicode escape sequence";
- return *this;
- }
- mStr += c.value();
- current -= 1;
- break;
- }
-
- default:
- // Ignore.
- break;
- }
- mLastCharWasEscape = false;
- start = current + 1;
- } else if (*current == '"') {
- if (!mQuote && mTrailingSpace) {
- // We found an opening quote, and we have
- // trailing space, so we should append that
- // space now.
- if (mTrailingSpace) {
- // We had trailing whitespace, so
- // replace with a single space.
- if (!mStr.empty()) {
- mStr += ' ';
- }
- mTrailingSpace = false;
- }
- }
- mQuote = !mQuote;
- mStr.append(start, current - start);
- start = current + 1;
- } else if (*current == '\'' && !mQuote) {
- // This should be escaped.
- mError = "unescaped apostrophe";
- return *this;
- } else if (*current == '\\') {
- // This is an escape sequence, convert to the real value.
- if (!mQuote && mTrailingSpace) {
- // We had trailing whitespace, so
- // replace with a single space.
- if (!mStr.empty()) {
- mStr += ' ';
- }
- mTrailingSpace = false;
- }
- mStr.append(start, current - start);
- start = current + 1;
- mLastCharWasEscape = true;
- } else if (!mQuote) {
- // This is not quoted text, so look for whitespace.
- if (isspace(*current)) {
- // We found whitespace, see if we have seen some
- // before.
- if (!mTrailingSpace) {
- // We didn't see a previous adjacent space,
- // so mark that we did.
- mTrailingSpace = true;
- mStr.append(start, current - start);
- }
-
- // Keep skipping whitespace.
- start = current + 1;
- } else if (mTrailingSpace) {
- // We saw trailing space before, so replace all
- // that trailing space with one space.
- if (!mStr.empty()) {
- mStr += ' ';
- }
- mTrailingSpace = false;
- }
- }
- current++;
- }
- mStr.append(start, end - start);
-
- // Accumulate the added string's UTF-16 length.
- ssize_t len = utf8_to_utf16_length(
- reinterpret_cast<const uint8_t*>(mStr.data()) + newDataIndex,
- mStr.size() - newDataIndex);
- if (len < 0) {
- mError = "invalid unicode code point";
- return *this;
- }
- mUtf16Len += len;
+StringBuilder& StringBuilder::Append(const StringPiece& str) {
+ if (!error_.empty()) {
return *this;
-}
+ }
-std::u16string utf8ToUtf16(const StringPiece& utf8) {
- ssize_t utf16Length = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(utf8.data()),
- utf8.length());
- if (utf16Length <= 0) {
- return {};
- }
+ // Where the new data will be appended to.
+ size_t new_data_index = str_.size();
- std::u16string utf16;
- utf16.resize(utf16Length);
- utf8_to_utf16(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length(),
- &*utf16.begin(), utf16Length + 1);
- return utf16;
-}
-
-std::string utf16ToUtf8(const StringPiece16& utf16) {
- ssize_t utf8Length = utf16_to_utf8_length(utf16.data(), utf16.length());
- if (utf8Length <= 0) {
- return {};
- }
-
- std::string utf8;
- utf8.resize(utf8Length);
- utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin(), utf8Length + 1);
- return utf8;
-}
-
-bool writeAll(std::ostream& out, const BigBuffer& buffer) {
- for (const auto& b : buffer) {
- if (!out.write(reinterpret_cast<const char*>(b.buffer.get()), b.size)) {
- return false;
+ const char* const end = str.end();
+ const char* start = str.begin();
+ const char* current = start;
+ while (current != end) {
+ if (last_char_was_escape_) {
+ switch (*current) {
+ case 't':
+ str_ += '\t';
+ break;
+ case 'n':
+ str_ += '\n';
+ break;
+ case '#':
+ str_ += '#';
+ break;
+ case '@':
+ str_ += '@';
+ break;
+ case '?':
+ str_ += '?';
+ break;
+ case '"':
+ str_ += '"';
+ break;
+ case '\'':
+ str_ += '\'';
+ break;
+ case '\\':
+ str_ += '\\';
+ break;
+ case 'u': {
+ current++;
+ Maybe<std::string> c = ParseUnicodeCodepoint(¤t, end);
+ if (!c) {
+ error_ = "invalid unicode escape sequence";
+ return *this;
+ }
+ str_ += c.value();
+ current -= 1;
+ break;
}
+
+ default:
+ // Ignore.
+ break;
+ }
+ last_char_was_escape_ = false;
+ start = current + 1;
+ } else if (*current == '"') {
+ if (!quote_ && trailing_space_) {
+ // We found an opening quote, and we have
+ // trailing space, so we should append that
+ // space now.
+ if (trailing_space_) {
+ // We had trailing whitespace, so
+ // replace with a single space.
+ if (!str_.empty()) {
+ str_ += ' ';
+ }
+ trailing_space_ = false;
+ }
+ }
+ quote_ = !quote_;
+ str_.append(start, current - start);
+ start = current + 1;
+ } else if (*current == '\'' && !quote_) {
+ // This should be escaped.
+ error_ = "unescaped apostrophe";
+ return *this;
+ } else if (*current == '\\') {
+ // This is an escape sequence, convert to the real value.
+ if (!quote_ && trailing_space_) {
+ // We had trailing whitespace, so
+ // replace with a single space.
+ if (!str_.empty()) {
+ str_ += ' ';
+ }
+ trailing_space_ = false;
+ }
+ str_.append(start, current - start);
+ start = current + 1;
+ last_char_was_escape_ = true;
+ } else if (!quote_) {
+ // This is not quoted text, so look for whitespace.
+ if (isspace(*current)) {
+ // We found whitespace, see if we have seen some
+ // before.
+ if (!trailing_space_) {
+ // We didn't see a previous adjacent space,
+ // so mark that we did.
+ trailing_space_ = true;
+ str_.append(start, current - start);
+ }
+
+ // Keep skipping whitespace.
+ start = current + 1;
+ } else if (trailing_space_) {
+ // We saw trailing space before, so replace all
+ // that trailing space with one space.
+ if (!str_.empty()) {
+ str_ += ' ';
+ }
+ trailing_space_ = false;
+ }
}
- return true;
+ current++;
+ }
+ str_.append(start, end - start);
+
+ // Accumulate the added string's UTF-16 length.
+ ssize_t len = utf8_to_utf16_length(
+ reinterpret_cast<const uint8_t*>(str_.data()) + new_data_index,
+ str_.size() - new_data_index);
+ if (len < 0) {
+ error_ = "invalid unicode code point";
+ return *this;
+ }
+ utf16_len_ += len;
+ return *this;
}
-std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer) {
- std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[buffer.size()]);
- uint8_t* p = data.get();
- for (const auto& block : buffer) {
- memcpy(p, block.buffer.get(), block.size);
- p += block.size;
+std::u16string Utf8ToUtf16(const StringPiece& utf8) {
+ ssize_t utf16_length = utf8_to_utf16_length(
+ reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
+ if (utf16_length <= 0) {
+ return {};
+ }
+
+ std::u16string utf16;
+ utf16.resize(utf16_length);
+ utf8_to_utf16(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length(),
+ &*utf16.begin(), utf16_length + 1);
+ return utf16;
+}
+
+std::string Utf16ToUtf8(const StringPiece16& utf16) {
+ ssize_t utf8_length = utf16_to_utf8_length(utf16.data(), utf16.length());
+ if (utf8_length <= 0) {
+ return {};
+ }
+
+ std::string utf8;
+ utf8.resize(utf8_length);
+ utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin(), utf8_length + 1);
+ return utf8;
+}
+
+bool WriteAll(std::ostream& out, const BigBuffer& buffer) {
+ for (const auto& b : buffer) {
+ if (!out.write(reinterpret_cast<const char*>(b.buffer.get()), b.size)) {
+ return false;
}
- return data;
+ }
+ return true;
+}
+
+std::unique_ptr<uint8_t[]> Copy(const BigBuffer& buffer) {
+ std::unique_ptr<uint8_t[]> data =
+ std::unique_ptr<uint8_t[]>(new uint8_t[buffer.size()]);
+ uint8_t* p = data.get();
+ for (const auto& block : buffer) {
+ memcpy(p, block.buffer.get(), block.size);
+ p += block.size;
+ }
+ return data;
}
typename Tokenizer::iterator& Tokenizer::iterator::operator++() {
- const char* start = mToken.end();
- const char* end = mStr.end();
- if (start == end) {
- mEnd = true;
- mToken.assign(mToken.end(), 0);
- return *this;
- }
-
- start += 1;
- const char* current = start;
- while (current != end) {
- if (*current == mSeparator) {
- mToken.assign(start, current - start);
- return *this;
- }
- ++current;
- }
- mToken.assign(start, end - start);
+ const char* start = token_.end();
+ const char* end = str_.end();
+ if (start == end) {
+ end_ = true;
+ token_.assign(token_.end(), 0);
return *this;
+ }
+
+ start += 1;
+ const char* current = start;
+ while (current != end) {
+ if (*current == separator_) {
+ token_.assign(start, current - start);
+ return *this;
+ }
+ ++current;
+ }
+ token_.assign(start, end - start);
+ return *this;
}
bool Tokenizer::iterator::operator==(const iterator& rhs) const {
- // We check equality here a bit differently.
- // We need to know that the addresses are the same.
- return mToken.begin() == rhs.mToken.begin() && mToken.end() == rhs.mToken.end() &&
- mEnd == rhs.mEnd;
+ // We check equality here a bit differently.
+ // We need to know that the addresses are the same.
+ return token_.begin() == rhs.token_.begin() &&
+ token_.end() == rhs.token_.end() && end_ == rhs.end_;
}
bool Tokenizer::iterator::operator!=(const iterator& rhs) const {
- return !(*this == rhs);
+ return !(*this == rhs);
}
-Tokenizer::iterator::iterator(StringPiece s, char sep, StringPiece tok, bool end) :
- mStr(s), mSeparator(sep), mToken(tok), mEnd(end) {
-}
+Tokenizer::iterator::iterator(StringPiece s, char sep, StringPiece tok,
+ bool end)
+ : str_(s), separator_(sep), token_(tok), end_(end) {}
-Tokenizer::Tokenizer(StringPiece str, char sep) :
- mBegin(++iterator(str, sep, StringPiece(str.begin() - 1, 0), false)),
- mEnd(str, sep, StringPiece(str.end(), 0), true) {
-}
+Tokenizer::Tokenizer(StringPiece str, char sep)
+ : begin_(++iterator(str, sep, StringPiece(str.begin() - 1, 0), false)),
+ end_(str, sep, StringPiece(str.end(), 0), true) {}
-bool extractResFilePathParts(const StringPiece& path, StringPiece* outPrefix,
- StringPiece* outEntry, StringPiece* outSuffix) {
- const StringPiece resPrefix("res/");
- if (!stringStartsWith(path, resPrefix)) {
- return false;
+bool ExtractResFilePathParts(const StringPiece& path, StringPiece* out_prefix,
+ StringPiece* out_entry, StringPiece* out_suffix) {
+ const StringPiece res_prefix("res/");
+ if (!StartsWith(path, res_prefix)) {
+ return false;
+ }
+
+ StringPiece::const_iterator last_occurence = path.end();
+ for (auto iter = path.begin() + res_prefix.size(); iter != path.end();
+ ++iter) {
+ if (*iter == '/') {
+ last_occurence = iter;
}
+ }
- StringPiece::const_iterator lastOccurence = path.end();
- for (auto iter = path.begin() + resPrefix.size(); iter != path.end(); ++iter) {
- if (*iter == '/') {
- lastOccurence = iter;
- }
- }
+ if (last_occurence == path.end()) {
+ return false;
+ }
- if (lastOccurence == path.end()) {
- return false;
- }
-
- auto iter = std::find(lastOccurence, path.end(), '.');
- *outSuffix = StringPiece(iter, path.end() - iter);
- *outEntry = StringPiece(lastOccurence + 1, iter - lastOccurence - 1);
- *outPrefix = StringPiece(path.begin(), lastOccurence - path.begin() + 1);
- return true;
+ auto iter = std::find(last_occurence, path.end(), '.');
+ *out_suffix = StringPiece(iter, path.end() - iter);
+ *out_entry = StringPiece(last_occurence + 1, iter - last_occurence - 1);
+ *out_prefix = StringPiece(path.begin(), last_occurence - path.begin() + 1);
+ return true;
}
-StringPiece16 getString16(const android::ResStringPool& pool, size_t idx) {
- size_t len;
- const char16_t* str = pool.stringAt(idx, &len);
- if (str != nullptr) {
- return StringPiece16(str, len);
- }
- return StringPiece16();
+StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) {
+ size_t len;
+ const char16_t* str = pool.stringAt(idx, &len);
+ if (str != nullptr) {
+ return StringPiece16(str, len);
+ }
+ return StringPiece16();
}
-std::string getString(const android::ResStringPool& pool, size_t idx) {
- size_t len;
- const char* str = pool.string8At(idx, &len);
- if (str != nullptr) {
- return std::string(str, len);
- }
- return utf16ToUtf8(getString16(pool, idx));
+std::string GetString(const android::ResStringPool& pool, size_t idx) {
+ size_t len;
+ const char* str = pool.string8At(idx, &len);
+ if (str != nullptr) {
+ return std::string(str, len);
+ }
+ return Utf16ToUtf8(GetString16(pool, idx));
}
-} // namespace util
-} // namespace aapt
+} // namespace util
+} // namespace aapt
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 9c88354..05e9cc5 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -17,264 +17,246 @@
#ifndef AAPT_UTIL_H
#define AAPT_UTIL_H
-#include "util/BigBuffer.h"
-#include "util/Maybe.h"
-#include "util/StringPiece.h"
-
-#include <androidfw/ResourceTypes.h>
#include <functional>
#include <memory>
#include <ostream>
#include <string>
#include <vector>
+#include "androidfw/ResourceTypes.h"
+#include "utils/ByteOrder.h"
+
+#include "util/BigBuffer.h"
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+
+#ifdef _WIN32
+// TODO(adamlesinski): remove once http://b/32447322 is resolved.
+// utils/ByteOrder.h includes winsock2.h on WIN32,
+// which will pull in the ERROR definition. This conflicts
+// with android-base/logging.h, which takes care of undefining
+// ERROR, but it gets included too early (before winsock2.h).
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
namespace aapt {
namespace util {
-std::vector<std::string> split(const StringPiece& str, char sep);
-std::vector<std::string> splitAndLowercase(const StringPiece& str, char sep);
+std::vector<std::string> Split(const StringPiece& str, char sep);
+std::vector<std::string> SplitAndLowercase(const StringPiece& str, char sep);
/**
* Returns true if the string starts with prefix.
*/
-bool stringStartsWith(const StringPiece& str, const StringPiece& prefix);
+bool StartsWith(const StringPiece& str, const StringPiece& prefix);
/**
* Returns true if the string ends with suffix.
*/
-bool stringEndsWith(const StringPiece& str, const StringPiece& suffix);
+bool EndsWith(const StringPiece& str, const StringPiece& suffix);
/**
* Creates a new StringPiece16 that points to a substring
* of the original string without leading or trailing whitespace.
*/
-StringPiece trimWhitespace(const StringPiece& str);
+StringPiece TrimWhitespace(const StringPiece& str);
-StringPiece trimWhitespace(const StringPiece& str);
+StringPiece TrimWhitespace(const StringPiece& str);
/**
* UTF-16 isspace(). It basically checks for lower range characters that are
* whitespace.
*/
-inline bool isspace16(char16_t c) {
- return c < 0x0080 && isspace(c);
-}
+inline bool isspace16(char16_t c) { return c < 0x0080 && isspace(c); }
/**
* Returns an iterator to the first character that is not alpha-numeric and that
* is not in the allowedChars set.
*/
-StringPiece::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece& str,
- const StringPiece& allowedChars);
+StringPiece::const_iterator FindNonAlphaNumericAndNotInSet(
+ const StringPiece& str, const StringPiece& allowed_chars);
/**
* Tests that the string is a valid Java class name.
*/
-bool isJavaClassName(const StringPiece& str);
+bool IsJavaClassName(const StringPiece& str);
/**
* Tests that the string is a valid Java package name.
*/
-bool isJavaPackageName(const StringPiece& str);
+bool IsJavaPackageName(const StringPiece& str);
/**
- * Converts the class name to a fully qualified class name from the given `package`. Ex:
+ * Converts the class name to a fully qualified class name from the given
+ * `package`. Ex:
*
* asdf --> package.asdf
* .asdf --> package.asdf
* .a.b --> package.a.b
* asdf.adsf --> asdf.adsf
*/
-Maybe<std::string> getFullyQualifiedClassName(const StringPiece& package,
- const StringPiece& className);
+Maybe<std::string> GetFullyQualifiedClassName(const StringPiece& package,
+ const StringPiece& class_name);
/**
- * Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
+ * Makes a std::unique_ptr<> with the template parameter inferred by the
+ * compiler.
* This will be present in C++14 and can be removed then.
*/
template <typename T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
- return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
+ return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
/**
- * Writes a set of items to the std::ostream, joining the times with the provided
+ * Writes a set of items to the std::ostream, joining the times with the
+ * provided
* separator.
*/
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;
- }
- return out;
- };
-}
-
-inline ::std::function<::std::ostream&(::std::ostream&)> formatSize(size_t size) {
- return [size](::std::ostream& out) -> ::std::ostream& {
- constexpr size_t K = 1024u;
- constexpr size_t M = K * K;
- constexpr size_t G = M * K;
- if (size < K) {
- out << size << "B";
- } else if (size < M) {
- out << (double(size) / K) << " KiB";
- } else if (size < G) {
- out << (double(size) / M) << " MiB";
- } else {
- out << (double(size) / G) << " GiB";
- }
- return out;
- };
+::std::function<::std::ostream&(::std::ostream&)> Joiner(
+ const Container& container, const char* sep) {
+ using std::begin;
+ using std::end;
+ const auto begin_iter = begin(container);
+ const auto end_iter = end(container);
+ return [begin_iter, end_iter, sep](::std::ostream& out) -> ::std::ostream& {
+ for (auto iter = begin_iter; iter != end_iter; ++iter) {
+ if (iter != begin_iter) {
+ out << sep;
+ }
+ out << *iter;
+ }
+ return out;
+ };
}
/**
- * Helper method to extract a UTF-16 string from a StringPool. If the string is stored as UTF-8,
+ * Helper method to extract a UTF-16 string from a StringPool. If the string is
+ * stored as UTF-8,
* the conversion to UTF-16 happens within ResStringPool.
*/
-StringPiece16 getString16(const android::ResStringPool& pool, size_t idx);
+StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx);
/**
- * Helper method to extract a UTF-8 string from a StringPool. If the string is stored as UTF-16,
- * the conversion from UTF-16 to UTF-8 does not happen in ResStringPool and is done by this method,
- * which maintains no state or cache. This means we must return an std::string copy.
+ * Helper method to extract a UTF-8 string from a StringPool. If the string is
+ * stored as UTF-16,
+ * the conversion from UTF-16 to UTF-8 does not happen in ResStringPool and is
+ * done by this method,
+ * which maintains no state or cache. This means we must return an std::string
+ * copy.
*/
-std::string getString(const android::ResStringPool& pool, size_t idx);
+std::string GetString(const android::ResStringPool& pool, size_t idx);
/**
- * Checks that the Java string format contains no non-positional arguments (arguments without
- * explicitly specifying an index) when there are more than one argument. This is an error
- * because translations may rearrange the order of the arguments in the string, which will
+ * Checks that the Java string format contains no non-positional arguments
+ * (arguments without
+ * explicitly specifying an index) when there are more than one argument. This
+ * is an error
+ * because translations may rearrange the order of the arguments in the string,
+ * which will
* break the string interpolation.
*/
-bool verifyJavaStringFormat(const StringPiece& str);
+bool VerifyJavaStringFormat(const StringPiece& str);
class StringBuilder {
-public:
- StringBuilder& append(const StringPiece& str);
- const std::string& str() const;
- const std::string& error() const;
+ public:
+ StringBuilder& Append(const StringPiece& str);
+ const std::string& ToString() const;
+ const std::string& Error() const;
- // When building StyledStrings, we need UTF-16 indices into the string,
- // which is what the Java layer expects when dealing with java String.charAt().
- size_t utf16Len() const;
+ // When building StyledStrings, we need UTF-16 indices into the string,
+ // which is what the Java layer expects when dealing with java
+ // String.charAt().
+ size_t Utf16Len() const;
- operator bool() const;
+ explicit operator bool() const;
-private:
- std::string mStr;
- size_t mUtf16Len = 0;
- bool mQuote = false;
- bool mTrailingSpace = false;
- bool mLastCharWasEscape = false;
- std::string mError;
+ private:
+ std::string str_;
+ size_t utf16_len_ = 0;
+ bool quote_ = false;
+ bool trailing_space_ = false;
+ bool last_char_was_escape_ = false;
+ std::string error_;
};
-inline const std::string& StringBuilder::str() const {
- return mStr;
-}
+inline const std::string& StringBuilder::ToString() const { return str_; }
-inline const std::string& StringBuilder::error() const {
- return mError;
-}
+inline const std::string& StringBuilder::Error() const { return error_; }
-inline size_t StringBuilder::utf16Len() const {
- return mUtf16Len;
-}
+inline size_t StringBuilder::Utf16Len() const { return utf16_len_; }
-inline StringBuilder::operator bool() const {
- return mError.empty();
-}
+inline StringBuilder::operator bool() const { return error_.empty(); }
/**
* Converts a UTF8 string to a UTF16 string.
*/
-std::u16string utf8ToUtf16(const StringPiece& utf8);
-std::string utf16ToUtf8(const StringPiece16& utf16);
+std::u16string Utf8ToUtf16(const StringPiece& utf8);
+std::string Utf16ToUtf8(const StringPiece16& utf16);
/**
* Writes the entire BigBuffer to the output stream.
*/
-bool writeAll(std::ostream& out, const BigBuffer& buffer);
+bool WriteAll(std::ostream& out, const BigBuffer& buffer);
/*
* Copies the entire BigBuffer into a single buffer.
*/
-std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer);
+std::unique_ptr<uint8_t[]> Copy(const BigBuffer& buffer);
/**
* A Tokenizer implemented as an iterable collection. It does not allocate
* any memory on the heap nor use standard containers.
*/
class Tokenizer {
-public:
- class iterator {
- public:
- iterator(const iterator&) = default;
- iterator& operator=(const iterator&) = default;
+ public:
+ class iterator {
+ public:
+ iterator(const iterator&) = default;
+ iterator& operator=(const iterator&) = default;
- iterator& operator++();
+ iterator& operator++();
- StringPiece operator*() {
- return mToken;
- }
- bool operator==(const iterator& rhs) const;
- bool operator!=(const iterator& rhs) const;
+ StringPiece operator*() { return token_; }
+ bool operator==(const iterator& rhs) const;
+ bool operator!=(const iterator& rhs) const;
- private:
- friend class Tokenizer;
+ private:
+ friend class Tokenizer;
- iterator(StringPiece s, char sep, StringPiece tok, bool end);
+ iterator(StringPiece s, char sep, StringPiece tok, bool end);
- StringPiece mStr;
- char mSeparator;
- StringPiece mToken;
- bool mEnd;
- };
+ StringPiece str_;
+ char separator_;
+ StringPiece token_;
+ bool end_;
+ };
- Tokenizer(StringPiece str, char sep);
+ Tokenizer(StringPiece str, char sep);
- iterator begin() {
- return mBegin;
- }
+ iterator begin() { return begin_; }
- iterator end() {
- return mEnd;
- }
+ iterator end() { return end_; }
-private:
- const iterator mBegin;
- const iterator mEnd;
+ private:
+ const iterator begin_;
+ const iterator end_;
};
-inline Tokenizer tokenize(const StringPiece& str, char sep) {
- return Tokenizer(str, sep);
+inline Tokenizer Tokenize(const StringPiece& str, char sep) {
+ return Tokenizer(str, sep);
}
-inline uint16_t hostToDevice16(uint16_t value) {
- return htods(value);
-}
+inline uint16_t HostToDevice16(uint16_t value) { return htods(value); }
-inline uint32_t hostToDevice32(uint32_t value) {
- return htodl(value);
-}
+inline uint32_t HostToDevice32(uint32_t value) { return htodl(value); }
-inline uint16_t deviceToHost16(uint16_t value) {
- return dtohs(value);
-}
+inline uint16_t DeviceToHost16(uint16_t value) { return dtohs(value); }
-inline uint32_t deviceToHost32(uint32_t value) {
- return dtohl(value);
-}
+inline uint32_t DeviceToHost32(uint32_t value) { return dtohl(value); }
/**
* Given a path like: res/xml-sw600dp/foo.xml
@@ -285,20 +267,22 @@
*
* Returns true if successful.
*/
-bool extractResFilePathParts(const StringPiece& path, StringPiece* outPrefix,
- StringPiece* outEntry, StringPiece* outSuffix);
+bool ExtractResFilePathParts(const StringPiece& path, StringPiece* out_prefix,
+ StringPiece* out_entry, StringPiece* out_suffix);
-} // namespace util
+} // namespace util
/**
- * Stream operator for functions. Calls the function with the stream as an argument.
+ * Stream operator for functions. Calls the function with the stream as an
+ * argument.
* In the aapt namespace for lookup.
*/
-inline ::std::ostream& operator<<(::std::ostream& out,
- const ::std::function<::std::ostream&(::std::ostream&)>& f) {
- return f(out);
+inline ::std::ostream& operator<<(
+ ::std::ostream& out,
+ const ::std::function<::std::ostream&(::std::ostream&)>& f) {
+ return f(out);
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_UTIL_H
+#endif // AAPT_UTIL_H
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index 0e27213..cac3de4 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -14,191 +14,196 @@
* limitations under the License.
*/
-#include "test/Test.h"
-#include "util/StringPiece.h"
#include "util/Util.h"
#include <string>
+#include "test/Test.h"
+
namespace aapt {
TEST(UtilTest, TrimOnlyWhitespace) {
- const std::string full = "\n ";
+ const std::string full = "\n ";
- StringPiece trimmed = util::trimWhitespace(full);
- EXPECT_TRUE(trimmed.empty());
- EXPECT_EQ(0u, trimmed.size());
+ StringPiece trimmed = util::TrimWhitespace(full);
+ EXPECT_TRUE(trimmed.empty());
+ EXPECT_EQ(0u, trimmed.size());
}
TEST(UtilTest, StringEndsWith) {
- EXPECT_TRUE(util::stringEndsWith("hello.xml", ".xml"));
+ EXPECT_TRUE(util::EndsWith("hello.xml", ".xml"));
}
TEST(UtilTest, StringStartsWith) {
- EXPECT_TRUE(util::stringStartsWith("hello.xml", "he"));
+ EXPECT_TRUE(util::StartsWith("hello.xml", "he"));
}
TEST(UtilTest, StringBuilderSplitEscapeSequence) {
- EXPECT_EQ(StringPiece("this is a new\nline."),
- util::StringBuilder().append("this is a new\\")
- .append("nline.")
- .str());
+ EXPECT_EQ(StringPiece("this is a new\nline."), util::StringBuilder()
+ .Append("this is a new\\")
+ .Append("nline.")
+ .ToString());
}
TEST(UtilTest, StringBuilderWhitespaceRemoval) {
- EXPECT_EQ(StringPiece("hey guys this is so cool"),
- util::StringBuilder().append(" hey guys ")
- .append(" this is so cool ")
- .str());
+ EXPECT_EQ(StringPiece("hey guys this is so cool"),
+ util::StringBuilder()
+ .Append(" hey guys ")
+ .Append(" this is so cool ")
+ .ToString());
- EXPECT_EQ(StringPiece(" wow, so many \t spaces. what?"),
- util::StringBuilder().append(" \" wow, so many \t ")
- .append("spaces. \"what? ")
- .str());
+ EXPECT_EQ(StringPiece(" wow, so many \t spaces. what?"),
+ util::StringBuilder()
+ .Append(" \" wow, so many \t ")
+ .Append("spaces. \"what? ")
+ .ToString());
- EXPECT_EQ(StringPiece("where is the pie?"),
- util::StringBuilder().append(" where \t ")
- .append(" \nis the "" pie?")
- .str());
+ EXPECT_EQ(StringPiece("where is the pie?"), util::StringBuilder()
+ .Append(" where \t ")
+ .Append(" \nis the "
+ " pie?")
+ .ToString());
}
TEST(UtilTest, StringBuilderEscaping) {
- EXPECT_EQ(StringPiece("hey guys\n this \t is so\\ cool"),
- util::StringBuilder().append(" hey guys\\n ")
- .append(" this \\t is so\\\\ cool ")
- .str());
+ EXPECT_EQ(StringPiece("hey guys\n this \t is so\\ cool"),
+ util::StringBuilder()
+ .Append(" hey guys\\n ")
+ .Append(" this \\t is so\\\\ cool ")
+ .ToString());
- EXPECT_EQ(StringPiece("@?#\\\'"),
- util::StringBuilder().append("\\@\\?\\#\\\\\\'")
- .str());
+ EXPECT_EQ(StringPiece("@?#\\\'"),
+ util::StringBuilder().Append("\\@\\?\\#\\\\\\'").ToString());
}
TEST(UtilTest, StringBuilderMisplacedQuote) {
- util::StringBuilder builder{};
- EXPECT_FALSE(builder.append("they're coming!"));
+ util::StringBuilder builder{};
+ EXPECT_FALSE(builder.Append("they're coming!"));
}
TEST(UtilTest, StringBuilderUnicodeCodes) {
- EXPECT_EQ(std::string("\u00AF\u0AF0 woah"),
- util::StringBuilder().append("\\u00AF\\u0AF0 woah")
- .str());
+ EXPECT_EQ(std::string("\u00AF\u0AF0 woah"),
+ util::StringBuilder().Append("\\u00AF\\u0AF0 woah").ToString());
- EXPECT_FALSE(util::StringBuilder().append("\\u00 yo"));
+ EXPECT_FALSE(util::StringBuilder().Append("\\u00 yo"));
}
TEST(UtilTest, TokenizeInput) {
- auto tokenizer = util::tokenize(StringPiece("this| is|the|end"), '|');
- auto iter = tokenizer.begin();
- ASSERT_EQ(*iter, StringPiece("this"));
- ++iter;
- ASSERT_EQ(*iter, StringPiece(" is"));
- ++iter;
- ASSERT_EQ(*iter, StringPiece("the"));
- ++iter;
- ASSERT_EQ(*iter, StringPiece("end"));
- ++iter;
- ASSERT_EQ(tokenizer.end(), iter);
+ auto tokenizer = util::Tokenize(StringPiece("this| is|the|end"), '|');
+ auto iter = tokenizer.begin();
+ ASSERT_EQ(*iter, StringPiece("this"));
+ ++iter;
+ ASSERT_EQ(*iter, StringPiece(" is"));
+ ++iter;
+ ASSERT_EQ(*iter, StringPiece("the"));
+ ++iter;
+ ASSERT_EQ(*iter, StringPiece("end"));
+ ++iter;
+ ASSERT_EQ(tokenizer.end(), iter);
}
TEST(UtilTest, TokenizeEmptyString) {
- auto tokenizer = util::tokenize(StringPiece(""), '|');
- auto iter = tokenizer.begin();
- ASSERT_NE(tokenizer.end(), iter);
- ASSERT_EQ(StringPiece(), *iter);
- ++iter;
- ASSERT_EQ(tokenizer.end(), iter);
+ auto tokenizer = util::Tokenize(StringPiece(""), '|');
+ auto iter = tokenizer.begin();
+ ASSERT_NE(tokenizer.end(), iter);
+ ASSERT_EQ(StringPiece(), *iter);
+ ++iter;
+ ASSERT_EQ(tokenizer.end(), iter);
}
TEST(UtilTest, TokenizeAtEnd) {
- auto tokenizer = util::tokenize(StringPiece("one."), '.');
- auto iter = tokenizer.begin();
- ASSERT_EQ(*iter, StringPiece("one"));
- ++iter;
- ASSERT_NE(iter, tokenizer.end());
- ASSERT_EQ(*iter, StringPiece());
+ auto tokenizer = util::Tokenize(StringPiece("one."), '.');
+ auto iter = tokenizer.begin();
+ ASSERT_EQ(*iter, StringPiece("one"));
+ ++iter;
+ ASSERT_NE(iter, tokenizer.end());
+ ASSERT_EQ(*iter, StringPiece());
}
TEST(UtilTest, IsJavaClassName) {
- EXPECT_TRUE(util::isJavaClassName("android.test.Class"));
- EXPECT_TRUE(util::isJavaClassName("android.test.Class$Inner"));
- EXPECT_TRUE(util::isJavaClassName("android_test.test.Class"));
- EXPECT_TRUE(util::isJavaClassName("_android_.test._Class_"));
- EXPECT_FALSE(util::isJavaClassName("android.test.$Inner"));
- EXPECT_FALSE(util::isJavaClassName("android.test.Inner$"));
- EXPECT_FALSE(util::isJavaClassName(".test.Class"));
- EXPECT_FALSE(util::isJavaClassName("android"));
+ EXPECT_TRUE(util::IsJavaClassName("android.test.Class"));
+ EXPECT_TRUE(util::IsJavaClassName("android.test.Class$Inner"));
+ EXPECT_TRUE(util::IsJavaClassName("android_test.test.Class"));
+ EXPECT_TRUE(util::IsJavaClassName("_android_.test._Class_"));
+ EXPECT_FALSE(util::IsJavaClassName("android.test.$Inner"));
+ EXPECT_FALSE(util::IsJavaClassName("android.test.Inner$"));
+ EXPECT_FALSE(util::IsJavaClassName(".test.Class"));
+ EXPECT_FALSE(util::IsJavaClassName("android"));
}
TEST(UtilTest, IsJavaPackageName) {
- EXPECT_TRUE(util::isJavaPackageName("android"));
- EXPECT_TRUE(util::isJavaPackageName("android.test"));
- EXPECT_TRUE(util::isJavaPackageName("android.test_thing"));
- EXPECT_FALSE(util::isJavaPackageName("_android"));
- EXPECT_FALSE(util::isJavaPackageName("android_"));
- EXPECT_FALSE(util::isJavaPackageName("android."));
- EXPECT_FALSE(util::isJavaPackageName(".android"));
- EXPECT_FALSE(util::isJavaPackageName("android._test"));
- EXPECT_FALSE(util::isJavaPackageName(".."));
+ EXPECT_TRUE(util::IsJavaPackageName("android"));
+ EXPECT_TRUE(util::IsJavaPackageName("android.test"));
+ EXPECT_TRUE(util::IsJavaPackageName("android.test_thing"));
+ EXPECT_FALSE(util::IsJavaPackageName("_android"));
+ EXPECT_FALSE(util::IsJavaPackageName("android_"));
+ EXPECT_FALSE(util::IsJavaPackageName("android."));
+ EXPECT_FALSE(util::IsJavaPackageName(".android"));
+ EXPECT_FALSE(util::IsJavaPackageName("android._test"));
+ EXPECT_FALSE(util::IsJavaPackageName(".."));
}
TEST(UtilTest, FullyQualifiedClassName) {
- Maybe<std::string> res = util::getFullyQualifiedClassName("android", ".asdf");
- AAPT_ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), "android.asdf");
+ Maybe<std::string> res = util::GetFullyQualifiedClassName("android", ".asdf");
+ AAPT_ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), "android.asdf");
- res = util::getFullyQualifiedClassName("android", ".a.b");
- AAPT_ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), "android.a.b");
+ res = util::GetFullyQualifiedClassName("android", ".a.b");
+ AAPT_ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), "android.a.b");
- res = util::getFullyQualifiedClassName("android", "a.b");
- AAPT_ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), "a.b");
+ res = util::GetFullyQualifiedClassName("android", "a.b");
+ AAPT_ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), "a.b");
- res = util::getFullyQualifiedClassName("", "a.b");
- AAPT_ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), "a.b");
+ res = util::GetFullyQualifiedClassName("", "a.b");
+ AAPT_ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), "a.b");
- res = util::getFullyQualifiedClassName("android", "Class");
- AAPT_ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), "android.Class");
+ res = util::GetFullyQualifiedClassName("android", "Class");
+ AAPT_ASSERT_TRUE(res);
+ EXPECT_EQ(res.value(), "android.Class");
- res = util::getFullyQualifiedClassName("", "");
- AAPT_ASSERT_FALSE(res);
+ res = util::GetFullyQualifiedClassName("", "");
+ AAPT_ASSERT_FALSE(res);
- res = util::getFullyQualifiedClassName("android", "./Apple");
- AAPT_ASSERT_FALSE(res);
+ res = util::GetFullyQualifiedClassName("android", "./Apple");
+ AAPT_ASSERT_FALSE(res);
}
TEST(UtilTest, ExtractResourcePathComponents) {
- StringPiece prefix, entry, suffix;
- ASSERT_TRUE(util::extractResFilePathParts("res/xml-sw600dp/entry.xml", &prefix, &entry,
- &suffix));
- EXPECT_EQ(prefix, "res/xml-sw600dp/");
- EXPECT_EQ(entry, "entry");
- EXPECT_EQ(suffix, ".xml");
+ StringPiece prefix, entry, suffix;
+ ASSERT_TRUE(util::ExtractResFilePathParts("res/xml-sw600dp/entry.xml",
+ &prefix, &entry, &suffix));
+ EXPECT_EQ(prefix, "res/xml-sw600dp/");
+ EXPECT_EQ(entry, "entry");
+ EXPECT_EQ(suffix, ".xml");
- ASSERT_TRUE(util::extractResFilePathParts("res/xml-sw600dp/entry.9.png", &prefix, &entry,
- &suffix));
+ ASSERT_TRUE(util::ExtractResFilePathParts("res/xml-sw600dp/entry.9.png",
+ &prefix, &entry, &suffix));
- EXPECT_EQ(prefix, "res/xml-sw600dp/");
- EXPECT_EQ(entry, "entry");
- EXPECT_EQ(suffix, ".9.png");
+ EXPECT_EQ(prefix, "res/xml-sw600dp/");
+ EXPECT_EQ(entry, "entry");
+ EXPECT_EQ(suffix, ".9.png");
- EXPECT_FALSE(util::extractResFilePathParts("AndroidManifest.xml", &prefix, &entry, &suffix));
- EXPECT_FALSE(util::extractResFilePathParts("res/.xml", &prefix, &entry, &suffix));
+ EXPECT_FALSE(util::ExtractResFilePathParts("AndroidManifest.xml", &prefix,
+ &entry, &suffix));
+ EXPECT_FALSE(
+ util::ExtractResFilePathParts("res/.xml", &prefix, &entry, &suffix));
- ASSERT_TRUE(util::extractResFilePathParts("res//.", &prefix, &entry, &suffix));
- EXPECT_EQ(prefix, "res//");
- EXPECT_EQ(entry, "");
- EXPECT_EQ(suffix, ".");
+ ASSERT_TRUE(
+ util::ExtractResFilePathParts("res//.", &prefix, &entry, &suffix));
+ EXPECT_EQ(prefix, "res//");
+ EXPECT_EQ(entry, "");
+ EXPECT_EQ(suffix, ".");
}
TEST(UtilTest, VerifyJavaStringFormat) {
- ASSERT_TRUE(util::verifyJavaStringFormat("%09.34f"));
- ASSERT_TRUE(util::verifyJavaStringFormat("%9$.34f %8$"));
- ASSERT_TRUE(util::verifyJavaStringFormat("%% %%"));
- ASSERT_FALSE(util::verifyJavaStringFormat("%09$f %f"));
- ASSERT_FALSE(util::verifyJavaStringFormat("%09f %08s"));
+ ASSERT_TRUE(util::VerifyJavaStringFormat("%09.34f"));
+ ASSERT_TRUE(util::VerifyJavaStringFormat("%9$.34f %8$"));
+ ASSERT_TRUE(util::VerifyJavaStringFormat("%% %%"));
+ ASSERT_FALSE(util::VerifyJavaStringFormat("%09$f %f"));
+ ASSERT_FALSE(util::VerifyJavaStringFormat("%09f %08s"));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlActionExecutor.cpp b/tools/aapt2/xml/XmlActionExecutor.cpp
index 745079c..7580b46 100644
--- a/tools/aapt2/xml/XmlActionExecutor.cpp
+++ b/tools/aapt2/xml/XmlActionExecutor.cpp
@@ -19,93 +19,94 @@
namespace aapt {
namespace xml {
-static bool wrapperOne(XmlNodeAction::ActionFunc& f, Element* el, SourcePathDiagnostics*) {
- return f(el);
+static bool wrapper_one(XmlNodeAction::ActionFunc& f, Element* el,
+ SourcePathDiagnostics*) {
+ return f(el);
}
-static bool wrapperTwo(XmlNodeAction::ActionFuncWithDiag& f, Element* el,
- SourcePathDiagnostics* diag) {
- return f(el, diag);
+static bool wrapper_two(XmlNodeAction::ActionFuncWithDiag& f, Element* el,
+ SourcePathDiagnostics* diag) {
+ return f(el, diag);
}
-void XmlNodeAction::action(XmlNodeAction::ActionFunc f) {
- mActions.emplace_back(std::bind(wrapperOne, std::move(f),
- std::placeholders::_1,
- std::placeholders::_2));
+void XmlNodeAction::Action(XmlNodeAction::ActionFunc f) {
+ actions_.emplace_back(std::bind(
+ wrapper_one, std::move(f), std::placeholders::_1, std::placeholders::_2));
}
-void XmlNodeAction::action(XmlNodeAction::ActionFuncWithDiag f) {
- mActions.emplace_back(std::bind(wrapperTwo, std::move(f),
- std::placeholders::_1,
- std::placeholders::_2));
+void XmlNodeAction::Action(XmlNodeAction::ActionFuncWithDiag f) {
+ actions_.emplace_back(std::bind(
+ wrapper_two, std::move(f), std::placeholders::_1, std::placeholders::_2));
}
-static void printElementToDiagMessage(const Element* el, DiagMessage* msg) {
- *msg << "<";
- if (!el->namespaceUri.empty()) {
- *msg << el->namespaceUri << ":";
- }
- *msg << el->name << ">";
+static void PrintElementToDiagMessage(const Element* el, DiagMessage* msg) {
+ *msg << "<";
+ if (!el->namespace_uri.empty()) {
+ *msg << el->namespace_uri << ":";
+ }
+ *msg << el->name << ">";
}
-bool XmlNodeAction::execute(XmlActionExecutorPolicy policy, SourcePathDiagnostics* diag,
- Element* el) const {
- bool error = false;
- for (const ActionFuncWithDiag& action : mActions) {
- error |= !action(el, diag);
+bool XmlNodeAction::Execute(XmlActionExecutorPolicy policy,
+ SourcePathDiagnostics* diag, Element* el) const {
+ bool error = false;
+ for (const ActionFuncWithDiag& action : actions_) {
+ error |= !action(el, diag);
+ }
+
+ for (Element* child_el : el->GetChildElements()) {
+ if (child_el->namespace_uri.empty()) {
+ std::map<std::string, XmlNodeAction>::const_iterator iter =
+ map_.find(child_el->name);
+ if (iter != map_.end()) {
+ error |= !iter->second.Execute(policy, diag, child_el);
+ continue;
+ }
}
- for (Element* childEl : el->getChildElements()) {
- if (childEl->namespaceUri.empty()) {
- std::map<std::string, XmlNodeAction>::const_iterator iter = mMap.find(childEl->name);
- if (iter != mMap.end()) {
- error |= !iter->second.execute(policy, diag, childEl);
- continue;
- }
- }
-
- if (policy == XmlActionExecutorPolicy::Whitelist) {
- DiagMessage errorMsg(childEl->lineNumber);
- errorMsg << "unknown element ";
- printElementToDiagMessage(childEl, &errorMsg);
- errorMsg << " found";
- diag->error(errorMsg);
- error = true;
- }
+ if (policy == XmlActionExecutorPolicy::kWhitelist) {
+ DiagMessage error_msg(child_el->line_number);
+ error_msg << "unknown element ";
+ PrintElementToDiagMessage(child_el, &error_msg);
+ error_msg << " found";
+ diag->Error(error_msg);
+ error = true;
}
- return !error;
+ }
+ return !error;
}
-bool XmlActionExecutor::execute(XmlActionExecutorPolicy policy, IDiagnostics* diag,
- XmlResource* doc) const {
- SourcePathDiagnostics sourceDiag(doc->file.source, diag);
+bool XmlActionExecutor::Execute(XmlActionExecutorPolicy policy,
+ IDiagnostics* diag, XmlResource* doc) const {
+ SourcePathDiagnostics source_diag(doc->file.source, diag);
- Element* el = findRootElement(doc);
- if (!el) {
- if (policy == XmlActionExecutorPolicy::Whitelist) {
- sourceDiag.error(DiagMessage() << "no root XML tag found");
- return false;
- }
- return true;
- }
-
- if (el->namespaceUri.empty()) {
- std::map<std::string, XmlNodeAction>::const_iterator iter = mMap.find(el->name);
- if (iter != mMap.end()) {
- return iter->second.execute(policy, &sourceDiag, el);
- }
- }
-
- if (policy == XmlActionExecutorPolicy::Whitelist) {
- DiagMessage errorMsg(el->lineNumber);
- errorMsg << "unknown element ";
- printElementToDiagMessage(el, &errorMsg);
- errorMsg << " found";
- sourceDiag.error(errorMsg);
- return false;
+ Element* el = FindRootElement(doc);
+ if (!el) {
+ if (policy == XmlActionExecutorPolicy::kWhitelist) {
+ source_diag.Error(DiagMessage() << "no root XML tag found");
+ return false;
}
return true;
+ }
+
+ if (el->namespace_uri.empty()) {
+ std::map<std::string, XmlNodeAction>::const_iterator iter =
+ map_.find(el->name);
+ if (iter != map_.end()) {
+ return iter->second.Execute(policy, &source_diag, el);
+ }
+ }
+
+ if (policy == XmlActionExecutorPolicy::kWhitelist) {
+ DiagMessage error_msg(el->line_number);
+ error_msg << "unknown element ";
+ PrintElementToDiagMessage(el, &error_msg);
+ error_msg << " found";
+ source_diag.Error(error_msg);
+ return false;
+ }
+ return true;
}
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlActionExecutor.h b/tools/aapt2/xml/XmlActionExecutor.h
index cad508c..68e3563 100644
--- a/tools/aapt2/xml/XmlActionExecutor.h
+++ b/tools/aapt2/xml/XmlActionExecutor.h
@@ -17,92 +17,97 @@
#ifndef AAPT_XML_XMLPATTERN_H
#define AAPT_XML_XMLPATTERN_H
-#include "Diagnostics.h"
-#include "xml/XmlDom.h"
-
-#include <android-base/macros.h>
#include <functional>
#include <map>
#include <string>
#include <vector>
+#include "android-base/macros.h"
+
+#include "Diagnostics.h"
+#include "xml/XmlDom.h"
+
namespace aapt {
namespace xml {
enum class XmlActionExecutorPolicy {
- /**
- * Actions on run if elements are matched, errors occur only when actions return false.
- */
- None,
+ /**
+ * Actions on run if elements are matched, errors occur only when actions
+ * return false.
+ */
+ kNone,
- /**
- * The actions defined must match and run. If an element is found that does not match
- * an action, an error occurs.
- */
- Whitelist,
+ /**
+ * The actions defined must match and run. If an element is found that does
+ * not match
+ * an action, an error occurs.
+ */
+ kWhitelist,
};
/**
- * Contains the actions to perform at this XML node. This is a recursive data structure that
+ * Contains the actions to perform at this XML node. This is a recursive data
+ * structure that
* holds XmlNodeActions for child XML nodes.
*/
class XmlNodeAction {
-public:
- using ActionFuncWithDiag = std::function<bool(Element*, SourcePathDiagnostics*)>;
- using ActionFunc = std::function<bool(Element*)>;
+ public:
+ using ActionFuncWithDiag =
+ std::function<bool(Element*, SourcePathDiagnostics*)>;
+ using ActionFunc = std::function<bool(Element*)>;
- /**
- * Find or create a child XmlNodeAction that will be performed for the child element
- * with the name `name`.
- */
- XmlNodeAction& operator[](const std::string& name) {
- return mMap[name];
- }
+ /**
+ * Find or create a child XmlNodeAction that will be performed for the child
+ * element
+ * with the name `name`.
+ */
+ XmlNodeAction& operator[](const std::string& name) { return map_[name]; }
- /**
- * Add an action to be performed at this XmlNodeAction.
- */
- void action(ActionFunc f);
- void action(ActionFuncWithDiag);
+ /**
+ * Add an action to be performed at this XmlNodeAction.
+ */
+ void Action(ActionFunc f);
+ void Action(ActionFuncWithDiag);
-private:
- friend class XmlActionExecutor;
+ private:
+ friend class XmlActionExecutor;
- bool execute(XmlActionExecutorPolicy policy, SourcePathDiagnostics* diag, Element* el) const;
+ bool Execute(XmlActionExecutorPolicy policy, SourcePathDiagnostics* diag,
+ Element* el) const;
- std::map<std::string, XmlNodeAction> mMap;
- std::vector<ActionFuncWithDiag> mActions;
+ std::map<std::string, XmlNodeAction> map_;
+ std::vector<ActionFuncWithDiag> actions_;
};
/**
- * Allows the definition of actions to execute at specific XML elements defined by their
+ * Allows the definition of actions to execute at specific XML elements defined
+ * by their
* hierarchy.
*/
class XmlActionExecutor {
-public:
- XmlActionExecutor() = default;
+ public:
+ XmlActionExecutor() = default;
- /**
- * Find or create a root XmlNodeAction that will be performed for the root XML element
- * with the name `name`.
- */
- XmlNodeAction& operator[](const std::string& name) {
- return mMap[name];
- }
+ /**
+ * Find or create a root XmlNodeAction that will be performed for the root XML
+ * element with the name `name`.
+ */
+ XmlNodeAction& operator[](const std::string& name) { return map_[name]; }
- /**
- * Execute the defined actions for this XmlResource.
- * Returns true if all actions return true, otherwise returns false.
- */
- bool execute(XmlActionExecutorPolicy policy, IDiagnostics* diag, XmlResource* doc) const;
+ /**
+ * Execute the defined actions for this XmlResource.
+ * Returns true if all actions return true, otherwise returns false.
+ */
+ bool Execute(XmlActionExecutorPolicy policy, IDiagnostics* diag,
+ XmlResource* doc) const;
-private:
- std::map<std::string, XmlNodeAction> mMap;
+ private:
+ std::map<std::string, XmlNodeAction> map_;
- DISALLOW_COPY_AND_ASSIGN(XmlActionExecutor);
+ DISALLOW_COPY_AND_ASSIGN(XmlActionExecutor);
};
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
#endif /* AAPT_XML_XMLPATTERN_H */
diff --git a/tools/aapt2/xml/XmlActionExecutor_test.cpp b/tools/aapt2/xml/XmlActionExecutor_test.cpp
index 106e856..7110c90 100644
--- a/tools/aapt2/xml/XmlActionExecutor_test.cpp
+++ b/tools/aapt2/xml/XmlActionExecutor_test.cpp
@@ -14,49 +14,53 @@
* limitations under the License.
*/
-#include "test/Test.h"
#include "xml/XmlActionExecutor.h"
+#include "test/Test.h"
+
namespace aapt {
namespace xml {
TEST(XmlActionExecutorTest, BuildsAccessibleNestedPattern) {
- XmlActionExecutor executor;
- XmlNodeAction& manifestAction = executor["manifest"];
- XmlNodeAction& applicationAction = manifestAction["application"];
+ XmlActionExecutor executor;
+ XmlNodeAction& manifest_action = executor["manifest"];
+ XmlNodeAction& application_action = manifest_action["application"];
- Element* manifestEl = nullptr;
- manifestAction.action([&](Element* manifest) -> bool {
- manifestEl = manifest;
- return true;
- });
+ Element* manifest_el = nullptr;
+ manifest_action.Action([&](Element* manifest) -> bool {
+ manifest_el = manifest;
+ return true;
+ });
- Element* applicationEl = nullptr;
- applicationAction.action([&](Element* application) -> bool {
- applicationEl = application;
- return true;
- });
+ Element* application_el = nullptr;
+ application_action.Action([&](Element* application) -> bool {
+ application_el = application;
+ return true;
+ });
- std::unique_ptr<XmlResource> doc = test::buildXmlDom("<manifest><application /></manifest>");
+ std::unique_ptr<XmlResource> doc =
+ test::BuildXmlDom("<manifest><application /></manifest>");
- StdErrDiagnostics diag;
- ASSERT_TRUE(executor.execute(XmlActionExecutorPolicy::None, &diag, doc.get()));
- ASSERT_NE(nullptr, manifestEl);
- EXPECT_EQ(std::string("manifest"), manifestEl->name);
+ StdErrDiagnostics diag;
+ ASSERT_TRUE(
+ executor.Execute(XmlActionExecutorPolicy::kNone, &diag, doc.get()));
+ ASSERT_NE(nullptr, manifest_el);
+ EXPECT_EQ(std::string("manifest"), manifest_el->name);
- ASSERT_NE(nullptr, applicationEl);
- EXPECT_EQ(std::string("application"), applicationEl->name);
+ ASSERT_NE(nullptr, application_el);
+ EXPECT_EQ(std::string("application"), application_el->name);
}
TEST(XmlActionExecutorTest, FailsWhenUndefinedHierarchyExists) {
- XmlActionExecutor executor;
- executor["manifest"]["application"];
+ XmlActionExecutor executor;
+ executor["manifest"]["application"];
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(
- "<manifest><application /><activity /></manifest>");
- StdErrDiagnostics diag;
- ASSERT_FALSE(executor.execute(XmlActionExecutorPolicy::Whitelist, &diag, doc.get()));
+ std::unique_ptr<XmlResource> doc =
+ test::BuildXmlDom("<manifest><application /><activity /></manifest>");
+ StdErrDiagnostics diag;
+ ASSERT_FALSE(
+ executor.Execute(XmlActionExecutorPolicy::kWhitelist, &diag, doc.get()));
}
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 28de78a..567418e 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -15,475 +15,496 @@
*/
#include "XmlDom.h"
-#include "XmlPullParser.h"
-#include "util/Util.h"
+
+#include <expat.h>
#include <cassert>
-#include <expat.h>
#include <memory>
#include <stack>
#include <string>
#include <tuple>
+#include "android-base/logging.h"
+
+#include "XmlPullParser.h"
+#include "util/Util.h"
+
namespace aapt {
namespace xml {
constexpr char kXmlNamespaceSep = 1;
struct Stack {
- std::unique_ptr<xml::Node> root;
- std::stack<xml::Node*> nodeStack;
- std::string pendingComment;
+ std::unique_ptr<xml::Node> root;
+ std::stack<xml::Node*> node_stack;
+ std::string pending_comment;
};
/**
* Extracts the namespace and name of an expanded element or attribute name.
*/
-static void splitName(const char* name, std::string* outNs, std::string* outName) {
- const char* p = name;
- while (*p != 0 && *p != kXmlNamespaceSep) {
- p++;
- }
+static void SplitName(const char* name, std::string* out_ns,
+ std::string* out_name) {
+ const char* p = name;
+ while (*p != 0 && *p != kXmlNamespaceSep) {
+ p++;
+ }
- if (*p == 0) {
- outNs->clear();
- *outName = StringPiece(name).toString();
- } else {
- *outNs = StringPiece(name, (p - name)).toString();
- *outName = StringPiece(p + 1).toString();
- }
+ if (*p == 0) {
+ out_ns->clear();
+ *out_name = StringPiece(name).ToString();
+ } else {
+ *out_ns = StringPiece(name, (p - name)).ToString();
+ *out_name = StringPiece(p + 1).ToString();
+ }
}
-static void addToStack(Stack* stack, XML_Parser parser, std::unique_ptr<Node> node) {
- node->lineNumber = XML_GetCurrentLineNumber(parser);
- node->columnNumber = XML_GetCurrentColumnNumber(parser);
+static void AddToStack(Stack* stack, XML_Parser parser,
+ std::unique_ptr<Node> node) {
+ node->line_number = XML_GetCurrentLineNumber(parser);
+ node->column_number = XML_GetCurrentColumnNumber(parser);
- Node* thisNode = node.get();
- if (!stack->nodeStack.empty()) {
- stack->nodeStack.top()->addChild(std::move(node));
- } else {
- stack->root = std::move(node);
- }
+ Node* this_node = node.get();
+ if (!stack->node_stack.empty()) {
+ stack->node_stack.top()->AddChild(std::move(node));
+ } else {
+ stack->root = std::move(node);
+ }
- if (!nodeCast<Text>(thisNode)) {
- stack->nodeStack.push(thisNode);
- }
+ if (!NodeCast<Text>(this_node)) {
+ stack->node_stack.push(this_node);
+ }
}
-static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri) {
- XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
- Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix,
+ const char* uri) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
- std::unique_ptr<Namespace> ns = util::make_unique<Namespace>();
- if (prefix) {
- ns->namespacePrefix = StringPiece(prefix).toString();
- }
+ std::unique_ptr<Namespace> ns = util::make_unique<Namespace>();
+ if (prefix) {
+ ns->namespace_prefix = StringPiece(prefix).ToString();
+ }
- if (uri) {
- ns->namespaceUri = StringPiece(uri).toString();
- }
+ if (uri) {
+ ns->namespace_uri = StringPiece(uri).ToString();
+ }
- addToStack(stack, parser, std::move(ns));
+ AddToStack(stack, parser, std::move(ns));
}
-static void XMLCALL endNamespaceHandler(void* userData, const char* prefix) {
- XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
- Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
- assert(!stack->nodeStack.empty());
- stack->nodeStack.pop();
+ CHECK(!stack->node_stack.empty());
+ stack->node_stack.pop();
}
-static bool lessAttribute(const Attribute& lhs, const Attribute& rhs) {
- return std::tie(lhs.namespaceUri, lhs.name, lhs.value) <
- std::tie(rhs.namespaceUri, rhs.name, rhs.value);
+static bool less_attribute(const Attribute& lhs, const Attribute& rhs) {
+ return std::tie(lhs.namespace_uri, lhs.name, lhs.value) <
+ std::tie(rhs.namespace_uri, rhs.name, rhs.value);
}
-static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs) {
- XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
- Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+static void XMLCALL StartElementHandler(void* user_data, const char* name,
+ const char** attrs) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
- std::unique_ptr<Element> el = util::make_unique<Element>();
- splitName(name, &el->namespaceUri, &el->name);
+ std::unique_ptr<Element> el = util::make_unique<Element>();
+ SplitName(name, &el->namespace_uri, &el->name);
- while (*attrs) {
- Attribute attribute;
- splitName(*attrs++, &attribute.namespaceUri, &attribute.name);
- attribute.value = StringPiece(*attrs++).toString();
+ while (*attrs) {
+ Attribute attribute;
+ SplitName(*attrs++, &attribute.namespace_uri, &attribute.name);
+ attribute.value = StringPiece(*attrs++).ToString();
- // Insert in sorted order.
- auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(), attribute,
- lessAttribute);
- el->attributes.insert(iter, std::move(attribute));
- }
+ // Insert in sorted order.
+ auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(),
+ attribute, less_attribute);
+ el->attributes.insert(iter, std::move(attribute));
+ }
- el->comment = std::move(stack->pendingComment);
- addToStack(stack, parser, std::move(el));
+ el->comment = std::move(stack->pending_comment);
+ AddToStack(stack, parser, std::move(el));
}
-static void XMLCALL endElementHandler(void* userData, const char* name) {
- XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
- Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+static void XMLCALL EndElementHandler(void* user_data, const char* name) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
- assert(!stack->nodeStack.empty());
- //stack->nodeStack.top()->comment = std::move(stack->pendingComment);
- stack->nodeStack.pop();
+ CHECK(!stack->node_stack.empty());
+ // stack->nodeStack.top()->comment = std::move(stack->pendingComment);
+ stack->node_stack.pop();
}
-static void XMLCALL characterDataHandler(void* userData, const char* s, int len) {
- XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
- Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+static void XMLCALL CharacterDataHandler(void* user_data, const char* s,
+ int len) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
- if (!s || len <= 0) {
+ if (!s || len <= 0) {
+ return;
+ }
+
+ // See if we can just append the text to a previous text node.
+ if (!stack->node_stack.empty()) {
+ Node* currentParent = stack->node_stack.top();
+ if (!currentParent->children.empty()) {
+ Node* last_child = currentParent->children.back().get();
+ if (Text* text = NodeCast<Text>(last_child)) {
+ text->text += StringPiece(s, len).ToString();
return;
+ }
}
+ }
- // See if we can just append the text to a previous text node.
- if (!stack->nodeStack.empty()) {
- Node* currentParent = stack->nodeStack.top();
- if (!currentParent->children.empty()) {
- Node* lastChild = currentParent->children.back().get();
- if (Text* text = nodeCast<Text>(lastChild)) {
- text->text += StringPiece(s, len).toString();
- return;
- }
- }
- }
-
- std::unique_ptr<Text> text = util::make_unique<Text>();
- text->text = StringPiece(s, len).toString();
- addToStack(stack, parser, std::move(text));
+ std::unique_ptr<Text> text = util::make_unique<Text>();
+ text->text = StringPiece(s, len).ToString();
+ AddToStack(stack, parser, std::move(text));
}
-static void XMLCALL commentDataHandler(void* userData, const char* comment) {
- XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
- Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+static void XMLCALL CommentDataHandler(void* user_data, const char* comment) {
+ XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
+ Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
- if (!stack->pendingComment.empty()) {
- stack->pendingComment += '\n';
- }
- stack->pendingComment += comment;
+ if (!stack->pending_comment.empty()) {
+ stack->pending_comment += '\n';
+ }
+ stack->pending_comment += comment;
}
-std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag, const Source& source) {
- Stack stack;
-
- XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
- XML_SetUserData(parser, &stack);
- XML_UseParserAsHandlerArg(parser);
- XML_SetElementHandler(parser, startElementHandler, endElementHandler);
- XML_SetNamespaceDeclHandler(parser, startNamespaceHandler, endNamespaceHandler);
- XML_SetCharacterDataHandler(parser, characterDataHandler);
- XML_SetCommentHandler(parser, commentDataHandler);
-
- char buffer[1024];
- while (!in->eof()) {
- in->read(buffer, sizeof(buffer) / sizeof(buffer[0]));
- if (in->bad() && !in->eof()) {
- stack.root = {};
- diag->error(DiagMessage(source) << strerror(errno));
- break;
- }
-
- if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) {
- stack.root = {};
- diag->error(DiagMessage(source.withLine(XML_GetCurrentLineNumber(parser)))
- << XML_ErrorString(XML_GetErrorCode(parser)));
- break;
- }
- }
-
- XML_ParserFree(parser);
- if (stack.root) {
- return util::make_unique<XmlResource>(ResourceFile{ {}, {}, source }, std::move(stack.root));
- }
- return {};
-}
-
-static void copyAttributes(Element* el, android::ResXMLParser* parser) {
- const size_t attrCount = parser->getAttributeCount();
- if (attrCount > 0) {
- el->attributes.reserve(attrCount);
- for (size_t i = 0; i < attrCount; i++) {
- Attribute attr;
- size_t len;
- const char16_t* str16 = parser->getAttributeNamespace(i, &len);
- if (str16) {
- attr.namespaceUri = util::utf16ToUtf8(StringPiece16(str16, len));
- }
-
- str16 = parser->getAttributeName(i, &len);
- if (str16) {
- attr.name = util::utf16ToUtf8(StringPiece16(str16, len));
- }
-
- str16 = parser->getAttributeStringValue(i, &len);
- if (str16) {
- attr.value = util::utf16ToUtf8(StringPiece16(str16, len));
- }
- el->attributes.push_back(std::move(attr));
- }
- }
-}
-
-std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
+std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag,
const Source& source) {
- // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
- // causes errors when qualifying it with android::
- using namespace android;
+ Stack stack;
- std::unique_ptr<Node> root;
- std::stack<Node*> nodeStack;
+ XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
+ XML_SetUserData(parser, &stack);
+ XML_UseParserAsHandlerArg(parser);
+ XML_SetElementHandler(parser, StartElementHandler, EndElementHandler);
+ XML_SetNamespaceDeclHandler(parser, StartNamespaceHandler,
+ EndNamespaceHandler);
+ XML_SetCharacterDataHandler(parser, CharacterDataHandler);
+ XML_SetCommentHandler(parser, CommentDataHandler);
- ResXMLTree tree;
- if (tree.setTo(data, dataLen) != NO_ERROR) {
- return {};
+ char buffer[1024];
+ while (!in->eof()) {
+ in->read(buffer, sizeof(buffer) / sizeof(buffer[0]));
+ if (in->bad() && !in->eof()) {
+ stack.root = {};
+ diag->Error(DiagMessage(source) << strerror(errno));
+ break;
}
- ResXMLParser::event_code_t code;
- while ((code = tree.next()) != ResXMLParser::BAD_DOCUMENT &&
- code != ResXMLParser::END_DOCUMENT) {
- std::unique_ptr<Node> newNode;
- switch (code) {
- case ResXMLParser::START_NAMESPACE: {
- std::unique_ptr<Namespace> node = util::make_unique<Namespace>();
- size_t len;
- const char16_t* str16 = tree.getNamespacePrefix(&len);
- if (str16) {
- node->namespacePrefix = util::utf16ToUtf8(StringPiece16(str16, len));
- }
+ if (XML_Parse(parser, buffer, in->gcount(), in->eof()) ==
+ XML_STATUS_ERROR) {
+ stack.root = {};
+ diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser)))
+ << XML_ErrorString(XML_GetErrorCode(parser)));
+ break;
+ }
+ }
- str16 = tree.getNamespaceUri(&len);
- if (str16) {
- node->namespaceUri = util::utf16ToUtf8(StringPiece16(str16, len));
- }
- newNode = std::move(node);
- break;
- }
+ XML_ParserFree(parser);
+ if (stack.root) {
+ return util::make_unique<XmlResource>(ResourceFile{{}, {}, source},
+ std::move(stack.root));
+ }
+ return {};
+}
- case ResXMLParser::START_TAG: {
- std::unique_ptr<Element> node = util::make_unique<Element>();
- size_t len;
- const char16_t* str16 = tree.getElementNamespace(&len);
- if (str16) {
- node->namespaceUri = util::utf16ToUtf8(StringPiece16(str16, len));
- }
+static void CopyAttributes(Element* el, android::ResXMLParser* parser) {
+ const size_t attr_count = parser->getAttributeCount();
+ if (attr_count > 0) {
+ el->attributes.reserve(attr_count);
+ for (size_t i = 0; i < attr_count; i++) {
+ Attribute attr;
+ size_t len;
+ const char16_t* str16 = parser->getAttributeNamespace(i, &len);
+ if (str16) {
+ attr.namespace_uri = util::Utf16ToUtf8(StringPiece16(str16, len));
+ }
- str16 = tree.getElementName(&len);
- if (str16) {
- node->name = util::utf16ToUtf8(StringPiece16(str16, len));
- }
+ str16 = parser->getAttributeName(i, &len);
+ if (str16) {
+ attr.name = util::Utf16ToUtf8(StringPiece16(str16, len));
+ }
- copyAttributes(node.get(), &tree);
+ str16 = parser->getAttributeStringValue(i, &len);
+ if (str16) {
+ attr.value = util::Utf16ToUtf8(StringPiece16(str16, len));
+ }
+ el->attributes.push_back(std::move(attr));
+ }
+ }
+}
- newNode = std::move(node);
- break;
- }
+std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len,
+ IDiagnostics* diag, const Source& source) {
+ // We import the android namespace because on Windows NO_ERROR is a macro, not
+ // an enum, which
+ // causes errors when qualifying it with android::
+ using namespace android;
- case ResXMLParser::TEXT: {
- std::unique_ptr<Text> node = util::make_unique<Text>();
- size_t len;
- const char16_t* str16 = tree.getText(&len);
- if (str16) {
- node->text = util::utf16ToUtf8(StringPiece16(str16, len));
- }
- newNode = std::move(node);
- break;
- }
+ std::unique_ptr<Node> root;
+ std::stack<Node*> node_stack;
- case ResXMLParser::END_NAMESPACE:
- case ResXMLParser::END_TAG:
- assert(!nodeStack.empty());
- nodeStack.pop();
- break;
+ ResXMLTree tree;
+ if (tree.setTo(data, data_len) != NO_ERROR) {
+ return {};
+ }
- default:
- assert(false);
- break;
+ ResXMLParser::event_code_t code;
+ while ((code = tree.next()) != ResXMLParser::BAD_DOCUMENT &&
+ code != ResXMLParser::END_DOCUMENT) {
+ std::unique_ptr<Node> new_node;
+ switch (code) {
+ case ResXMLParser::START_NAMESPACE: {
+ std::unique_ptr<Namespace> node = util::make_unique<Namespace>();
+ size_t len;
+ const char16_t* str16 = tree.getNamespacePrefix(&len);
+ if (str16) {
+ node->namespace_prefix = util::Utf16ToUtf8(StringPiece16(str16, len));
}
- if (newNode) {
- newNode->lineNumber = tree.getLineNumber();
-
- Node* thisNode = newNode.get();
- if (!root) {
- assert(nodeStack.empty());
- root = std::move(newNode);
- } else {
- assert(!nodeStack.empty());
- nodeStack.top()->addChild(std::move(newNode));
- }
-
- if (!nodeCast<Text>(thisNode)) {
- nodeStack.push(thisNode);
- }
+ str16 = tree.getNamespaceUri(&len);
+ if (str16) {
+ node->namespace_uri = util::Utf16ToUtf8(StringPiece16(str16, len));
}
- }
- return util::make_unique<XmlResource>(ResourceFile{}, std::move(root));
-}
+ new_node = std::move(node);
+ break;
+ }
-std::unique_ptr<Node> Namespace::clone() {
- auto ns = util::make_unique<Namespace>();
- ns->comment = comment;
- ns->lineNumber = lineNumber;
- ns->columnNumber = columnNumber;
- ns->namespacePrefix = namespacePrefix;
- ns->namespaceUri = namespaceUri;
-
- ns->children.reserve(children.size());
- for (const std::unique_ptr<xml::Node>& child : children) {
- ns->addChild(child->clone());
- }
- return std::move(ns);
-}
-
-Element* findRootElement(XmlResource* doc) {
- return findRootElement(doc->root.get());
-}
-
-Element* findRootElement(Node* node) {
- if (!node) {
- return nullptr;
- }
-
- Element* el = nullptr;
- while ((el = nodeCast<Element>(node)) == nullptr) {
- if (node->children.empty()) {
- return nullptr;
+ case ResXMLParser::START_TAG: {
+ std::unique_ptr<Element> node = util::make_unique<Element>();
+ size_t len;
+ const char16_t* str16 = tree.getElementNamespace(&len);
+ if (str16) {
+ node->namespace_uri = util::Utf16ToUtf8(StringPiece16(str16, len));
}
- // We are looking for the first element, and namespaces can only have one child.
- node = node->children.front().get();
- }
- return el;
-}
-void Node::addChild(std::unique_ptr<Node> child) {
- child->parent = this;
- children.push_back(std::move(child));
-}
-
-Attribute* Element::findAttribute(const StringPiece& ns, const StringPiece& name) {
- for (auto& attr : attributes) {
- if (ns == attr.namespaceUri && name == attr.name) {
- return &attr;
+ str16 = tree.getElementName(&len);
+ if (str16) {
+ node->name = util::Utf16ToUtf8(StringPiece16(str16, len));
}
+
+ CopyAttributes(node.get(), &tree);
+
+ new_node = std::move(node);
+ break;
+ }
+
+ case ResXMLParser::TEXT: {
+ std::unique_ptr<Text> node = util::make_unique<Text>();
+ size_t len;
+ const char16_t* str16 = tree.getText(&len);
+ if (str16) {
+ node->text = util::Utf16ToUtf8(StringPiece16(str16, len));
+ }
+ new_node = std::move(node);
+ break;
+ }
+
+ case ResXMLParser::END_NAMESPACE:
+ case ResXMLParser::END_TAG:
+ CHECK(!node_stack.empty());
+ node_stack.pop();
+ break;
+
+ default:
+ LOG(FATAL) << "unhandled XML chunk type";
+ break;
}
+
+ if (new_node) {
+ new_node->line_number = tree.getLineNumber();
+
+ Node* this_node = new_node.get();
+ if (!root) {
+ CHECK(node_stack.empty()) << "node stack should be empty";
+ root = std::move(new_node);
+ } else {
+ CHECK(!node_stack.empty()) << "node stack should not be empty";
+ node_stack.top()->AddChild(std::move(new_node));
+ }
+
+ if (!NodeCast<Text>(this_node)) {
+ node_stack.push(this_node);
+ }
+ }
+ }
+ return util::make_unique<XmlResource>(ResourceFile{}, std::move(root));
+}
+
+std::unique_ptr<Node> Namespace::Clone() {
+ auto ns = util::make_unique<Namespace>();
+ ns->comment = comment;
+ ns->line_number = line_number;
+ ns->column_number = column_number;
+ ns->namespace_prefix = namespace_prefix;
+ ns->namespace_uri = namespace_uri;
+
+ ns->children.reserve(children.size());
+ for (const std::unique_ptr<xml::Node>& child : children) {
+ ns->AddChild(child->Clone());
+ }
+ return std::move(ns);
+}
+
+Element* FindRootElement(XmlResource* doc) {
+ return FindRootElement(doc->root.get());
+}
+
+Element* FindRootElement(Node* node) {
+ if (!node) {
return nullptr;
-}
+ }
-Element* Element::findChild(const StringPiece& ns, const StringPiece& name) {
- return findChildWithAttribute(ns, name, {}, {}, {});
-}
-
-Element* Element::findChildWithAttribute(const StringPiece& ns, const StringPiece& name,
- const StringPiece& attrNs, const StringPiece& attrName,
- const StringPiece& attrValue) {
- for (auto& childNode : children) {
- Node* child = childNode.get();
- while (nodeCast<Namespace>(child)) {
- if (child->children.empty()) {
- break;
- }
- child = child->children[0].get();
- }
-
- if (Element* el = nodeCast<Element>(child)) {
- if (ns == el->namespaceUri && name == el->name) {
- if (attrNs.empty() && attrName.empty()) {
- return el;
- }
-
- Attribute* attr = el->findAttribute(attrNs, attrName);
- if (attr && attrValue == attr->value) {
- return el;
- }
- }
- }
+ Element* el = nullptr;
+ while ((el = NodeCast<Element>(node)) == nullptr) {
+ if (node->children.empty()) {
+ return nullptr;
}
- return nullptr;
+ // We are looking for the first element, and namespaces can only have one
+ // child.
+ node = node->children.front().get();
+ }
+ return el;
}
-std::vector<Element*> Element::getChildElements() {
- std::vector<Element*> elements;
- for (auto& childNode : children) {
- Node* child = childNode.get();
- while (nodeCast<Namespace>(child)) {
- if (child->children.empty()) {
- break;
- }
- child = child->children[0].get();
- }
+void Node::AddChild(std::unique_ptr<Node> child) {
+ child->parent = this;
+ children.push_back(std::move(child));
+}
- if (Element* el = nodeCast<Element>(child)) {
- elements.push_back(el);
- }
+Attribute* Element::FindAttribute(const StringPiece& ns,
+ const StringPiece& name) {
+ for (auto& attr : attributes) {
+ if (ns == attr.namespace_uri && name == attr.name) {
+ return &attr;
}
- return elements;
+ }
+ return nullptr;
}
-std::unique_ptr<Node> Element::clone() {
- auto el = util::make_unique<Element>();
- el->comment = comment;
- el->lineNumber = lineNumber;
- el->columnNumber = columnNumber;
- el->name = name;
- el->namespaceUri = namespaceUri;
+Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) {
+ return FindChildWithAttribute(ns, name, {}, {}, {});
+}
- el->attributes.reserve(attributes.size());
- for (xml::Attribute& attr : attributes) {
- // Don't copy compiled values or attributes.
- el->attributes.push_back(xml::Attribute{ attr.namespaceUri, attr.name, attr.value });
+Element* Element::FindChildWithAttribute(const StringPiece& ns,
+ const StringPiece& name,
+ const StringPiece& attr_ns,
+ const StringPiece& attr_name,
+ const StringPiece& attr_value) {
+ for (auto& child_node : children) {
+ Node* child = child_node.get();
+ while (NodeCast<Namespace>(child)) {
+ if (child->children.empty()) {
+ break;
+ }
+ child = child->children[0].get();
}
- el->children.reserve(children.size());
- for (const std::unique_ptr<xml::Node>& child : children) {
- el->addChild(child->clone());
+ if (Element* el = NodeCast<Element>(child)) {
+ if (ns == el->namespace_uri && name == el->name) {
+ if (attr_ns.empty() && attr_name.empty()) {
+ return el;
+ }
+
+ Attribute* attr = el->FindAttribute(attr_ns, attr_name);
+ if (attr && attr_value == attr->value) {
+ return el;
+ }
+ }
}
- return std::move(el);
+ }
+ return nullptr;
}
-std::unique_ptr<Node> Text::clone() {
- auto t = util::make_unique<Text>();
- t->comment = comment;
- t->lineNumber = lineNumber;
- t->columnNumber = columnNumber;
- t->text = text;
- return std::move(t);
+std::vector<Element*> Element::GetChildElements() {
+ std::vector<Element*> elements;
+ for (auto& child_node : children) {
+ Node* child = child_node.get();
+ while (NodeCast<Namespace>(child)) {
+ if (child->children.empty()) {
+ break;
+ }
+ child = child->children[0].get();
+ }
+
+ if (Element* el = NodeCast<Element>(child)) {
+ elements.push_back(el);
+ }
+ }
+ return elements;
}
-void PackageAwareVisitor::visit(Namespace* ns) {
- bool added = false;
- if (Maybe<ExtractedPackage> maybePackage = extractPackageFromNamespace(ns->namespaceUri)) {
- ExtractedPackage& package = maybePackage.value();
- mPackageDecls.push_back(PackageDecl{ ns->namespacePrefix, std::move(package) });
- added = true;
- }
+std::unique_ptr<Node> Element::Clone() {
+ auto el = util::make_unique<Element>();
+ el->comment = comment;
+ el->line_number = line_number;
+ el->column_number = column_number;
+ el->name = name;
+ el->namespace_uri = namespace_uri;
- Visitor::visit(ns);
+ el->attributes.reserve(attributes.size());
+ for (xml::Attribute& attr : attributes) {
+ // Don't copy compiled values or attributes.
+ el->attributes.push_back(
+ xml::Attribute{attr.namespace_uri, attr.name, attr.value});
+ }
- if (added) {
- mPackageDecls.pop_back();
- }
+ el->children.reserve(children.size());
+ for (const std::unique_ptr<xml::Node>& child : children) {
+ el->AddChild(child->Clone());
+ }
+ return std::move(el);
}
-Maybe<ExtractedPackage> PackageAwareVisitor::transformPackageAlias(
- const StringPiece& alias, const StringPiece& localPackage) const {
- if (alias.empty()) {
- return ExtractedPackage{ localPackage.toString(), false /* private */ };
- }
-
- const auto rend = mPackageDecls.rend();
- for (auto iter = mPackageDecls.rbegin(); iter != rend; ++iter) {
- if (alias == iter->prefix) {
- if (iter->package.package.empty()) {
- return ExtractedPackage{ localPackage.toString(),
- iter->package.privateNamespace };
- }
- return iter->package;
- }
- }
- return {};
+std::unique_ptr<Node> Text::Clone() {
+ auto t = util::make_unique<Text>();
+ t->comment = comment;
+ t->line_number = line_number;
+ t->column_number = column_number;
+ t->text = text;
+ return std::move(t);
}
-} // namespace xml
-} // namespace aapt
+void PackageAwareVisitor::Visit(Namespace* ns) {
+ bool added = false;
+ if (Maybe<ExtractedPackage> maybe_package =
+ ExtractPackageFromNamespace(ns->namespace_uri)) {
+ ExtractedPackage& package = maybe_package.value();
+ package_decls_.push_back(
+ PackageDecl{ns->namespace_prefix, std::move(package)});
+ added = true;
+ }
+
+ Visitor::Visit(ns);
+
+ if (added) {
+ package_decls_.pop_back();
+ }
+}
+
+Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
+ const StringPiece& alias, const StringPiece& local_package) const {
+ if (alias.empty()) {
+ return ExtractedPackage{local_package.ToString(), false /* private */};
+ }
+
+ const auto rend = package_decls_.rend();
+ for (auto iter = package_decls_.rbegin(); iter != rend; ++iter) {
+ if (alias == iter->prefix) {
+ if (iter->package.package.empty()) {
+ return ExtractedPackage{local_package.ToString(),
+ iter->package.private_namespace};
+ }
+ return iter->package;
+ }
+ }
+ return {};
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index e4f41b0..e771d87 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -17,6 +17,11 @@
#ifndef AAPT_XML_DOM_H
#define AAPT_XML_DOM_H
+#include <istream>
+#include <memory>
+#include <string>
+#include <vector>
+
#include "Diagnostics.h"
#include "Resource.h"
#include "ResourceValues.h"
@@ -24,11 +29,6 @@
#include "util/Util.h"
#include "xml/XmlUtil.h"
-#include <istream>
-#include <memory>
-#include <string>
-#include <vector>
-
namespace aapt {
namespace xml {
@@ -38,18 +38,18 @@
* Base class for all XML nodes.
*/
class Node {
-public:
- Node* parent = nullptr;
- size_t lineNumber = 0;
- size_t columnNumber = 0;
- std::string comment;
- std::vector<std::unique_ptr<Node>> children;
+ public:
+ Node* parent = nullptr;
+ size_t line_number = 0;
+ size_t column_number = 0;
+ std::string comment;
+ std::vector<std::unique_ptr<Node>> children;
- virtual ~Node() = default;
+ virtual ~Node() = default;
- void addChild(std::unique_ptr<Node> child);
- virtual void accept(RawVisitor* visitor) = 0;
- virtual std::unique_ptr<Node> clone() = 0;
+ void AddChild(std::unique_ptr<Node> child);
+ virtual void Accept(RawVisitor* visitor) = 0;
+ virtual std::unique_ptr<Node> Clone() = 0;
};
/**
@@ -58,178 +58,174 @@
*/
template <typename Derived>
class BaseNode : public Node {
-public:
- virtual void accept(RawVisitor* visitor) override;
+ public:
+ virtual void Accept(RawVisitor* visitor) override;
};
/**
* A Namespace XML node. Can only have one child.
*/
class Namespace : public BaseNode<Namespace> {
-public:
- std::string namespacePrefix;
- std::string namespaceUri;
+ public:
+ std::string namespace_prefix;
+ std::string namespace_uri;
- std::unique_ptr<Node> clone() override;
+ std::unique_ptr<Node> Clone() override;
};
struct AaptAttribute {
- Maybe<ResourceId> id;
- aapt::Attribute attribute;
+ Maybe<ResourceId> id;
+ aapt::Attribute attribute;
};
/**
* An XML attribute.
*/
struct Attribute {
- std::string namespaceUri;
- std::string name;
- std::string value;
+ std::string namespace_uri;
+ std::string name;
+ std::string value;
- Maybe<AaptAttribute> compiledAttribute;
- std::unique_ptr<Item> compiledValue;
+ Maybe<AaptAttribute> compiled_attribute;
+ std::unique_ptr<Item> compiled_value;
};
/**
* An Element XML node.
*/
class Element : public BaseNode<Element> {
-public:
- std::string namespaceUri;
- std::string name;
- std::vector<Attribute> attributes;
+ public:
+ std::string namespace_uri;
+ std::string name;
+ std::vector<Attribute> attributes;
- Attribute* findAttribute(const StringPiece& ns, const StringPiece& name);
- xml::Element* findChild(const StringPiece& ns, const StringPiece& name);
- xml::Element* findChildWithAttribute(const StringPiece& ns, const StringPiece& name,
- const StringPiece& attrNs,
- const StringPiece& attrName,
- const StringPiece& attrValue);
- std::vector<xml::Element*> getChildElements();
- std::unique_ptr<Node> clone() override;
+ Attribute* FindAttribute(const StringPiece& ns, const StringPiece& name);
+ xml::Element* FindChild(const StringPiece& ns, const StringPiece& name);
+ xml::Element* FindChildWithAttribute(const StringPiece& ns,
+ const StringPiece& name,
+ const StringPiece& attr_ns,
+ const StringPiece& attr_name,
+ const StringPiece& attr_value);
+ std::vector<xml::Element*> GetChildElements();
+ std::unique_ptr<Node> Clone() override;
};
/**
* A Text (CDATA) XML node. Can not have any children.
*/
class Text : public BaseNode<Text> {
-public:
- std::string text;
+ public:
+ std::string text;
- std::unique_ptr<Node> clone() override;
+ std::unique_ptr<Node> Clone() override;
};
/**
* An XML resource with a source, name, and XML tree.
*/
class XmlResource {
-public:
- ResourceFile file;
- std::unique_ptr<xml::Node> root;
+ public:
+ ResourceFile file;
+ std::unique_ptr<xml::Node> root;
};
/**
* Inflates an XML DOM from a text stream, logging errors to the logger.
* Returns the root node on success, or nullptr on failure.
*/
-std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag, const Source& source);
+std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag,
+ const Source& source);
/**
* Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger.
* Returns the root node on success, or nullptr on failure.
*/
-std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
- const Source& source);
+std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len,
+ IDiagnostics* diag, const Source& source);
-Element* findRootElement(XmlResource* doc);
-Element* findRootElement(Node* node);
+Element* FindRootElement(XmlResource* doc);
+Element* FindRootElement(Node* node);
/**
- * A visitor interface for the different XML Node subtypes. This will not traverse into
+ * A visitor interface for the different XML Node subtypes. This will not
+ * traverse into
* children. Use Visitor for that.
*/
class RawVisitor {
-public:
- virtual ~RawVisitor() = default;
+ public:
+ virtual ~RawVisitor() = default;
- virtual void visit(Namespace* node) {}
- virtual void visit(Element* node) {}
- virtual void visit(Text* text) {}
+ virtual void Visit(Namespace* node) {}
+ virtual void Visit(Element* node) {}
+ virtual void Visit(Text* text) {}
};
/**
* Visitor whose default implementation visits the children nodes of any node.
*/
class Visitor : public RawVisitor {
-public:
- using RawVisitor::visit;
+ public:
+ using RawVisitor::Visit;
- void visit(Namespace* node) override {
- visitChildren(node);
- }
+ void Visit(Namespace* node) override { VisitChildren(node); }
- void visit(Element* node) override {
- visitChildren(node);
- }
+ void Visit(Element* node) override { VisitChildren(node); }
- void visit(Text* text) override {
- visitChildren(text);
- }
+ void Visit(Text* text) override { VisitChildren(text); }
- void visitChildren(Node* node) {
- for (auto& child : node->children) {
- child->accept(this);
- }
+ void VisitChildren(Node* node) {
+ for (auto& child : node->children) {
+ child->Accept(this);
}
+ }
};
/**
* An XML DOM visitor that will record the package name for a namespace prefix.
*/
class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
-public:
- using Visitor::visit;
+ public:
+ using Visitor::Visit;
- void visit(Namespace* ns) override;
- Maybe<ExtractedPackage> transformPackageAlias(
- const StringPiece& alias, const StringPiece& localPackage) const override;
+ void Visit(Namespace* ns) override;
+ Maybe<ExtractedPackage> TransformPackageAlias(
+ const StringPiece& alias,
+ const StringPiece& local_package) const override;
-private:
- struct PackageDecl {
- std::string prefix;
- ExtractedPackage package;
- };
+ private:
+ struct PackageDecl {
+ std::string prefix;
+ ExtractedPackage package;
+ };
- std::vector<PackageDecl> mPackageDecls;
+ std::vector<PackageDecl> package_decls_;
};
// Implementations
template <typename Derived>
-void BaseNode<Derived>::accept(RawVisitor* visitor) {
- visitor->visit(static_cast<Derived*>(this));
+void BaseNode<Derived>::Accept(RawVisitor* visitor) {
+ visitor->Visit(static_cast<Derived*>(this));
}
template <typename T>
class NodeCastImpl : public RawVisitor {
-public:
- using RawVisitor::visit;
+ public:
+ using RawVisitor::Visit;
- T* value = nullptr;
+ T* value = nullptr;
- void visit(T* v) override {
- value = v;
- }
+ void Visit(T* v) override { value = v; }
};
template <typename T>
-T* nodeCast(Node* node) {
- NodeCastImpl<T> visitor;
- node->accept(&visitor);
- return visitor.value;
+T* NodeCast(Node* node) {
+ NodeCastImpl<T> visitor;
+ node->Accept(&visitor);
+ return visitor.value;
}
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
-#endif // AAPT_XML_DOM_H
+#endif // AAPT_XML_DOM_H
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index 1909f75..a414afe 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -16,17 +16,19 @@
#include "xml/XmlDom.h"
-#include <gtest/gtest.h>
#include <sstream>
#include <string>
+#include "test/Test.h"
+
namespace aapt {
-constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+constexpr const char* kXmlPreamble =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
TEST(XmlDomTest, Inflate) {
- std::stringstream in(kXmlPreamble);
- in << R"EOF(
+ std::stringstream in(kXmlPreamble);
+ in << R"EOF(
<Layout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@@ -36,15 +38,15 @@
</Layout>
)EOF";
- const Source source = { "test.xml" };
- StdErrDiagnostics diag;
- std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, source);
- ASSERT_NE(doc, nullptr);
+ const Source source = {"test.xml"};
+ StdErrDiagnostics diag;
+ std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, source);
+ ASSERT_NE(doc, nullptr);
- xml::Namespace* ns = xml::nodeCast<xml::Namespace>(doc->root.get());
- ASSERT_NE(ns, nullptr);
- EXPECT_EQ(ns->namespaceUri, xml::kSchemaAndroid);
- EXPECT_EQ(ns->namespacePrefix, "android");
+ xml::Namespace* ns = xml::NodeCast<xml::Namespace>(doc->root.get());
+ ASSERT_NE(ns, nullptr);
+ EXPECT_EQ(ns->namespace_uri, xml::kSchemaAndroid);
+ EXPECT_EQ(ns->namespace_prefix, "android");
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index 4a944f1..e59fa86 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -14,295 +14,295 @@
* limitations under the License.
*/
+#include <iostream>
+#include <string>
+
#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlPullParser.h"
#include "xml/XmlUtil.h"
-#include <iostream>
-#include <string>
-
namespace aapt {
namespace xml {
constexpr char kXmlNamespaceSep = 1;
-XmlPullParser::XmlPullParser(std::istream& in) : mIn(in), mEmpty(), mDepth(0) {
- mParser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
- XML_SetUserData(mParser, this);
- XML_SetElementHandler(mParser, startElementHandler, endElementHandler);
- XML_SetNamespaceDeclHandler(mParser, startNamespaceHandler, endNamespaceHandler);
- XML_SetCharacterDataHandler(mParser, characterDataHandler);
- XML_SetCommentHandler(mParser, commentDataHandler);
- mEventQueue.push(EventData{ Event::kStartDocument, 0, mDepth++ });
+XmlPullParser::XmlPullParser(std::istream& in) : in_(in), empty_(), depth_(0) {
+ parser_ = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
+ XML_SetUserData(parser_, this);
+ XML_SetElementHandler(parser_, StartElementHandler, EndElementHandler);
+ XML_SetNamespaceDeclHandler(parser_, StartNamespaceHandler,
+ EndNamespaceHandler);
+ XML_SetCharacterDataHandler(parser_, CharacterDataHandler);
+ XML_SetCommentHandler(parser_, CommentDataHandler);
+ event_queue_.push(EventData{Event::kStartDocument, 0, depth_++});
}
-XmlPullParser::~XmlPullParser() {
- XML_ParserFree(mParser);
-}
+XmlPullParser::~XmlPullParser() { XML_ParserFree(parser_); }
-XmlPullParser::Event XmlPullParser::next() {
- const Event currentEvent = getEvent();
- if (currentEvent == Event::kBadDocument || currentEvent == Event::kEndDocument) {
- return currentEvent;
+XmlPullParser::Event XmlPullParser::Next() {
+ const Event currentEvent = event();
+ if (currentEvent == Event::kBadDocument ||
+ currentEvent == Event::kEndDocument) {
+ return currentEvent;
+ }
+
+ event_queue_.pop();
+ while (event_queue_.empty()) {
+ in_.read(buffer_, sizeof(buffer_) / sizeof(*buffer_));
+
+ const bool done = in_.eof();
+ if (in_.bad() && !done) {
+ error_ = strerror(errno);
+ event_queue_.push(EventData{Event::kBadDocument});
+ continue;
}
- mEventQueue.pop();
- while (mEventQueue.empty()) {
- mIn.read(mBuffer, sizeof(mBuffer) / sizeof(*mBuffer));
-
- const bool done = mIn.eof();
- if (mIn.bad() && !done) {
- mLastError = strerror(errno);
- mEventQueue.push(EventData{ Event::kBadDocument });
- continue;
- }
-
- if (XML_Parse(mParser, mBuffer, mIn.gcount(), done) == XML_STATUS_ERROR) {
- mLastError = XML_ErrorString(XML_GetErrorCode(mParser));
- mEventQueue.push(EventData{ Event::kBadDocument });
- continue;
- }
-
- if (done) {
- mEventQueue.push(EventData{ Event::kEndDocument, 0, 0 });
- }
+ if (XML_Parse(parser_, buffer_, in_.gcount(), done) == XML_STATUS_ERROR) {
+ error_ = XML_ErrorString(XML_GetErrorCode(parser_));
+ event_queue_.push(EventData{Event::kBadDocument});
+ continue;
}
- Event event = getEvent();
-
- // Record namespace prefixes and package names so that we can do our own
- // handling of references that use namespace aliases.
- if (event == Event::kStartNamespace || event == Event::kEndNamespace) {
- Maybe<ExtractedPackage> result = extractPackageFromNamespace(getNamespaceUri());
- if (event == Event::kStartNamespace) {
- if (result) {
- mPackageAliases.emplace_back(
- PackageDecl{ getNamespacePrefix(), std::move(result.value()) });
- }
- } else {
- if (result) {
- mPackageAliases.pop_back();
- }
- }
+ if (done) {
+ event_queue_.push(EventData{Event::kEndDocument, 0, 0});
}
+ }
- return event;
-}
+ Event next_event = event();
-XmlPullParser::Event XmlPullParser::getEvent() const {
- return mEventQueue.front().event;
-}
-
-const std::string& XmlPullParser::getLastError() const {
- return mLastError;
-}
-
-const std::string& XmlPullParser::getComment() const {
- return mEventQueue.front().data1;
-}
-
-size_t XmlPullParser::getLineNumber() const {
- return mEventQueue.front().lineNumber;
-}
-
-size_t XmlPullParser::getDepth() const {
- return mEventQueue.front().depth;
-}
-
-const std::string& XmlPullParser::getText() const {
- if (getEvent() != Event::kText) {
- return mEmpty;
+ // Record namespace prefixes and package names so that we can do our own
+ // handling of references that use namespace aliases.
+ if (next_event == Event::kStartNamespace ||
+ next_event == Event::kEndNamespace) {
+ Maybe<ExtractedPackage> result =
+ ExtractPackageFromNamespace(namespace_uri());
+ if (next_event == Event::kStartNamespace) {
+ if (result) {
+ package_aliases_.emplace_back(
+ PackageDecl{namespace_prefix(), std::move(result.value())});
+ }
+ } else {
+ if (result) {
+ package_aliases_.pop_back();
+ }
}
- return mEventQueue.front().data1;
+ }
+
+ return next_event;
}
-const std::string& XmlPullParser::getNamespacePrefix() const {
- const Event currentEvent = getEvent();
- if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
- return mEmpty;
+XmlPullParser::Event XmlPullParser::event() const {
+ return event_queue_.front().event;
+}
+
+const std::string& XmlPullParser::error() const { return error_; }
+
+const std::string& XmlPullParser::comment() const {
+ return event_queue_.front().data1;
+}
+
+size_t XmlPullParser::line_number() const {
+ return event_queue_.front().line_number;
+}
+
+size_t XmlPullParser::depth() const { return event_queue_.front().depth; }
+
+const std::string& XmlPullParser::text() const {
+ if (event() != Event::kText) {
+ return empty_;
+ }
+ return event_queue_.front().data1;
+}
+
+const std::string& XmlPullParser::namespace_prefix() const {
+ const Event current_event = event();
+ if (current_event != Event::kStartNamespace &&
+ current_event != Event::kEndNamespace) {
+ return empty_;
+ }
+ return event_queue_.front().data1;
+}
+
+const std::string& XmlPullParser::namespace_uri() const {
+ const Event current_event = event();
+ if (current_event != Event::kStartNamespace &&
+ current_event != Event::kEndNamespace) {
+ return empty_;
+ }
+ return event_queue_.front().data2;
+}
+
+Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(
+ const StringPiece& alias, const StringPiece& local_package) const {
+ if (alias.empty()) {
+ return ExtractedPackage{local_package.ToString(), false /* private */};
+ }
+
+ const auto end_iter = package_aliases_.rend();
+ for (auto iter = package_aliases_.rbegin(); iter != end_iter; ++iter) {
+ if (alias == iter->prefix) {
+ if (iter->package.package.empty()) {
+ return ExtractedPackage{local_package.ToString(),
+ iter->package.private_namespace};
+ }
+ return iter->package;
}
- return mEventQueue.front().data1;
+ }
+ return {};
}
-const std::string& XmlPullParser::getNamespaceUri() const {
- const Event currentEvent = getEvent();
- if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
- return mEmpty;
- }
- return mEventQueue.front().data2;
+const std::string& XmlPullParser::element_namespace() const {
+ const Event current_event = event();
+ if (current_event != Event::kStartElement &&
+ current_event != Event::kEndElement) {
+ return empty_;
+ }
+ return event_queue_.front().data1;
}
-Maybe<ExtractedPackage> XmlPullParser::transformPackageAlias(
- const StringPiece& alias, const StringPiece& localPackage) const {
- if (alias.empty()) {
- return ExtractedPackage{ localPackage.toString(), false /* private */ };
- }
-
- const auto endIter = mPackageAliases.rend();
- for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
- if (alias == iter->prefix) {
- if (iter->package.package.empty()) {
- return ExtractedPackage{ localPackage.toString(),
- iter->package.privateNamespace };
- }
- return iter->package;
- }
- }
- return {};
+const std::string& XmlPullParser::element_name() const {
+ const Event current_event = event();
+ if (current_event != Event::kStartElement &&
+ current_event != Event::kEndElement) {
+ return empty_;
+ }
+ return event_queue_.front().data2;
}
-const std::string& XmlPullParser::getElementNamespace() const {
- const Event currentEvent = getEvent();
- if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
- return mEmpty;
- }
- return mEventQueue.front().data1;
+XmlPullParser::const_iterator XmlPullParser::begin_attributes() const {
+ return event_queue_.front().attributes.begin();
}
-const std::string& XmlPullParser::getElementName() const {
- const Event currentEvent = getEvent();
- if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
- return mEmpty;
- }
- return mEventQueue.front().data2;
+XmlPullParser::const_iterator XmlPullParser::end_attributes() const {
+ return event_queue_.front().attributes.end();
}
-XmlPullParser::const_iterator XmlPullParser::beginAttributes() const {
- return mEventQueue.front().attributes.begin();
-}
-
-XmlPullParser::const_iterator XmlPullParser::endAttributes() const {
- return mEventQueue.front().attributes.end();
-}
-
-size_t XmlPullParser::getAttributeCount() const {
- if (getEvent() != Event::kStartElement) {
- return 0;
- }
- return mEventQueue.front().attributes.size();
+size_t XmlPullParser::attribute_count() const {
+ if (event() != Event::kStartElement) {
+ return 0;
+ }
+ return event_queue_.front().attributes.size();
}
/**
* Extracts the namespace and name of an expanded element or attribute name.
*/
-static void splitName(const char* name, std::string& outNs, std::string& outName) {
- const char* p = name;
- while (*p != 0 && *p != kXmlNamespaceSep) {
- p++;
+static void SplitName(const char* name, std::string& out_ns,
+ std::string& out_name) {
+ const char* p = name;
+ while (*p != 0 && *p != kXmlNamespaceSep) {
+ p++;
+ }
+
+ if (*p == 0) {
+ out_ns = std::string();
+ out_name = name;
+ } else {
+ out_ns = StringPiece(name, (p - name)).ToString();
+ out_name = p + 1;
+ }
+}
+
+void XMLCALL XmlPullParser::StartNamespaceHandler(void* user_data,
+ const char* prefix,
+ const char* uri) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+ std::string namespace_uri = uri != nullptr ? uri : std::string();
+ parser->namespace_uris_.push(namespace_uri);
+ parser->event_queue_.push(
+ EventData{Event::kStartNamespace,
+ XML_GetCurrentLineNumber(parser->parser_), parser->depth_++,
+ prefix != nullptr ? prefix : std::string(), namespace_uri});
+}
+
+void XMLCALL XmlPullParser::StartElementHandler(void* user_data,
+ const char* name,
+ const char** attrs) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+ EventData data = {Event::kStartElement,
+ XML_GetCurrentLineNumber(parser->parser_),
+ parser->depth_++};
+ SplitName(name, data.data1, data.data2);
+
+ while (*attrs) {
+ Attribute attribute;
+ SplitName(*attrs++, attribute.namespace_uri, attribute.name);
+ attribute.value = *attrs++;
+
+ // Insert in sorted order.
+ auto iter = std::lower_bound(data.attributes.begin(), data.attributes.end(),
+ attribute);
+ data.attributes.insert(iter, std::move(attribute));
+ }
+
+ // Move the structure into the queue (no copy).
+ parser->event_queue_.push(std::move(data));
+}
+
+void XMLCALL XmlPullParser::CharacterDataHandler(void* user_data, const char* s,
+ int len) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+ parser->event_queue_.push(
+ EventData{Event::kText, XML_GetCurrentLineNumber(parser->parser_),
+ parser->depth_, StringPiece(s, len).ToString()});
+}
+
+void XMLCALL XmlPullParser::EndElementHandler(void* user_data,
+ const char* name) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+ EventData data = {Event::kEndElement,
+ XML_GetCurrentLineNumber(parser->parser_),
+ --(parser->depth_)};
+ SplitName(name, data.data1, data.data2);
+
+ // Move the data into the queue (no copy).
+ parser->event_queue_.push(std::move(data));
+}
+
+void XMLCALL XmlPullParser::EndNamespaceHandler(void* user_data,
+ const char* prefix) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+ parser->event_queue_.push(
+ EventData{Event::kEndNamespace, XML_GetCurrentLineNumber(parser->parser_),
+ --(parser->depth_), prefix != nullptr ? prefix : std::string(),
+ parser->namespace_uris_.top()});
+ parser->namespace_uris_.pop();
+}
+
+void XMLCALL XmlPullParser::CommentDataHandler(void* user_data,
+ const char* comment) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+ parser->event_queue_.push(EventData{Event::kComment,
+ XML_GetCurrentLineNumber(parser->parser_),
+ parser->depth_, comment});
+}
+
+Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
+ const StringPiece& name) {
+ auto iter = parser->FindAttribute("", name);
+ if (iter != parser->end_attributes()) {
+ return StringPiece(util::TrimWhitespace(iter->value));
+ }
+ return {};
+}
+
+Maybe<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
+ const StringPiece& name) {
+ auto iter = parser->FindAttribute("", name);
+ if (iter != parser->end_attributes()) {
+ StringPiece trimmed = util::TrimWhitespace(iter->value);
+ if (!trimmed.empty()) {
+ return trimmed;
}
-
- if (*p == 0) {
- outNs = std::string();
- outName = name;
- } else {
- outNs = StringPiece(name, (p - name)).toString();
- outName = p + 1;
- }
+ }
+ return {};
}
-void XMLCALL XmlPullParser::startNamespaceHandler(void* userData, const char* prefix,
- const char* uri) {
- XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
- std::string namespaceUri = uri != nullptr ? uri : std::string();
- parser->mNamespaceUris.push(namespaceUri);
- parser->mEventQueue.push(EventData{
- Event::kStartNamespace,
- XML_GetCurrentLineNumber(parser->mParser),
- parser->mDepth++,
- prefix != nullptr ? prefix : std::string(),
- namespaceUri
- });
-}
-
-void XMLCALL XmlPullParser::startElementHandler(void* userData, const char* name,
- const char** attrs) {
- XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
-
- EventData data = {
- Event::kStartElement, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth++
- };
- splitName(name, data.data1, data.data2);
-
- while (*attrs) {
- Attribute attribute;
- splitName(*attrs++, attribute.namespaceUri, attribute.name);
- attribute.value = *attrs++;
-
- // Insert in sorted order.
- auto iter = std::lower_bound(data.attributes.begin(), data.attributes.end(), attribute);
- data.attributes.insert(iter, std::move(attribute));
- }
-
- // Move the structure into the queue (no copy).
- parser->mEventQueue.push(std::move(data));
-}
-
-void XMLCALL XmlPullParser::characterDataHandler(void* userData, const char* s, int len) {
- XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
-
- parser->mEventQueue.push(EventData{
- Event::kText,
- XML_GetCurrentLineNumber(parser->mParser),
- parser->mDepth,
- StringPiece(s, len).toString()
- });
-}
-
-void XMLCALL XmlPullParser::endElementHandler(void* userData, const char* name) {
- XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
-
- EventData data = {
- Event::kEndElement, XML_GetCurrentLineNumber(parser->mParser), --(parser->mDepth)
- };
- splitName(name, data.data1, data.data2);
-
- // Move the data into the queue (no copy).
- parser->mEventQueue.push(std::move(data));
-}
-
-void XMLCALL XmlPullParser::endNamespaceHandler(void* userData, const char* prefix) {
- XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
-
- parser->mEventQueue.push(EventData{
- Event::kEndNamespace,
- XML_GetCurrentLineNumber(parser->mParser),
- --(parser->mDepth),
- prefix != nullptr ? prefix : std::string(),
- parser->mNamespaceUris.top()
- });
- parser->mNamespaceUris.pop();
-}
-
-void XMLCALL XmlPullParser::commentDataHandler(void* userData, const char* comment) {
- XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
-
- parser->mEventQueue.push(EventData{
- Event::kComment,
- XML_GetCurrentLineNumber(parser->mParser),
- parser->mDepth,
- comment
- });
-}
-
-Maybe<StringPiece> findAttribute(const XmlPullParser* parser, const StringPiece& name) {
- auto iter = parser->findAttribute("", name);
- if (iter != parser->endAttributes()) {
- return StringPiece(util::trimWhitespace(iter->value));
- }
- return {};
-}
-
-Maybe<StringPiece> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece& name) {
- auto iter = parser->findAttribute("", name);
- if (iter != parser->endAttributes()) {
- StringPiece trimmed = util::trimWhitespace(iter->value);
- if (!trimmed.empty()) {
- return trimmed;
- }
- }
- return {};
-}
-
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index a24d109..ff58d60 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -17,14 +17,9 @@
#ifndef AAPT_XML_PULL_PARSER_H
#define AAPT_XML_PULL_PARSER_H
-#include "Resource.h"
-#include "process/IResourceTableConsumer.h"
-#include "util/Maybe.h"
-#include "util/StringPiece.h"
-#include "xml/XmlUtil.h"
+#include <expat.h>
#include <algorithm>
-#include <expat.h>
#include <istream>
#include <ostream>
#include <queue>
@@ -32,267 +27,303 @@
#include <string>
#include <vector>
+#include "android-base/macros.h"
+
+#include "Resource.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+#include "xml/XmlUtil.h"
+
namespace aapt {
namespace xml {
class XmlPullParser : public IPackageDeclStack {
-public:
- enum class Event {
- kBadDocument,
- kStartDocument,
- kEndDocument,
+ public:
+ enum class Event {
+ kBadDocument,
+ kStartDocument,
+ kEndDocument,
- kStartNamespace,
- kEndNamespace,
- kStartElement,
- kEndElement,
- kText,
- kComment,
- };
+ kStartNamespace,
+ kEndNamespace,
+ kStartElement,
+ kEndElement,
+ kText,
+ kComment,
+ };
- /**
- * Skips to the next direct descendant node of the given startDepth,
- * skipping namespace nodes.
- *
- * When nextChildNode returns true, you can expect Comments, Text, and StartElement events.
- */
- static bool nextChildNode(XmlPullParser* parser, size_t startDepth);
- static bool skipCurrentElement(XmlPullParser* parser);
- static bool isGoodEvent(Event event);
+ /**
+ * Skips to the next direct descendant node of the given start_depth,
+ * skipping namespace nodes.
+ *
+ * When NextChildNode() returns true, you can expect Comments, Text, and
+ * StartElement events.
+ */
+ static bool NextChildNode(XmlPullParser* parser, size_t start_depth);
+ static bool SkipCurrentElement(XmlPullParser* parser);
+ static bool IsGoodEvent(Event event);
- explicit XmlPullParser(std::istream& in);
- ~XmlPullParser();
+ explicit XmlPullParser(std::istream& in);
+ ~XmlPullParser();
- /**
- * Returns the current event that is being processed.
- */
- Event getEvent() const;
+ /**
+ * Returns the current event that is being processed.
+ */
+ Event event() const;
- const std::string& getLastError() const;
+ const std::string& error() const;
- /**
- * Note, unlike XmlPullParser, the first call to next() will return
- * StartElement of the first element.
- */
- Event next();
+ /**
+ * Note, unlike XmlPullParser, the first call to next() will return
+ * StartElement of the first element.
+ */
+ Event Next();
- //
- // These are available for all nodes.
- //
+ //
+ // These are available for all nodes.
+ //
- const std::string& getComment() const;
- size_t getLineNumber() const;
- size_t getDepth() const;
+ const std::string& comment() const;
+ size_t line_number() const;
+ size_t depth() const;
- /**
- * Returns the character data for a Text event.
- */
- const std::string& getText() const;
+ /**
+ * Returns the character data for a Text event.
+ */
+ const std::string& text() const;
- //
- // Namespace prefix and URI are available for StartNamespace and EndNamespace.
- //
+ //
+ // Namespace prefix and URI are available for StartNamespace and EndNamespace.
+ //
- const std::string& getNamespacePrefix() const;
- const std::string& getNamespaceUri() const;
+ const std::string& namespace_prefix() const;
+ const std::string& namespace_uri() const;
- //
- // These are available for StartElement and EndElement.
- //
+ //
+ // These are available for StartElement and EndElement.
+ //
- const std::string& getElementNamespace() const;
- const std::string& getElementName() const;
+ const std::string& element_namespace() const;
+ const std::string& element_name() const;
- /*
- * Uses the current stack of namespaces to resolve the package. Eg:
- * xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
- * ...
- * android:text="@app:string/message"
- *
- * In this case, 'app' will be converted to 'com.android.app'.
- *
- * If xmlns:app="http://schemas.android.com/apk/res-auto", then
- * 'package' will be set to 'defaultPackage'.
- */
- Maybe<ExtractedPackage> transformPackageAlias(
- const StringPiece& alias, const StringPiece& localPackage) const override;
+ /*
+ * Uses the current stack of namespaces to resolve the package. Eg:
+ * xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
+ * ...
+ * android:text="@app:string/message"
+ *
+ * In this case, 'app' will be converted to 'com.android.app'.
+ *
+ * If xmlns:app="http://schemas.android.com/apk/res-auto", then
+ * 'package' will be set to 'defaultPackage'.
+ */
+ Maybe<ExtractedPackage> TransformPackageAlias(
+ const StringPiece& alias,
+ const StringPiece& local_package) const override;
- //
- // Remaining methods are for retrieving information about attributes
- // associated with a StartElement.
- //
- // Attributes must be in sorted order (according to the less than operator
- // of struct Attribute).
- //
+ //
+ // Remaining methods are for retrieving information about attributes
+ // associated with a StartElement.
+ //
+ // Attributes must be in sorted order (according to the less than operator
+ // of struct Attribute).
+ //
- struct Attribute {
- std::string namespaceUri;
- std::string name;
- std::string value;
+ struct Attribute {
+ std::string namespace_uri;
+ std::string name;
+ std::string value;
- int compare(const Attribute& rhs) const;
- bool operator<(const Attribute& rhs) const;
- bool operator==(const Attribute& rhs) const;
- bool operator!=(const Attribute& rhs) const;
- };
+ int compare(const Attribute& rhs) const;
+ bool operator<(const Attribute& rhs) const;
+ bool operator==(const Attribute& rhs) const;
+ bool operator!=(const Attribute& rhs) const;
+ };
- using const_iterator = std::vector<Attribute>::const_iterator;
+ using const_iterator = std::vector<Attribute>::const_iterator;
- const_iterator beginAttributes() const;
- const_iterator endAttributes() const;
- size_t getAttributeCount() const;
- const_iterator findAttribute(StringPiece namespaceUri, StringPiece name) const;
+ const_iterator begin_attributes() const;
+ const_iterator end_attributes() const;
+ size_t attribute_count() const;
+ const_iterator FindAttribute(StringPiece namespace_uri,
+ StringPiece name) const;
-private:
- static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri);
- static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs);
- static void XMLCALL characterDataHandler(void* userData, const char* s, int len);
- static void XMLCALL endElementHandler(void* userData, const char* name);
- static void XMLCALL endNamespaceHandler(void* userData, const char* prefix);
- static void XMLCALL commentDataHandler(void* userData, const char* comment);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XmlPullParser);
- struct EventData {
- Event event;
- size_t lineNumber;
- size_t depth;
- std::string data1;
- std::string data2;
- std::vector<Attribute> attributes;
- };
+ static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix,
+ const char* uri);
+ static void XMLCALL StartElementHandler(void* user_data, const char* name,
+ const char** attrs);
+ static void XMLCALL CharacterDataHandler(void* user_data, const char* s,
+ int len);
+ static void XMLCALL EndElementHandler(void* user_data, const char* name);
+ static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix);
+ static void XMLCALL CommentDataHandler(void* user_data, const char* comment);
- std::istream& mIn;
- XML_Parser mParser;
- char mBuffer[16384];
- std::queue<EventData> mEventQueue;
- std::string mLastError;
- const std::string mEmpty;
- size_t mDepth;
- std::stack<std::string> mNamespaceUris;
+ struct EventData {
+ Event event;
+ size_t line_number;
+ size_t depth;
+ std::string data1;
+ std::string data2;
+ std::vector<Attribute> attributes;
+ };
- struct PackageDecl {
- std::string prefix;
- ExtractedPackage package;
- };
- std::vector<PackageDecl> mPackageAliases;
+ std::istream& in_;
+ XML_Parser parser_;
+ char buffer_[16384];
+ std::queue<EventData> event_queue_;
+ std::string error_;
+ const std::string empty_;
+ size_t depth_;
+ std::stack<std::string> namespace_uris_;
+
+ struct PackageDecl {
+ std::string prefix;
+ ExtractedPackage package;
+ };
+ std::vector<PackageDecl> package_aliases_;
};
/**
* Finds the attribute in the current element within the global namespace.
*/
-Maybe<StringPiece> findAttribute(const XmlPullParser* parser, const StringPiece& name);
+Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
+ const StringPiece& name);
/**
- * Finds the attribute in the current element within the global namespace. The attribute's value
+ * Finds the attribute in the current element within the global namespace. The
+ * attribute's value
* must not be the empty string.
*/
-Maybe<StringPiece> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece& name);
+Maybe<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
+ const StringPiece& name);
//
// Implementation
//
-inline ::std::ostream& operator<<(::std::ostream& out, XmlPullParser::Event event) {
+inline ::std::ostream& operator<<(::std::ostream& out,
+ XmlPullParser::Event event) {
+ switch (event) {
+ case XmlPullParser::Event::kBadDocument:
+ return out << "BadDocument";
+ case XmlPullParser::Event::kStartDocument:
+ return out << "StartDocument";
+ case XmlPullParser::Event::kEndDocument:
+ return out << "EndDocument";
+ case XmlPullParser::Event::kStartNamespace:
+ return out << "StartNamespace";
+ case XmlPullParser::Event::kEndNamespace:
+ return out << "EndNamespace";
+ case XmlPullParser::Event::kStartElement:
+ return out << "StartElement";
+ case XmlPullParser::Event::kEndElement:
+ return out << "EndElement";
+ case XmlPullParser::Event::kText:
+ return out << "Text";
+ case XmlPullParser::Event::kComment:
+ return out << "Comment";
+ }
+ return out;
+}
+
+inline bool XmlPullParser::NextChildNode(XmlPullParser* parser,
+ size_t start_depth) {
+ Event event;
+
+ // First get back to the start depth.
+ while (IsGoodEvent(event = parser->Next()) &&
+ parser->depth() > start_depth + 1) {
+ }
+
+ // Now look for the first good node.
+ while ((event != Event::kEndElement || parser->depth() > start_depth) &&
+ IsGoodEvent(event)) {
switch (event) {
- case XmlPullParser::Event::kBadDocument: return out << "BadDocument";
- case XmlPullParser::Event::kStartDocument: return out << "StartDocument";
- case XmlPullParser::Event::kEndDocument: return out << "EndDocument";
- case XmlPullParser::Event::kStartNamespace: return out << "StartNamespace";
- case XmlPullParser::Event::kEndNamespace: return out << "EndNamespace";
- case XmlPullParser::Event::kStartElement: return out << "StartElement";
- case XmlPullParser::Event::kEndElement: return out << "EndElement";
- case XmlPullParser::Event::kText: return out << "Text";
- case XmlPullParser::Event::kComment: return out << "Comment";
+ case Event::kText:
+ case Event::kComment:
+ case Event::kStartElement:
+ return true;
+ default:
+ break;
}
- return out;
+ event = parser->Next();
+ }
+ return false;
}
-inline bool XmlPullParser::nextChildNode(XmlPullParser* parser, size_t startDepth) {
- Event event;
-
- // First get back to the start depth.
- while (isGoodEvent(event = parser->next()) && parser->getDepth() > startDepth + 1) {}
-
- // Now look for the first good node.
- while ((event != Event::kEndElement || parser->getDepth() > startDepth) && isGoodEvent(event)) {
- switch (event) {
- case Event::kText:
- case Event::kComment:
- case Event::kStartElement:
- return true;
- default:
- break;
- }
- event = parser->next();
+inline bool XmlPullParser::SkipCurrentElement(XmlPullParser* parser) {
+ int depth = 1;
+ while (depth > 0) {
+ switch (parser->Next()) {
+ case Event::kEndDocument:
+ return true;
+ case Event::kBadDocument:
+ return false;
+ case Event::kStartElement:
+ depth++;
+ break;
+ case Event::kEndElement:
+ depth--;
+ break;
+ default:
+ break;
}
- return false;
+ }
+ return true;
}
-inline bool XmlPullParser::skipCurrentElement(XmlPullParser* parser) {
- int depth = 1;
- while (depth > 0) {
- switch (parser->next()) {
- case Event::kEndDocument:
- return true;
- case Event::kBadDocument:
- return false;
- case Event::kStartElement:
- depth++;
- break;
- case Event::kEndElement:
- depth--;
- break;
- default:
- break;
- }
- }
- return true;
-}
-
-inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) {
- return event != Event::kBadDocument && event != Event::kEndDocument;
+inline bool XmlPullParser::IsGoodEvent(XmlPullParser::Event event) {
+ return event != Event::kBadDocument && event != Event::kEndDocument;
}
inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const {
- int cmp = namespaceUri.compare(rhs.namespaceUri);
- if (cmp != 0) return cmp;
- return name.compare(rhs.name);
+ int cmp = namespace_uri.compare(rhs.namespace_uri);
+ if (cmp != 0) return cmp;
+ return name.compare(rhs.name);
}
inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const {
- return compare(rhs) < 0;
+ return compare(rhs) < 0;
}
inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const {
- return compare(rhs) == 0;
+ return compare(rhs) == 0;
}
inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const {
- return compare(rhs) != 0;
+ return compare(rhs) != 0;
}
-inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece namespaceUri,
- StringPiece name) const {
- const auto endIter = endAttributes();
- const auto iter = std::lower_bound(beginAttributes(), endIter,
- std::pair<StringPiece, StringPiece>(namespaceUri, name),
- [](const Attribute& attr, const std::pair<StringPiece, StringPiece>& rhs) -> bool {
- int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.size(),
- rhs.first.data(), rhs.first.size());
- if (cmp < 0) return true;
- if (cmp > 0) return false;
- cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), rhs.second.size());
- if (cmp < 0) return true;
- return false;
- }
- );
+inline XmlPullParser::const_iterator XmlPullParser::FindAttribute(
+ StringPiece namespace_uri, StringPiece name) const {
+ const auto end_iter = end_attributes();
+ const auto iter = std::lower_bound(
+ begin_attributes(), end_iter,
+ std::pair<StringPiece, StringPiece>(namespace_uri, name),
+ [](const Attribute& attr,
+ const std::pair<StringPiece, StringPiece>& rhs) -> bool {
+ int cmp = attr.namespace_uri.compare(
+ 0, attr.namespace_uri.size(), rhs.first.data(), rhs.first.size());
+ if (cmp < 0) return true;
+ if (cmp > 0) return false;
+ cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(),
+ rhs.second.size());
+ if (cmp < 0) return true;
+ return false;
+ });
- if (iter != endIter && namespaceUri == iter->namespaceUri && name == iter->name) {
- return iter;
- }
- return endIter;
+ if (iter != end_iter && namespace_uri == iter->namespace_uri &&
+ name == iter->name) {
+ return iter;
+ }
+ return end_iter;
}
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
-#endif // AAPT_XML_PULL_PARSER_H
+#endif // AAPT_XML_PULL_PARSER_H
diff --git a/tools/aapt2/xml/XmlPullParser_test.cpp b/tools/aapt2/xml/XmlPullParser_test.cpp
index 2c1fdc7..4f18cd2 100644
--- a/tools/aapt2/xml/XmlPullParser_test.cpp
+++ b/tools/aapt2/xml/XmlPullParser_test.cpp
@@ -14,42 +14,43 @@
* limitations under the License.
*/
-#include "test/Test.h"
-#include "util/StringPiece.h"
#include "xml/XmlPullParser.h"
#include <sstream>
+#include "test/Test.h"
+#include "util/StringPiece.h"
+
namespace aapt {
TEST(XmlPullParserTest, NextChildNodeTraversesCorrectly) {
- std::stringstream str;
- str << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
- "<a><b><c xmlns:a=\"http://schema.org\"><d/></c><e/></b></a>";
- xml::XmlPullParser parser(str);
+ std::stringstream str;
+ str << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<a><b><c xmlns:a=\"http://schema.org\"><d/></c><e/></b></a>";
+ xml::XmlPullParser parser(str);
- const size_t depthOuter = parser.getDepth();
- ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthOuter));
+ const size_t depth_outer = parser.depth();
+ ASSERT_TRUE(xml::XmlPullParser::NextChildNode(&parser, depth_outer));
- EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
- EXPECT_EQ(StringPiece("a"), StringPiece(parser.getElementName()));
+ EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.event());
+ EXPECT_EQ(StringPiece("a"), StringPiece(parser.element_name()));
- const size_t depthA = parser.getDepth();
- ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthA));
- EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
- EXPECT_EQ(StringPiece("b"), StringPiece(parser.getElementName()));
+ const size_t depth_a = parser.depth();
+ ASSERT_TRUE(xml::XmlPullParser::NextChildNode(&parser, depth_a));
+ EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.event());
+ EXPECT_EQ(StringPiece("b"), StringPiece(parser.element_name()));
- const size_t depthB = parser.getDepth();
- ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB));
- EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
- EXPECT_EQ(StringPiece("c"), StringPiece(parser.getElementName()));
+ const size_t depth_b = parser.depth();
+ ASSERT_TRUE(xml::XmlPullParser::NextChildNode(&parser, depth_b));
+ EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.event());
+ EXPECT_EQ(StringPiece("c"), StringPiece(parser.element_name()));
- ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB));
- EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
- EXPECT_EQ(StringPiece("e"), StringPiece(parser.getElementName()));
+ ASSERT_TRUE(xml::XmlPullParser::NextChildNode(&parser, depth_b));
+ EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.event());
+ EXPECT_EQ(StringPiece("e"), StringPiece(parser.element_name()));
- ASSERT_FALSE(xml::XmlPullParser::nextChildNode(&parser, depthOuter));
- EXPECT_EQ(xml::XmlPullParser::Event::kEndDocument, parser.getEvent());
+ ASSERT_FALSE(xml::XmlPullParser::NextChildNode(&parser, depth_outer));
+ EXPECT_EQ(xml::XmlPullParser::Event::kEndDocument, parser.event());
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
index b570fd7..d00f7f2 100644
--- a/tools/aapt2/xml/XmlUtil.cpp
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -14,60 +14,69 @@
* limitations under the License.
*/
-#include "util/Maybe.h"
-#include "util/Util.h"
#include "xml/XmlUtil.h"
#include <string>
+#include "util/Maybe.h"
+#include "util/Util.h"
+
namespace aapt {
namespace xml {
-std::string buildPackageNamespace(const StringPiece& package, bool privateReference) {
- std::string result = privateReference ? kSchemaPrivatePrefix : kSchemaPublicPrefix;
- result.append(package.data(), package.size());
- return result;
+std::string BuildPackageNamespace(const StringPiece& package,
+ bool private_reference) {
+ std::string result =
+ private_reference ? kSchemaPrivatePrefix : kSchemaPublicPrefix;
+ result.append(package.data(), package.size());
+ return result;
}
-Maybe<ExtractedPackage> extractPackageFromNamespace(const std::string& namespaceUri) {
- if (util::stringStartsWith(namespaceUri, kSchemaPublicPrefix)) {
- StringPiece schemaPrefix = kSchemaPublicPrefix;
- StringPiece package = namespaceUri;
- package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size());
- if (package.empty()) {
- return {};
- }
- return ExtractedPackage{ package.toString(), false /* isPrivate */ };
-
- } else if (util::stringStartsWith(namespaceUri, kSchemaPrivatePrefix)) {
- StringPiece schemaPrefix = kSchemaPrivatePrefix;
- StringPiece package = namespaceUri;
- package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size());
- if (package.empty()) {
- return {};
- }
- return ExtractedPackage{ package.toString(), true /* isPrivate */ };
-
- } else if (namespaceUri == kSchemaAuto) {
- return ExtractedPackage{ std::string(), true /* isPrivate */ };
+Maybe<ExtractedPackage> ExtractPackageFromNamespace(
+ const std::string& namespace_uri) {
+ if (util::StartsWith(namespace_uri, kSchemaPublicPrefix)) {
+ StringPiece schema_prefix = kSchemaPublicPrefix;
+ StringPiece package = namespace_uri;
+ package = package.substr(schema_prefix.size(),
+ package.size() - schema_prefix.size());
+ if (package.empty()) {
+ return {};
}
- return {};
-}
+ return ExtractedPackage{package.ToString(), false /* is_private */};
-void transformReferenceFromNamespace(IPackageDeclStack* declStack,
- const StringPiece& localPackage, Reference* inRef) {
- if (inRef->name) {
- if (Maybe<ExtractedPackage> transformedPackage =
- declStack->transformPackageAlias(inRef->name.value().package, localPackage)) {
- ExtractedPackage& extractedPackage = transformedPackage.value();
- inRef->name.value().package = std::move(extractedPackage.package);
-
- // If the reference was already private (with a * prefix) and the namespace is public,
- // we keep the reference private.
- inRef->privateReference |= extractedPackage.privateNamespace;
- }
+ } else if (util::StartsWith(namespace_uri, kSchemaPrivatePrefix)) {
+ StringPiece schema_prefix = kSchemaPrivatePrefix;
+ StringPiece package = namespace_uri;
+ package = package.substr(schema_prefix.size(),
+ package.size() - schema_prefix.size());
+ if (package.empty()) {
+ return {};
}
+ return ExtractedPackage{package.ToString(), true /* is_private */};
+
+ } else if (namespace_uri == kSchemaAuto) {
+ return ExtractedPackage{std::string(), true /* is_private */};
+ }
+ return {};
}
-} // namespace xml
-} // namespace aapt
+void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
+ const StringPiece& local_package,
+ Reference* in_ref) {
+ if (in_ref->name) {
+ if (Maybe<ExtractedPackage> transformed_package =
+ decl_stack->TransformPackageAlias(in_ref->name.value().package,
+ local_package)) {
+ ExtractedPackage& extracted_package = transformed_package.value();
+ in_ref->name.value().package = std::move(extracted_package.package);
+
+ // If the reference was already private (with a * prefix) and the
+ // namespace is public,
+ // we keep the reference private.
+ in_ref->private_reference |= extracted_package.private_namespace;
+ }
+ }
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index a6ad79d..5365401 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -17,18 +17,21 @@
#ifndef AAPT_XML_XMLUTIL_H
#define AAPT_XML_XMLUTIL_H
+#include <string>
+
#include "ResourceValues.h"
#include "util/Maybe.h"
-#include <string>
-
namespace aapt {
namespace xml {
constexpr const char* kSchemaAuto = "http://schemas.android.com/apk/res-auto";
-constexpr const char* kSchemaPublicPrefix = "http://schemas.android.com/apk/res/";
-constexpr const char* kSchemaPrivatePrefix = "http://schemas.android.com/apk/prv/res/";
-constexpr const char* kSchemaAndroid = "http://schemas.android.com/apk/res/android";
+constexpr const char* kSchemaPublicPrefix =
+ "http://schemas.android.com/apk/res/";
+constexpr const char* kSchemaPrivatePrefix =
+ "http://schemas.android.com/apk/prv/res/";
+constexpr const char* kSchemaAndroid =
+ "http://schemas.android.com/apk/res/android";
constexpr const char* kSchemaTools = "http://schemas.android.com/tools";
constexpr const char* kSchemaAapt = "http://schemas.android.com/aapt";
@@ -36,17 +39,19 @@
* Result of extracting a package name from a namespace URI declaration.
*/
struct ExtractedPackage {
- /**
- * The name of the package. This can be the empty string, which means that the package
- * should be assumed to be the package being compiled.
- */
- std::string package;
+ /**
+ * The name of the package. This can be the empty string, which means that the
+ * package
+ * should be assumed to be the package being compiled.
+ */
+ std::string package;
- /**
- * True if the package's private namespace was declared. This means that private resources
- * are made visible.
- */
- bool privateNamespace;
+ /**
+ * True if the package's private namespace was declared. This means that
+ * private resources
+ * are made visible.
+ */
+ bool private_namespace;
};
/**
@@ -57,7 +62,8 @@
* Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
* returns an empty package name.
*/
-Maybe<ExtractedPackage> extractPackageFromNamespace(const std::string& namespaceUri);
+Maybe<ExtractedPackage> ExtractPackageFromNamespace(
+ const std::string& namespace_uri);
/**
* Returns an XML Android namespace for the given package of the form:
@@ -68,31 +74,36 @@
*
* http://schemas.android.com/apk/prv/res/<package>
*/
-std::string buildPackageNamespace(const StringPiece& package, bool privateReference=false);
+std::string BuildPackageNamespace(const StringPiece& package,
+ bool private_reference = false);
/**
- * Interface representing a stack of XML namespace declarations. When looking up the package
+ * Interface representing a stack of XML namespace declarations. When looking up
+ * the package
* for a namespace prefix, the stack is checked from top to bottom.
*/
struct IPackageDeclStack {
- virtual ~IPackageDeclStack() = default;
+ virtual ~IPackageDeclStack() = default;
- /**
- * Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
- */
- virtual Maybe<ExtractedPackage> transformPackageAlias(
- const StringPiece& alias, const StringPiece& localPackage) const = 0;
+ /**
+ * Returns an ExtractedPackage struct if the alias given corresponds with a
+ * package declaration.
+ */
+ virtual Maybe<ExtractedPackage> TransformPackageAlias(
+ const StringPiece& alias, const StringPiece& local_package) const = 0;
};
/**
- * Helper function for transforming the original Reference inRef to a fully qualified reference
- * via the IPackageDeclStack. This will also mark the Reference as private if the namespace of
- * the package declaration was private.
+ * Helper function for transforming the original Reference inRef to a fully
+ * qualified reference
+ * via the IPackageDeclStack. This will also mark the Reference as private if
+ * the namespace of the package declaration was private.
*/
-void transformReferenceFromNamespace(IPackageDeclStack* declStack,
- const StringPiece& localPackage, Reference* inRef);
+void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
+ const StringPiece& local_package,
+ Reference* in_ref);
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
#endif /* AAPT_XML_XMLUTIL_H */
diff --git a/tools/aapt2/xml/XmlUtil_test.cpp b/tools/aapt2/xml/XmlUtil_test.cpp
index cbeb8bc..5eecc8f 100644
--- a/tools/aapt2/xml/XmlUtil_test.cpp
+++ b/tools/aapt2/xml/XmlUtil_test.cpp
@@ -14,38 +14,46 @@
* limitations under the License.
*/
-#include "test/Test.h"
#include "xml/XmlUtil.h"
+#include "test/Test.h"
+
namespace aapt {
TEST(XmlUtilTest, ExtractPackageFromNamespace) {
- AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace("com.android"));
- AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace("http://schemas.android.com/apk"));
- AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace("http://schemas.android.com/apk/res"));
- AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace("http://schemas.android.com/apk/res/"));
- AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace("http://schemas.android.com/apk/prv/res/"));
+ AAPT_ASSERT_FALSE(xml::ExtractPackageFromNamespace("com.android"));
+ AAPT_ASSERT_FALSE(
+ xml::ExtractPackageFromNamespace("http://schemas.android.com/apk"));
+ AAPT_ASSERT_FALSE(
+ xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res"));
+ AAPT_ASSERT_FALSE(
+ xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res/"));
+ AAPT_ASSERT_FALSE(xml::ExtractPackageFromNamespace(
+ "http://schemas.android.com/apk/prv/res/"));
- Maybe<xml::ExtractedPackage> p =
- xml::extractPackageFromNamespace("http://schemas.android.com/apk/res/a");
- AAPT_ASSERT_TRUE(p);
- EXPECT_EQ(std::string("a"), p.value().package);
- EXPECT_FALSE(p.value().privateNamespace);
+ Maybe<xml::ExtractedPackage> p =
+ xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res/a");
+ AAPT_ASSERT_TRUE(p);
+ EXPECT_EQ(std::string("a"), p.value().package);
+ EXPECT_FALSE(p.value().private_namespace);
- p = xml::extractPackageFromNamespace("http://schemas.android.com/apk/prv/res/android");
- AAPT_ASSERT_TRUE(p);
- EXPECT_EQ(std::string("android"), p.value().package);
- EXPECT_TRUE(p.value().privateNamespace);
+ p = xml::ExtractPackageFromNamespace(
+ "http://schemas.android.com/apk/prv/res/android");
+ AAPT_ASSERT_TRUE(p);
+ EXPECT_EQ(std::string("android"), p.value().package);
+ EXPECT_TRUE(p.value().private_namespace);
- p = xml::extractPackageFromNamespace("http://schemas.android.com/apk/prv/res/com.test");
- AAPT_ASSERT_TRUE(p);
- EXPECT_EQ(std::string("com.test"), p.value().package);
- EXPECT_TRUE(p.value().privateNamespace);
+ p = xml::ExtractPackageFromNamespace(
+ "http://schemas.android.com/apk/prv/res/com.test");
+ AAPT_ASSERT_TRUE(p);
+ EXPECT_EQ(std::string("com.test"), p.value().package);
+ EXPECT_TRUE(p.value().private_namespace);
- p = xml::extractPackageFromNamespace("http://schemas.android.com/apk/res-auto");
- AAPT_ASSERT_TRUE(p);
- EXPECT_EQ(std::string(), p.value().package);
- EXPECT_TRUE(p.value().privateNamespace);
+ p = xml::ExtractPackageFromNamespace(
+ "http://schemas.android.com/apk/res-auto");
+ AAPT_ASSERT_TRUE(p);
+ EXPECT_EQ(std::string(), p.value().package);
+ EXPECT_TRUE(p.value().private_namespace);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/bit/Android.mk b/tools/bit/Android.mk
new file mode 100644
index 0000000..1c1291f
--- /dev/null
+++ b/tools/bit/Android.mk
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+
+# ==========================================================
+# Build the host executable: protoc-gen-javastream
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bit
+
+# This tool doesn't build on darwin.
+LOCAL_MODULE_HOST_OS := linux
+
+LOCAL_SRC_FILES := \
+ aapt.cpp \
+ adb.cpp \
+ command.cpp \
+ main.cpp \
+ make.cpp \
+ print.cpp \
+ util.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+ libexpat \
+ libinstrumentation \
+ libjsoncpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libprotobuf-cpp-full
+
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tools/bit/aapt.cpp b/tools/bit/aapt.cpp
new file mode 100644
index 0000000..961b47c
--- /dev/null
+++ b/tools/bit/aapt.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "aapt.h"
+
+#include "command.h"
+#include "print.h"
+#include "util.h"
+
+#include <regex>
+
+const regex NS_REGEX("( *)N: ([^=]+)=(.*)");
+const regex ELEMENT_REGEX("( *)E: ([^ ]+) \\(line=(\\d+)\\)");
+const regex ATTR_REGEX("( *)A: ([^\\(=]+)[^=]*=\"([^\"]+)\".*");
+
+const string ANDROID_NS("http://schemas.android.com/apk/res/android");
+
+bool
+Apk::HasActivity(const string& className)
+{
+ string fullClassName = full_class_name(package, className);
+ const size_t N = activities.size();
+ for (size_t i=0; i<N; i++) {
+ if (activities[i] == fullClassName) {
+ return true;
+ }
+ }
+ return false;
+}
+
+struct Attribute {
+ string ns;
+ string name;
+ string value;
+};
+
+struct Element {
+ Element* parent;
+ string ns;
+ string name;
+ int lineno;
+ vector<Attribute> attributes;
+ vector<Element*> children;
+
+ /**
+ * Indentation in the xmltree dump. Might not be equal to the distance
+ * from the root because namespace rows (scopes) have their own indentation.
+ */
+ int depth;
+
+ Element();
+ ~Element();
+
+ string GetAttr(const string& ns, const string& name) const;
+ void FindElements(const string& ns, const string& name, vector<Element*>* result, bool recurse);
+
+};
+
+Element::Element()
+{
+}
+
+Element::~Element()
+{
+ const size_t N = children.size();
+ for (size_t i=0; i<N; i++) {
+ delete children[i];
+ }
+}
+
+string
+Element::GetAttr(const string& ns, const string& name) const
+{
+ const size_t N = attributes.size();
+ for (size_t i=0; i<N; i++) {
+ const Attribute& attr = attributes[i];
+ if (attr.ns == ns && attr.name == name) {
+ return attr.value;
+ }
+ }
+ return string();
+}
+
+void
+Element::FindElements(const string& ns, const string& name, vector<Element*>* result, bool recurse)
+{
+ const size_t N = children.size();
+ for (size_t i=0; i<N; i++) {
+ Element* child = children[i];
+ if (child->ns == ns && child->name == name) {
+ result->push_back(child);
+ }
+ if (recurse) {
+ child->FindElements(ns, name, result, recurse);
+ }
+ }
+}
+
+struct Scope {
+ Scope* parent;
+ int depth;
+ map<string,string> namespaces;
+
+ Scope(Scope* parent, int depth);
+};
+
+Scope::Scope(Scope* p, int d)
+ :parent(p),
+ depth(d)
+{
+ if (p != NULL) {
+ namespaces = p->namespaces;
+ }
+}
+
+
+string
+full_class_name(const string& packageName, const string& className)
+{
+ if (className.length() == 0) {
+ return "";
+ }
+ if (className[0] == '.') {
+ return packageName + className;
+ }
+ if (className.find('.') == string::npos) {
+ return packageName + "." + className;
+ }
+ return className;
+}
+
+string
+pretty_component_name(const string& packageName, const string& className)
+{
+ if (starts_with(packageName, className)) {
+ size_t pn = packageName.length();
+ size_t cn = className.length();
+ if (cn > pn && className[pn] == '.') {
+ return packageName + "/" + string(className, pn, string::npos);
+ }
+ }
+ return packageName + "/" + className;
+}
+
+int
+inspect_apk(Apk* apk, const string& filename)
+{
+ // Load the manifest xml
+ Command cmd("aapt");
+ cmd.AddArg("dump");
+ cmd.AddArg("xmltree");
+ cmd.AddArg(filename);
+ cmd.AddArg("AndroidManifest.xml");
+
+ int err;
+
+ string output = get_command_output(cmd, &err, false);
+ check_error(err);
+
+ // Parse the manifest xml
+ Scope* scope = new Scope(NULL, -1);
+ Element* root = NULL;
+ Element* current = NULL;
+ vector<string> lines;
+ split_lines(&lines, output);
+ for (size_t i=0; i<lines.size(); i++) {
+ const string& line = lines[i];
+ smatch match;
+ if (regex_match(line, match, NS_REGEX)) {
+ int depth = match[1].length() / 2;
+ while (depth < scope->depth) {
+ Scope* tmp = scope;
+ scope = scope->parent;
+ delete tmp;
+ }
+ scope = new Scope(scope, depth);
+ scope->namespaces[match[2]] = match[3];
+ } else if (regex_match(line, match, ELEMENT_REGEX)) {
+ Element* element = new Element();
+
+ string str = match[2];
+ size_t colon = str.find(':');
+ if (colon == string::npos) {
+ element->name = str;
+ } else {
+ element->ns = scope->namespaces[string(str, 0, colon)];
+ element->name.assign(str, colon+1, string::npos);
+ }
+ element->lineno = atoi(match[3].str().c_str());
+ element->depth = match[1].length() / 2;
+
+ if (root == NULL) {
+ current = element;
+ root = element;
+ } else {
+ while (element->depth <= current->depth && current->parent != NULL) {
+ current = current->parent;
+ }
+ element->parent = current;
+ current->children.push_back(element);
+ current = element;
+ }
+ } else if (regex_match(line, match, ATTR_REGEX)) {
+ if (current != NULL) {
+ Attribute attr;
+ string str = match[2];
+ size_t colon = str.find(':');
+ if (colon == string::npos) {
+ attr.name = str;
+ } else {
+ attr.ns = scope->namespaces[string(str, 0, colon)];
+ attr.name.assign(str, colon+1, string::npos);
+ }
+ attr.value = match[3];
+ current->attributes.push_back(attr);
+ }
+ }
+ }
+ while (scope != NULL) {
+ Scope* tmp = scope;
+ scope = scope->parent;
+ delete tmp;
+ }
+
+ // Package name
+ apk->package = root->GetAttr("", "package");
+ if (apk->package.size() == 0) {
+ print_error("%s:%d: Manifest root element doesn't contain a package attribute",
+ filename.c_str(), root->lineno);
+ delete root;
+ return 1;
+ }
+
+ // Instrumentation runner
+ vector<Element*> instrumentation;
+ root->FindElements("", "instrumentation", &instrumentation, true);
+ if (instrumentation.size() > 0) {
+ // TODO: How could we deal with multiple instrumentation tags?
+ // We'll just pick the first one.
+ apk->runner = instrumentation[0]->GetAttr(ANDROID_NS, "name");
+ }
+
+ // Activities
+ vector<Element*> activities;
+ root->FindElements("", "activity", &activities, true);
+ for (size_t i=0; i<activities.size(); i++) {
+ string name = activities[i]->GetAttr(ANDROID_NS, "name");
+ if (name.size() == 0) {
+ continue;
+ }
+ apk->activities.push_back(full_class_name(apk->package, name));
+ }
+
+ delete root;
+ return 0;
+}
+
diff --git a/tools/bit/aapt.h b/tools/bit/aapt.h
new file mode 100644
index 0000000..6aeb03f
--- /dev/null
+++ b/tools/bit/aapt.h
@@ -0,0 +1,39 @@
+/*
+ * 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 AAPT_H
+#define AAPT_H
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+struct Apk
+{
+ string package;
+ string runner;
+ vector<string> activities;
+
+ bool HasActivity(const string& className);
+};
+
+string full_class_name(const string& packageName, const string& className);
+string pretty_component_name(const string& packageName, const string& className);
+
+int inspect_apk(Apk* apk, const string& filename);
+
+#endif // AAPT_H
diff --git a/tools/bit/adb.cpp b/tools/bit/adb.cpp
new file mode 100644
index 0000000..0c8424d
--- /dev/null
+++ b/tools/bit/adb.cpp
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb.h"
+
+#include "command.h"
+#include "print.h"
+#include "util.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <limits.h>
+
+#include <iostream>
+#include <istream>
+#include <streambuf>
+
+using namespace std;
+
+struct Buffer: public streambuf
+{
+ Buffer(char* begin, size_t size);
+};
+
+Buffer::Buffer(char* begin, size_t size)
+{
+ this->setg(begin, begin, begin + size);
+}
+
+int
+run_adb(const char* first, ...)
+{
+ Command cmd("adb");
+
+ if (first == NULL) {
+ return 0;
+ }
+
+ cmd.AddArg(first);
+
+ va_list args;
+ va_start(args, first);
+ while (true) {
+ const char* arg = va_arg(args, char*);
+ if (arg == NULL) {
+ break;
+ }
+ cmd.AddArg(arg);
+ }
+ va_end(args);
+
+ return run_command(cmd);
+}
+
+string
+get_system_property(const string& name, int* err)
+{
+ Command cmd("adb");
+ cmd.AddArg("shell");
+ cmd.AddArg("getprop");
+ cmd.AddArg(name);
+
+ return trim(get_command_output(cmd, err, false));
+}
+
+
+static uint64_t
+read_varint(int fd, int* err, bool* done)
+{
+ uint32_t bits = 0;
+ uint64_t result = 0;
+ while (true) {
+ uint8_t byte;
+ ssize_t amt = read(fd, &byte, 1);
+ if (amt == 0) {
+ *done = true;
+ return result;
+ } else if (amt < 0) {
+ return *err = errno;
+ }
+ result |= uint64_t(byte & 0x7F) << bits;
+ if ((byte & 0x80) == 0) {
+ return result;
+ }
+ bits += 7;
+ if (bits > 64) {
+ *err = -1;
+ return 0;
+ }
+ }
+}
+
+static char*
+read_sized_buffer(int fd, int* err, size_t* resultSize)
+{
+ bool done = false;
+ uint64_t size = read_varint(fd, err, &done);
+ if (*err != 0 || done) {
+ return NULL;
+ }
+ if (size == 0) {
+ *resultSize = 0;
+ return NULL;
+ }
+ // 10 MB seems like a reasonable limit.
+ if (size > 10*1024*1024) {
+ print_error("result buffer too large: %llu", size);
+ return NULL;
+ }
+ char* buf = (char*)malloc(size);
+ if (buf == NULL) {
+ print_error("Can't allocate a buffer of size for test results: %llu", size);
+ return NULL;
+ }
+ int pos = 0;
+ while (size - pos > 0) {
+ ssize_t amt = read(fd, buf+pos, size-pos);
+ if (amt == 0) {
+ // early end of pipe
+ print_error("Early end of pipe.");
+ *err = -1;
+ free(buf);
+ return NULL;
+ } else if (amt < 0) {
+ // error
+ *err = errno;
+ free(buf);
+ return NULL;
+ }
+ pos += amt;
+ }
+ *resultSize = (size_t)size;
+ return buf;
+}
+
+static int
+read_sized_proto(int fd, Message* message)
+{
+ int err = 0;
+ size_t size;
+ char* buf = read_sized_buffer(fd, &err, &size);
+ if (err != 0) {
+ if (buf != NULL) {
+ free(buf);
+ }
+ return err;
+ } else if (size == 0) {
+ if (buf != NULL) {
+ free(buf);
+ }
+ return 0;
+ } else if (buf == NULL) {
+ return -1;
+ }
+ Buffer buffer(buf, size);
+ istream in(&buffer);
+
+ err = message->ParseFromIstream(&in) ? 0 : -1;
+
+ free(buf);
+ return err;
+}
+
+static int
+skip_bytes(int fd, ssize_t size, char* scratch, int scratchSize)
+{
+ while (size > 0) {
+ ssize_t amt = size < scratchSize ? size : scratchSize;
+ fprintf(stderr, "skipping %lu/%ld bytes\n", size, amt);
+ amt = read(fd, scratch, amt);
+ if (amt == 0) {
+ // early end of pipe
+ print_error("Early end of pipe.");
+ return -1;
+ } else if (amt < 0) {
+ // error
+ return errno;
+ }
+ size -= amt;
+ }
+ return 0;
+}
+
+static int
+skip_unknown_field(int fd, uint64_t tag, char* scratch, int scratchSize) {
+ bool done;
+ int err;
+ uint64_t size;
+ switch (tag & 0x7) {
+ case 0: // varint
+ read_varint(fd, &err, &done);
+ if (err != 0) {
+ return err;
+ } else if (done) {
+ return -1;
+ } else {
+ return 0;
+ }
+ case 1:
+ return skip_bytes(fd, 8, scratch, scratchSize);
+ case 2:
+ size = read_varint(fd, &err, &done);
+ if (err != 0) {
+ return err;
+ } else if (done) {
+ return -1;
+ }
+ if (size > INT_MAX) {
+ // we'll be here a long time but this keeps it from overflowing
+ return -1;
+ }
+ return skip_bytes(fd, (ssize_t)size, scratch, scratchSize);
+ case 5:
+ return skip_bytes(fd, 4, scratch, scratchSize);
+ default:
+ print_error("bad wire type for tag 0x%lx\n", tag);
+ return -1;
+ }
+}
+
+static int
+read_instrumentation_results(int fd, char* scratch, int scratchSize,
+ InstrumentationCallbacks* callbacks)
+{
+ bool done = false;
+ int err = 0;
+ string result;
+ while (true) {
+ uint64_t tag = read_varint(fd, &err, &done);
+ if (done) {
+ // Done reading input (this is the only place that a stream end isn't an error).
+ return 0;
+ } else if (err != 0) {
+ return err;
+ } else if (tag == 0xa) { // test_status
+ TestStatus status;
+ err = read_sized_proto(fd, &status);
+ if (err != 0) {
+ return err;
+ }
+ callbacks->OnTestStatus(status);
+ } else if (tag == 0x12) { // session_status
+ SessionStatus status;
+ err = read_sized_proto(fd, &status);
+ if (err != 0) {
+ return err;
+ }
+ callbacks->OnSessionStatus(status);
+ } else {
+ err = skip_unknown_field(fd, tag, scratch, scratchSize);
+ if (err != 0) {
+ return err;
+ }
+ }
+ }
+ return 0;
+}
+
+int
+run_instrumentation_test(const string& packageName, const string& runner, const string& className,
+ InstrumentationCallbacks* callbacks)
+{
+ Command cmd("adb");
+ cmd.AddArg("shell");
+ cmd.AddArg("am");
+ cmd.AddArg("instrument");
+ cmd.AddArg("-w");
+ cmd.AddArg("-m");
+ if (className.length() > 0) {
+ cmd.AddArg("-e");
+ cmd.AddArg("class");
+ cmd.AddArg(className);
+ }
+ cmd.AddArg(packageName + "/" + runner);
+
+ print_command(cmd);
+
+ int fds[2];
+ pipe(fds);
+
+ pid_t pid = fork();
+
+ if (pid == -1) {
+ // fork error
+ return errno;
+ } else if (pid == 0) {
+ // child
+ while ((dup2(fds[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
+ close(fds[1]);
+ close(fds[0]);
+ const char* prog = cmd.GetProg();
+ char* const* argv = cmd.GetArgv();
+ char* const* env = cmd.GetEnv();
+ exec_with_path_search(prog, argv, env);
+ print_error("Unable to run command: %s", prog);
+ exit(1);
+ } else {
+ // parent
+ close(fds[1]);
+ string result;
+ const int size = 16*1024;
+ char* buf = (char*)malloc(size);
+ int err = read_instrumentation_results(fds[0], buf, size, callbacks);
+ free(buf);
+ int status;
+ waitpid(pid, &status, 0);
+ if (err != 0) {
+ return err;
+ }
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ } else {
+ return -1;
+ }
+ }
+}
+
+/**
+ * Get the second to last bundle in the args list. Stores the last name found
+ * in last. If the path is not found or if the args list is empty, returns NULL.
+ */
+static const ResultsBundleEntry *
+find_penultimate_entry(const ResultsBundle& bundle, va_list args)
+{
+ const ResultsBundle* b = &bundle;
+ const char* arg = va_arg(args, char*);
+ while (arg) {
+ string last = arg;
+ arg = va_arg(args, char*);
+ bool found = false;
+ for (int i=0; i<b->entries_size(); i++) {
+ const ResultsBundleEntry& e = b->entries(i);
+ if (e.key() == last) {
+ if (arg == NULL) {
+ return &e;
+ } else if (e.has_value_bundle()) {
+ b = &e.value_bundle();
+ found = true;
+ }
+ }
+ }
+ if (!found) {
+ return NULL;
+ }
+ if (arg == NULL) {
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+string
+get_bundle_string(const ResultsBundle& bundle, bool* found, ...)
+{
+ va_list args;
+ va_start(args, found);
+ const ResultsBundleEntry* entry = find_penultimate_entry(bundle, args);
+ va_end(args);
+ if (entry == NULL) {
+ *found = false;
+ return string();
+ }
+ if (entry->has_value_string()) {
+ *found = true;
+ return entry->value_string();
+ }
+ *found = false;
+ return string();
+}
+
+int32_t
+get_bundle_int(const ResultsBundle& bundle, bool* found, ...)
+{
+ va_list args;
+ va_start(args, found);
+ const ResultsBundleEntry* entry = find_penultimate_entry(bundle, args);
+ va_end(args);
+ if (entry == NULL) {
+ *found = false;
+ return 0;
+ }
+ if (entry->has_value_int()) {
+ *found = true;
+ return entry->value_int();
+ }
+ *found = false;
+ return 0;
+}
+
+float
+get_bundle_float(const ResultsBundle& bundle, bool* found, ...)
+{
+ va_list args;
+ va_start(args, found);
+ const ResultsBundleEntry* entry = find_penultimate_entry(bundle, args);
+ va_end(args);
+ if (entry == NULL) {
+ *found = false;
+ return 0;
+ }
+ if (entry->has_value_float()) {
+ *found = true;
+ return entry->value_float();
+ }
+ *found = false;
+ return 0;
+}
+
+double
+get_bundle_double(const ResultsBundle& bundle, bool* found, ...)
+{
+ va_list args;
+ va_start(args, found);
+ const ResultsBundleEntry* entry = find_penultimate_entry(bundle, args);
+ va_end(args);
+ if (entry == NULL) {
+ *found = false;
+ return 0;
+ }
+ if (entry->has_value_double()) {
+ *found = true;
+ return entry->value_double();
+ }
+ *found = false;
+ return 0;
+}
+
+int64_t
+get_bundle_long(const ResultsBundle& bundle, bool* found, ...)
+{
+ va_list args;
+ va_start(args, found);
+ const ResultsBundleEntry* entry = find_penultimate_entry(bundle, args);
+ va_end(args);
+ if (entry == NULL) {
+ *found = false;
+ return 0;
+ }
+ if (entry->has_value_long()) {
+ *found = true;
+ return entry->value_long();
+ }
+ *found = false;
+ return 0;
+}
+
diff --git a/tools/bit/adb.h b/tools/bit/adb.h
new file mode 100644
index 0000000..dca80c8
--- /dev/null
+++ b/tools/bit/adb.h
@@ -0,0 +1,47 @@
+/*
+ * 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 ADB_H
+#define ADB_H
+
+#include "instrumentation_data.pb.h"
+
+#include <string>
+
+using namespace android::am;
+using namespace google::protobuf;
+using namespace std;
+
+class InstrumentationCallbacks {
+public:
+ virtual void OnTestStatus(TestStatus& status) = 0;
+ virtual void OnSessionStatus(SessionStatus& status) = 0;
+};
+
+int run_adb(const char* first, ...);
+
+string get_system_property(const string& name, int* err);
+
+int run_instrumentation_test(const string& packageName, const string& runner,
+ const string& className, InstrumentationCallbacks* callbacks);
+
+string get_bundle_string(const ResultsBundle& bundle, bool* found, ...);
+int32_t get_bundle_int(const ResultsBundle& bundle, bool* found, ...);
+float get_bundle_float(const ResultsBundle& bundle, bool* found, ...);
+double get_bundle_double(const ResultsBundle& bundle, bool* found, ...);
+int64_t get_bundle_long(const ResultsBundle& bundle, bool* found, ...);
+
+#endif // ADB_H
diff --git a/tools/bit/command.cpp b/tools/bit/command.cpp
new file mode 100644
index 0000000..9a8449b
--- /dev/null
+++ b/tools/bit/command.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "command.h"
+
+#include "print.h"
+#include "util.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+extern char **environ;
+
+Command::Command(const string& prog)
+ :prog(prog)
+{
+}
+
+Command::~Command()
+{
+}
+
+void
+Command::AddArg(const string& arg)
+{
+ args.push_back(arg);
+}
+
+void
+Command::AddEnv(const string& name, const string& value)
+{
+ env[name] = value;
+}
+
+const char*
+Command::GetProg() const
+{
+ return prog.c_str();
+}
+
+char *const *
+Command::GetArgv() const
+{
+ const int N = args.size();
+ char** result = (char**)malloc(sizeof(char*)*(N+2));
+ result[0] = strdup(prog.c_str());
+ for (int i=0; i<N; i++) {
+ result[i+1] = strdup(args[i].c_str());
+ }
+ result[N+1] = 0;
+ return result;
+}
+
+char *const *
+Command::GetEnv() const
+{
+ map<string,string> copy;
+ for (const char** p=(const char**)environ; *p != NULL; p++) {
+ char* name = strdup(*p);
+ char* value = strchr(name, '=');
+ *value = '\0';
+ value++;
+ copy[name] = value;
+ free(name);
+ }
+ for (map<string,string>::const_iterator it=env.begin(); it!=env.end(); it++) {
+ copy[it->first] = it->second;
+ }
+ char** result = (char**)malloc(sizeof(char*)*(copy.size()+1));
+ char** row = result;
+ for (map<string,string>::const_iterator it=copy.begin(); it!=copy.end(); it++) {
+ *row = (char*)malloc(it->first.size() + it->second.size() + 2);
+ strcpy(*row, it->first.c_str());
+ strcat(*row, "=");
+ strcat(*row, it->second.c_str());
+ row++;
+ }
+ *row = NULL;
+ return result;
+}
+
+string
+get_command_output(const Command& command, int* err, bool quiet)
+{
+ if (!quiet) {
+ print_command(command);
+ }
+
+ int fds[2];
+ pipe(fds);
+
+ pid_t pid = fork();
+
+ if (pid == -1) {
+ // fork error
+ *err = errno;
+ return string();
+ } else if (pid == 0) {
+ // child
+ while ((dup2(fds[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
+ close(fds[1]);
+ close(fds[0]);
+ const char* prog = command.GetProg();
+ char* const* argv = command.GetArgv();
+ char* const* env = command.GetEnv();
+ exec_with_path_search(prog, argv, env);
+ if (!quiet) {
+ print_error("Unable to run command: %s", prog);
+ }
+ exit(1);
+ } else {
+ // parent
+ close(fds[1]);
+ string result;
+ const int size = 16*1024;
+ char* buf = (char*)malloc(size);
+ while (true) {
+ ssize_t amt = read(fds[0], buf, size);
+ if (amt <= 0) {
+ break;
+ } else if (amt > 0) {
+ result.append(buf, amt);
+ }
+ }
+ free(buf);
+ int status;
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status)) {
+ *err = WEXITSTATUS(status);
+ return result;
+ } else {
+ *err = -1;
+ return string();
+ }
+ }
+}
+
+
+int
+run_command(const Command& command)
+{
+ print_command(command);
+
+ pid_t pid = fork();
+
+ if (pid == -1) {
+ // fork error
+ return errno;
+ } else if (pid == 0) {
+ // child
+ const char* prog = command.GetProg();
+ char* const* argv = command.GetArgv();
+ char* const* env = command.GetEnv();
+ exec_with_path_search(prog, argv, env);
+ print_error("Unable to run command: %s", prog);
+ exit(1);
+ } else {
+ // parent
+ int status;
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ } else {
+ return -1;
+ }
+ }
+}
+
+int
+exec_with_path_search(const char* prog, char const* const* argv, char const* const* envp)
+{
+ if (prog[0] == '/') {
+ return execve(prog, (char*const*)argv, (char*const*)envp);
+ } else {
+ char* pathEnv = strdup(getenv("PATH"));
+ if (pathEnv == NULL) {
+ return 1;
+ }
+ char* dir = pathEnv;
+ while (dir) {
+ char* next = strchr(dir, ':');
+ if (next != NULL) {
+ *next = '\0';
+ next++;
+ }
+ if (dir[0] == '/') {
+ struct stat st;
+ string executable = string(dir) + "/" + prog;
+ if (stat(executable.c_str(), &st) == 0) {
+ execve(executable.c_str(), (char*const*)argv, (char*const*)envp);
+ }
+ }
+ dir = next;
+ }
+ free(pathEnv);
+ return 1;
+ }
+}
+
diff --git a/tools/bit/command.h b/tools/bit/command.h
new file mode 100644
index 0000000..fb44900
--- /dev/null
+++ b/tools/bit/command.h
@@ -0,0 +1,61 @@
+/*
+ * 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 COMMAND_H
+#define COMMAND_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+struct Command
+{
+ Command(const string& prog);
+ ~Command();
+
+ void AddArg(const string& arg);
+ void AddEnv(const string& name, const string& value);
+
+ const char* GetProg() const;
+ char* const* GetArgv() const;
+ char* const* GetEnv() const;
+
+ string GetCommandline() const;
+
+ string prog;
+ vector<string> args;
+ map<string,string> env;
+};
+
+/**
+ * Run the command and collect stdout.
+ * Returns the exit code.
+ */
+string get_command_output(const Command& command, int* err, bool quiet=false);
+
+/**
+ * Run the command.
+ * Returns the exit code.
+ */
+int run_command(const Command& command);
+
+// Mac OS doesn't have execvpe. This is the same as execvpe.
+int exec_with_path_search(const char* prog, char const* const* argv, char const* const* envp);
+
+#endif // COMMAND_H
+
diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp
new file mode 100644
index 0000000..4974a44
--- /dev/null
+++ b/tools/bit/main.cpp
@@ -0,0 +1,984 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "aapt.h"
+#include "adb.h"
+#include "make.h"
+#include "print.h"
+#include "util.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <google/protobuf/stubs/common.h>
+
+using namespace std;
+
+/**
+ * An entry from the command line for something that will be built, installed,
+ * and/or tested.
+ */
+struct Target {
+ bool build;
+ bool install;
+ bool test;
+ string pattern;
+ string name;
+ vector<string> actions;
+ Module module;
+
+ int testActionCount;
+
+ int testPassCount;
+ int testFailCount;
+ bool actionsWithNoTests;
+
+ Target(bool b, bool i, bool t, const string& p);
+};
+
+Target::Target(bool b, bool i, bool t, const string& p)
+ :build(b),
+ install(i),
+ test(t),
+ pattern(p),
+ testActionCount(0),
+ testPassCount(0),
+ testFailCount(0),
+ actionsWithNoTests(false)
+{
+}
+
+/**
+ * Command line options.
+ */
+struct Options {
+ // For help
+ bool runHelp;
+
+ // For tab completion
+ bool runTab;
+ string tabPattern;
+
+ // For build/install/test
+ bool reboot;
+ vector<Target*> targets;
+
+ Options();
+ ~Options();
+};
+
+Options::Options()
+ :runHelp(false),
+ runTab(false),
+ reboot(false),
+ targets()
+{
+}
+
+Options::~Options()
+{
+}
+
+struct InstallApk
+{
+ TrackedFile file;
+ bool alwaysInstall;
+ bool installed;
+
+ InstallApk();
+ InstallApk(const InstallApk& that);
+ InstallApk(const string& filename, bool always);
+ ~InstallApk() {};
+};
+
+InstallApk::InstallApk()
+{
+}
+
+InstallApk::InstallApk(const InstallApk& that)
+ :file(that.file),
+ alwaysInstall(that.alwaysInstall),
+ installed(that.installed)
+{
+}
+
+InstallApk::InstallApk(const string& filename, bool always)
+ :file(filename),
+ alwaysInstall(always),
+ installed(false)
+{
+}
+
+
+/**
+ * Record for an test that is going to be launched.
+ */
+struct TestAction {
+ TestAction();
+
+ // The package name from the apk
+ string packageName;
+
+ // The test runner class
+ string runner;
+
+ // The test class, or none if all tests should be run
+ string className;
+
+ // The original target that requested this action
+ Target* target;
+
+ // The number of tests that passed
+ int passCount;
+
+ // The number of tests that failed
+ int failCount;
+};
+
+TestAction::TestAction()
+ :passCount(0),
+ failCount(0)
+{
+}
+
+/**
+ * Record for an activity that is going to be launched.
+ */
+struct ActivityAction {
+ // The package name from the apk
+ string packageName;
+
+ // The test class, or none if all tests should be run
+ string className;
+};
+
+/**
+ * Callback class for the am instrument command.
+ */
+class TestResults: public InstrumentationCallbacks
+{
+public:
+ virtual void OnTestStatus(TestStatus& status);
+ virtual void OnSessionStatus(SessionStatus& status);
+
+ /**
+ * Set the TestAction that the tests are for.
+ * It will be updated with statistics as the tests run.
+ */
+ void SetCurrentAction(TestAction* action);
+
+private:
+ TestAction* m_currentAction;
+};
+
+void
+TestResults::OnTestStatus(TestStatus& status)
+{
+ bool found;
+// printf("OnTestStatus\n");
+// status.PrintDebugString();
+ int32_t resultCode = status.has_results() ? status.result_code() : 0;
+
+ if (!status.has_results()) {
+ return;
+ }
+ const ResultsBundle &results = status.results();
+
+ int32_t currentTestNum = get_bundle_int(results, &found, "current", NULL);
+ if (!found) {
+ currentTestNum = -1;
+ }
+
+ int32_t testCount = get_bundle_int(results, &found, "numtests", NULL);
+ if (!found) {
+ testCount = -1;
+ }
+
+ string className = get_bundle_string(results, &found, "class", NULL);
+ if (!found) {
+ return;
+ }
+
+ string testName = get_bundle_string(results, &found, "test", NULL);
+ if (!found) {
+ return;
+ }
+
+ if (resultCode == 0) {
+ // test passed
+ m_currentAction->passCount++;
+ m_currentAction->target->testPassCount++;
+ } else if (resultCode == 1) {
+ // test starting
+ ostringstream line;
+ line << "Running";
+ if (currentTestNum > 0) {
+ line << ": " << currentTestNum;
+ if (testCount > 0) {
+ line << " of " << testCount;
+ }
+ }
+ line << ": " << m_currentAction->target->name << ':' << className << "\\#" << testName;
+ print_one_line("%s", line.str().c_str());
+ } else if (resultCode == -2) {
+ // test failed
+ m_currentAction->failCount++;
+ m_currentAction->target->testFailCount++;
+ printf("%s\n%sFailed: %s:%s\\#%s%s\n", g_escapeClearLine, g_escapeRedBold,
+ m_currentAction->target->name.c_str(), className.c_str(),
+ testName.c_str(), g_escapeEndColor);
+
+ string stack = get_bundle_string(results, &found, "stack", NULL);
+ if (found) {
+ printf("%s\n", stack.c_str());
+ }
+ }
+}
+
+void
+TestResults::OnSessionStatus(SessionStatus& /*status*/)
+{
+ //status.PrintDebugString();
+}
+
+void
+TestResults::SetCurrentAction(TestAction* action)
+{
+ m_currentAction = action;
+}
+
+/**
+ * Prints the usage statement / help text.
+ */
+static void
+print_usage(FILE* out) {
+ fprintf(out, "usage: bit OPTIONS PATTERN\n");
+ fprintf(out, "\n");
+ fprintf(out, " Build, sync and test android code.\n");
+ fprintf(out, "\n");
+ fprintf(out, " The -b -i and -t options allow you to specify which phases\n");
+ fprintf(out, " you want to run. If none of those options are given, then\n");
+ fprintf(out, " all phases are run. If any of these options are provided\n");
+ fprintf(out, " then only the listed phases are run.\n");
+ fprintf(out, "\n");
+ fprintf(out, " OPTIONS\n");
+ fprintf(out, " -b Run a build\n");
+ fprintf(out, " -i Install the targets\n");
+ fprintf(out, " -t Run the tests\n");
+ fprintf(out, "\n");
+ fprintf(out, " -r If the runtime needs to be restarted, do a full reboot\n");
+ fprintf(out, " instead\n");
+ fprintf(out, "\n");
+ fprintf(out, " PATTERN\n");
+ fprintf(out, " One or more targets to build, install and test. The target\n");
+ fprintf(out, " names are the names that appear in the LOCAL_MODULE or\n");
+ fprintf(out, " LOCAL_PACKAGE_NAME variables in Android.mk or Android.bp files.\n");
+ fprintf(out, "\n");
+ fprintf(out, " Building and installing\n");
+ fprintf(out, " -----------------------\n");
+ fprintf(out, " The modules specified will be built and then installed. If the\n");
+ fprintf(out, " files are on the system partition, they will be synced and the\n");
+ fprintf(out, " attached device rebooted. If they are APKs that aren't on the\n");
+ fprintf(out, " system partition they are installed with adb install.\n");
+ fprintf(out, "\n");
+ fprintf(out, " For example:\n");
+ fprintf(out, " bit framework\n");
+ fprintf(out, " Builds framework.jar, syncs the system partition and reboots.\n");
+ fprintf(out, "\n");
+ fprintf(out, " bit SystemUI\n");
+ fprintf(out, " Builds SystemUI.apk, syncs the system partition and reboots.\n");
+ fprintf(out, "\n");
+ fprintf(out, " bit CtsProtoTestCases\n");
+ fprintf(out, " Builds this CTS apk, adb installs it, but does not run any\n");
+ fprintf(out, " tests.\n");
+ fprintf(out, "\n");
+ fprintf(out, " Running Unit Tests\n");
+ fprintf(out, " ------------------\n");
+ fprintf(out, " To run a unit test, list the test class names and optionally the\n");
+ fprintf(out, " test method after the module.\n");
+ fprintf(out, "\n");
+ fprintf(out, " For example:\n");
+ fprintf(out, " bit CtsProtoTestCases:*\n");
+ fprintf(out, " Builds this CTS apk, adb installs it, and runs all the tests\n");
+ fprintf(out, " contained in that apk.\n");
+ fprintf(out, "\n");
+ fprintf(out, " bit framework CtsProtoTestCases:*\n");
+ fprintf(out, " Builds the framework and the apk, syncs and reboots, then\n");
+ fprintf(out, " adb installs CtsProtoTestCases.apk, and runs all tests \n");
+ fprintf(out, " contained in that apk.\n");
+ fprintf(out, "\n");
+ fprintf(out, " bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\n");
+ fprintf(out, " bit CtsProtoTestCases:android.util.proto.cts.ProtoOutputStreamBoolTest\n");
+ fprintf(out, " Builds and installs CtsProtoTestCases.apk, and runs all the\n");
+ fprintf(out, " tests in the ProtoOutputStreamBoolTest class.\n");
+ fprintf(out, "\n");
+ fprintf(out, " bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\\#testWrite\n");
+ fprintf(out, " Builds and installs CtsProtoTestCases.apk, and runs the testWrite\n");
+ fprintf(out, " test method on that class.\n");
+ fprintf(out, "\n");
+ fprintf(out, " bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\\#testWrite,.ProtoOutputStreamBoolTest\\#testRepeated\n");
+ fprintf(out, " Builds and installs CtsProtoTestCases.apk, and runs the testWrite\n");
+ fprintf(out, " and testRepeated test methods on that class.\n");
+ fprintf(out, "\n");
+ fprintf(out, " Launching an Activity\n");
+ fprintf(out, " ---------------------\n");
+ fprintf(out, " To launch an activity, specify the activity class name after\n");
+ fprintf(out, " the module name.\n");
+ fprintf(out, "\n");
+ fprintf(out, " For example:\n");
+ fprintf(out, " bit StatusBarTest:NotificationBuilderTest\n");
+ fprintf(out, " bit StatusBarTest:.NotificationBuilderTest\n");
+ fprintf(out, " bit StatusBarTest:com.android.statusbartest.NotificationBuilderTest\n");
+ fprintf(out, " Builds and installs StatusBarTest.apk, launches the\n");
+ fprintf(out, " com.android.statusbartest/.NotificationBuilderTest activity.\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "usage: bit --tab ...\n");
+ fprintf(out, "\n");
+ fprintf(out, " Lists the targets in a format for tab completion. To get tab\n");
+ fprintf(out, " completion, add this to your bash environment:\n");
+ fprintf(out, "\n");
+ fprintf(out, " complete -C \"bit --tab\" bit\n");
+ fprintf(out, "\n");
+ fprintf(out, " Sourcing android's build/envsetup.sh will do this for you\n");
+ fprintf(out, " automatically.\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "usage: bit --help\n");
+ fprintf(out, "usage: bit -h\n");
+ fprintf(out, "\n");
+ fprintf(out, " Print this help message\n");
+ fprintf(out, "\n");
+}
+
+
+/**
+ * Sets the appropriate flag* variables. If there is a problem with the
+ * commandline arguments, prints the help message and exits with an error.
+ */
+static void
+parse_args(Options* options, int argc, const char** argv)
+{
+ // Help
+ if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
+ options->runHelp = true;
+ return;
+ }
+
+ // Tab
+ if (argc >= 4 && strcmp(argv[1], "--tab") == 0) {
+ options->runTab = true;
+ options->tabPattern = argv[3];
+ return;
+ }
+
+ // Normal usage
+ bool anyPhases = false;
+ bool gotPattern = false;
+ bool flagBuild = false;
+ bool flagInstall = false;
+ bool flagTest = false;
+ for (int i=1; i < argc; i++) {
+ string arg(argv[i]);
+ if (arg[0] == '-') {
+ for (size_t j=1; j<arg.size(); j++) {
+ switch (arg[j]) {
+ case '-':
+ break;
+ case 'b':
+ if (gotPattern) {
+ gotPattern = false;
+ flagInstall = false;
+ flagTest = false;
+ }
+ flagBuild = true;
+ anyPhases = true;
+ break;
+ case 'i':
+ if (gotPattern) {
+ gotPattern = false;
+ flagBuild = false;
+ flagTest = false;
+ }
+ flagInstall = true;
+ anyPhases = true;
+ break;
+ case 't':
+ if (gotPattern) {
+ gotPattern = false;
+ flagBuild = false;
+ flagInstall = false;
+ }
+ flagTest = true;
+ anyPhases = true;
+ break;
+ case 'r':
+ options->reboot = true;
+ break;
+ default:
+ fprintf(stderr, "Unrecognized option '%c'\n", arg[j]);
+ print_usage(stderr);
+ exit(1);
+ break;
+ }
+ }
+ } else {
+ Target* target = new Target(flagBuild || !anyPhases, flagInstall || !anyPhases,
+ flagTest || !anyPhases, arg);
+ size_t colonPos = arg.find(':');
+ if (colonPos == 0) {
+ fprintf(stderr, "Test / activity supplied without a module to build: %s\n",
+ arg.c_str());
+ print_usage(stderr);
+ exit(1);
+ } else if (colonPos == string::npos) {
+ target->name = arg;
+ } else {
+ target->name.assign(arg, 0, colonPos);
+ size_t beginPos = colonPos+1;
+ size_t commaPos;
+ while (true) {
+ commaPos = arg.find(',', beginPos);
+ if (commaPos == string::npos) {
+ if (beginPos != arg.size()) {
+ target->actions.push_back(string(arg, beginPos, commaPos));
+ }
+ break;
+ } else {
+ if (commaPos != beginPos) {
+ target->actions.push_back(string(arg, beginPos, commaPos-beginPos));
+ }
+ beginPos = commaPos+1;
+ }
+ }
+ }
+ options->targets.push_back(target);
+ gotPattern = true;
+ }
+ }
+ // If no pattern was supplied, give an error
+ if (options->targets.size() == 0) {
+ fprintf(stderr, "No PATTERN supplied.\n\n");
+ print_usage(stderr);
+ exit(1);
+ }
+}
+
+/**
+ * Get an environment variable.
+ * Exits with an error if it is unset or the empty string.
+ */
+static string
+get_required_env(const char* name, bool quiet)
+{
+ const char* value = getenv(name);
+ if (value == NULL || value[0] == '\0') {
+ if (!quiet) {
+ fprintf(stderr, "%s not set. Did you source build/envsetup.sh,"
+ " run lunch and do a build?\n", name);
+ }
+ exit(1);
+ }
+ return string(value);
+}
+
+/**
+ * Get the out directory.
+ *
+ * This duplicates the logic in build/make/core/envsetup.mk (which hasn't changed since 2011)
+ * so that we don't have to wait for get_build_var make invocation.
+ */
+string
+get_out_dir()
+{
+ const char* out_dir = getenv("OUT_DIR");
+ if (out_dir == NULL || out_dir[0] == '\0') {
+ const char* common_base = getenv("OUT_DIR_COMMON_BASE");
+ if (common_base == NULL || common_base[0] == '\0') {
+ // We don't prefix with buildTop because we cd there and it
+ // makes all the filenames long when being pretty printed.
+ return "out";
+ } else {
+ char pwd[PATH_MAX];
+ if (getcwd(pwd, PATH_MAX) == NULL) {
+ fprintf(stderr, "Your pwd is too long.\n");
+ exit(1);
+ }
+ const char* slash = strrchr(pwd, '/');
+ if (slash == NULL) {
+ slash = "";
+ }
+ string result(common_base);
+ result += slash;
+ return result;
+ }
+ }
+ return string(out_dir);
+}
+
+/**
+ * Check that a system property on the device matches the expected value.
+ * Exits with an error if they don't.
+ */
+static void
+check_device_property(const string& property, const string& expected)
+{
+ int err;
+ string deviceValue = get_system_property(property, &err);
+ check_error(err);
+ if (deviceValue != expected) {
+ print_error("There is a mismatch between the build you just did and the device you");
+ print_error("are trying to sync it to in the %s system property", property.c_str());
+ print_error(" build: %s", expected.c_str());
+ print_error(" device: %s", deviceValue.c_str());
+ exit(1);
+ }
+}
+
+/**
+ * Run the build, install, and test actions.
+ */
+void
+run_phases(vector<Target*> targets, bool reboot)
+{
+ int err = 0;
+
+ //
+ // Initialization
+ //
+
+ print_status("Initializing");
+
+ const string buildTop = get_required_env("ANDROID_BUILD_TOP", false);
+ const string buildProduct = get_required_env("TARGET_PRODUCT", false);
+ const string buildVariant = get_required_env("TARGET_BUILD_VARIANT", false);
+ const string buildType = get_required_env("TARGET_BUILD_TYPE", false);
+ const string buildDevice = get_build_var(buildTop, "TARGET_DEVICE", false);
+ const string buildId = get_build_var(buildTop, "BUILD_ID", false);
+ const string buildOut = get_out_dir();
+
+ // TODO: print_command("cd", buildTop.c_str());
+ chdir(buildTop.c_str());
+
+ // Get the modules for the targets
+ map<string,Module> modules;
+ read_modules(buildOut, buildDevice, &modules, false);
+ for (size_t i=0; i<targets.size(); i++) {
+ Target* target = targets[i];
+ map<string,Module>::iterator mod = modules.find(target->name);
+ if (mod != modules.end()) {
+ target->module = mod->second;
+ } else {
+ print_error("Error: Could not find module: %s", target->name.c_str());
+ err = 1;
+ }
+ }
+ if (err != 0) {
+ exit(1);
+ }
+
+ // Choose the goals
+ vector<string> goals;
+ for (size_t i=0; i<targets.size(); i++) {
+ Target* target = targets[i];
+ if (target->build) {
+ goals.push_back(target->name);
+ }
+ }
+
+ // Figure out whether we need to sync the system and which apks to install
+ string systemPath = buildOut + "/target/product/" + buildDevice + "/system/";
+ string dataPath = buildOut + "/target/product/" + buildDevice + "/data/";
+ bool syncSystem = false;
+ bool alwaysSyncSystem = false;
+ vector<InstallApk> installApks;
+ for (size_t i=0; i<targets.size(); i++) {
+ Target* target = targets[i];
+ if (target->install) {
+ for (size_t j=0; j<target->module.installed.size(); j++) {
+ const string& file = target->module.installed[j];
+ // System partition
+ if (starts_with(file, systemPath)) {
+ syncSystem = true;
+ if (!target->build) {
+ // If a system partition target didn't get built then
+ // it won't change we will always need to do adb sync
+ alwaysSyncSystem = true;
+ }
+ continue;
+ }
+ // Apk in the data partition
+ if (starts_with(file, dataPath) && ends_with(file, ".apk")) {
+ // Always install it if we didn't build it because otherwise
+ // it will never have changed.
+ installApks.push_back(InstallApk(file, !target->build));
+ continue;
+ }
+ }
+ }
+ }
+ map<string,FileInfo> systemFilesBefore;
+ if (syncSystem && !alwaysSyncSystem) {
+ get_directory_contents(systemPath, &systemFilesBefore);
+ }
+
+ //
+ // Build
+ //
+
+ // Run the build
+ if (goals.size() > 0) {
+ print_status("Building");
+ err = build_goals(goals);
+ check_error(err);
+ }
+
+ //
+ // Install
+ //
+
+ // Sync the system partition and reboot
+ bool skipSync = false;
+ if (syncSystem) {
+ print_status("Syncing /system");
+
+ if (!alwaysSyncSystem) {
+ // If nothing changed and we weren't forced to sync, skip the reboot for speed.
+ map<string,FileInfo> systemFilesAfter;
+ get_directory_contents(systemPath, &systemFilesAfter);
+ skipSync = !directory_contents_differ(systemFilesBefore, systemFilesAfter);
+ }
+ if (skipSync) {
+ printf("Skipping sync because no files changed.\n");
+ } else {
+ // Do some sanity checks
+ check_device_property("ro.build.product", buildProduct);
+ check_device_property("ro.build.type", buildVariant);
+ check_device_property("ro.build.id", buildId);
+
+ // Stop & Sync
+ err = run_adb("shell", "stop", NULL);
+ check_error(err);
+ err = run_adb("remount", NULL);
+ check_error(err);
+ err = run_adb("sync", "system", NULL);
+ check_error(err);
+
+ if (reboot) {
+ print_status("Rebooting");
+
+ err = run_adb("reboot", NULL);
+ check_error(err);
+ err = run_adb("wait-for-device", NULL);
+ check_error(err);
+ } else {
+ print_status("Restarting the runtime");
+
+ err = run_adb("shell", "setprop", "sys.boot_completed", "0", NULL);
+ check_error(err);
+ err = run_adb("shell", "start", NULL);
+ check_error(err);
+ }
+
+ while (true) {
+ string completed = get_system_property("sys.boot_completed", &err);
+ check_error(err);
+ if (completed == "1") {
+ break;
+ }
+ sleep(2);
+ }
+ sleep(1);
+ err = run_adb("shell", "wm", "dismiss-keyguard", NULL);
+ check_error(err);
+ }
+ }
+
+ // Install APKs
+ if (installApks.size() > 0) {
+ print_status("Installing APKs");
+ for (size_t i=0; i<installApks.size(); i++) {
+ InstallApk& apk = installApks[i];
+ if (!apk.file.fileInfo.exists || apk.file.HasChanged()) {
+ // It didn't exist before or it changed, so int needs install
+ err = run_adb("install", "-r", apk.file.filename.c_str(), NULL);
+ check_error(err);
+ apk.installed = true;
+ } else {
+ printf("APK didn't change. Skipping install of %s\n", apk.file.filename.c_str());
+ }
+ }
+ }
+
+ //
+ // Actions
+ //
+
+ // Inspect the apks, and figure out what is an activity and what needs a test runner
+ bool printedInspecting = false;
+ vector<TestAction> testActions;
+ vector<ActivityAction> activityActions;
+ for (size_t i=0; i<targets.size(); i++) {
+ Target* target = targets[i];
+ if (target->test) {
+ for (size_t j=0; j<target->module.installed.size(); j++) {
+ string filename = target->module.installed[j];
+
+ if (!ends_with(filename, ".apk")) {
+ continue;
+ }
+
+ if (!printedInspecting) {
+ printedInspecting = true;
+ print_status("Inspecting APKs");
+ }
+
+ Apk apk;
+ err = inspect_apk(&apk, filename);
+ check_error(err);
+
+ for (size_t k=0; k<target->actions.size(); k++) {
+ string actionString = target->actions[k];
+ if (actionString == "*") {
+ if (apk.runner.length() == 0) {
+ print_error("Error: Test requested for apk that doesn't"
+ " have an <instrumentation> tag: %s\n",
+ target->module.name.c_str());
+ exit(1);
+ }
+ TestAction action;
+ action.packageName = apk.package;
+ action.runner = apk.runner;
+ action.target = target;
+ testActions.push_back(action);
+ target->testActionCount++;
+ } else if (apk.HasActivity(actionString)) {
+ ActivityAction action;
+ action.packageName = apk.package;
+ action.className = full_class_name(apk.package, actionString);
+ activityActions.push_back(action);
+ } else {
+ if (apk.runner.length() == 0) {
+ print_error("Error: Test requested for apk that doesn't"
+ " have an <instrumentation> tag: %s\n",
+ target->module.name.c_str());
+ exit(1);
+ }
+ TestAction action;
+ action.packageName = apk.package;
+ action.runner = apk.runner;
+ action.className = full_class_name(apk.package, actionString);
+ action.target = target;
+ testActions.push_back(action);
+ target->testActionCount++;
+ }
+ }
+ }
+ }
+ }
+
+ // Run the instrumentation tests
+ TestResults testResults;
+ if (testActions.size() > 0) {
+ print_status("Running tests");
+ for (size_t i=0; i<testActions.size(); i++) {
+ TestAction& action = testActions[i];
+ testResults.SetCurrentAction(&action);
+ err = run_instrumentation_test(action.packageName, action.runner, action.className,
+ &testResults);
+ check_error(err);
+ if (action.passCount == 0 && action.failCount == 0) {
+ action.target->actionsWithNoTests = true;
+ }
+ int total = action.passCount + action.failCount;
+ printf("%sRan %d test%s for %s. ", g_escapeClearLine,
+ total, total > 1 ? "s" : "", action.target->name.c_str());
+ if (action.passCount == 0 && action.failCount == 0) {
+ printf("%s%d passed, %d failed%s\n", g_escapeYellowBold, action.passCount,
+ action.failCount, g_escapeEndColor);
+ } else if (action.failCount > 0) {
+ printf("%d passed, %s%d failed%s\n", action.passCount, g_escapeRedBold,
+ action.failCount, g_escapeEndColor);
+ } else {
+ printf("%s%d passed%s, %d failed\n", g_escapeGreenBold, action.passCount,
+ g_escapeEndColor, action.failCount);
+ }
+ }
+ }
+
+ // Launch the activity
+ if (activityActions.size() > 0) {
+ print_status("Starting activity");
+
+ if (activityActions.size() > 1) {
+ print_warning("Multiple activities specified. Will only start the first one:");
+ for (size_t i=0; i<activityActions.size(); i++) {
+ ActivityAction& action = activityActions[i];
+ print_warning(" %s",
+ pretty_component_name(action.packageName, action.className).c_str());
+ }
+ }
+
+ const ActivityAction& action = activityActions[0];
+ string componentName = action.packageName + "/" + action.className;
+ err = run_adb("shell", "am", "start", componentName.c_str(), NULL);
+ check_error(err);
+ }
+
+ //
+ // Print summary
+ //
+
+ printf("\n%s--------------------------------------------%s\n", g_escapeBold, g_escapeEndColor);
+
+ // Build
+ if (goals.size() > 0) {
+ printf("%sBuilt:%s\n", g_escapeBold, g_escapeEndColor);
+ for (size_t i=0; i<goals.size(); i++) {
+ printf(" %s\n", goals[i].c_str());
+ }
+ }
+
+ // Install
+ if (syncSystem) {
+ if (skipSync) {
+ printf("%sSkipped syncing /system partition%s\n", g_escapeBold, g_escapeEndColor);
+ } else {
+ printf("%sSynced /system partition%s\n", g_escapeBold, g_escapeEndColor);
+ }
+ }
+ if (installApks.size() > 0) {
+ bool printedTitle = false;
+ for (size_t i=0; i<installApks.size(); i++) {
+ const InstallApk& apk = installApks[i];
+ if (apk.installed) {
+ if (!printedTitle) {
+ printf("%sInstalled:%s\n", g_escapeBold, g_escapeEndColor);
+ printedTitle = true;
+ }
+ printf(" %s\n", apk.file.filename.c_str());
+ }
+ }
+ printedTitle = false;
+ for (size_t i=0; i<installApks.size(); i++) {
+ const InstallApk& apk = installApks[i];
+ if (!apk.installed) {
+ if (!printedTitle) {
+ printf("%sSkipped install:%s\n", g_escapeBold, g_escapeEndColor);
+ printedTitle = true;
+ }
+ printf(" %s\n", apk.file.filename.c_str());
+ }
+ }
+ }
+
+ // Tests
+ if (testActions.size() > 0) {
+ printf("%sRan tests:%s\n", g_escapeBold, g_escapeEndColor);
+ size_t maxNameLength = 0;
+ for (size_t i=0; i<targets.size(); i++) {
+ Target* target = targets[i];
+ if (target->test) {
+ size_t len = target->name.length();
+ if (len > maxNameLength) {
+ maxNameLength = len;
+ }
+ }
+ }
+ string padding(maxNameLength, ' ');
+ for (size_t i=0; i<targets.size(); i++) {
+ Target* target = targets[i];
+ if (target->testActionCount > 0) {
+ printf(" %s%s", target->name.c_str(), padding.c_str() + target->name.length());
+ if (target->actionsWithNoTests) {
+ printf(" %s%d passed, %d failed%s\n", g_escapeYellowBold,
+ target->testPassCount, target->testFailCount, g_escapeEndColor);
+ } else if (target->testFailCount > 0) {
+ printf(" %d passed, %s%d failed%s\n", target->testPassCount,
+ g_escapeRedBold, target->testFailCount, g_escapeEndColor);
+ } else {
+ printf(" %s%d passed%s, %d failed\n", g_escapeGreenBold,
+ target->testPassCount, g_escapeEndColor, target->testFailCount);
+ }
+ }
+ }
+ }
+ if (activityActions.size() > 1) {
+ printf("%sStarted Activity:%s\n", g_escapeBold, g_escapeEndColor);
+ const ActivityAction& action = activityActions[0];
+ printf(" %s\n", pretty_component_name(action.packageName, action.className).c_str());
+ }
+
+ printf("%s--------------------------------------------%s\n", g_escapeBold, g_escapeEndColor);
+}
+
+/**
+ * Implement tab completion of the target names from the all modules file.
+ */
+void
+run_tab_completion(const string& word)
+{
+ const string buildTop = get_required_env("ANDROID_BUILD_TOP", true);
+ const string buildProduct = get_required_env("TARGET_PRODUCT", false);
+ const string buildOut = get_out_dir();
+
+ chdir(buildTop.c_str());
+
+ string buildDevice = sniff_device_name(buildOut, buildProduct);
+
+ map<string,Module> modules;
+ read_modules(buildOut, buildDevice, &modules, true);
+
+ for (map<string,Module>::const_iterator it = modules.begin(); it != modules.end(); it++) {
+ if (starts_with(it->first, word)) {
+ printf("%s\n", it->first.c_str());
+ }
+ }
+}
+
+/**
+ * Main entry point.
+ */
+int
+main(int argc, const char** argv)
+{
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+ init_print();
+
+ Options options;
+ parse_args(&options, argc, argv);
+
+ if (options.runHelp) {
+ // Help
+ print_usage(stdout);
+ exit(0);
+ } else if (options.runTab) {
+ run_tab_completion(options.tabPattern);
+ exit(0);
+ } else {
+ // Normal run
+ run_phases(options.targets, options.reboot);
+ }
+
+ return 0;
+}
+
diff --git a/tools/bit/make.cpp b/tools/bit/make.cpp
new file mode 100644
index 0000000..60b5687
--- /dev/null
+++ b/tools/bit/make.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "make.h"
+
+#include "command.h"
+#include "print.h"
+#include "util.h"
+
+#include <json/reader.h>
+#include <json/value.h>
+
+#include <fstream>
+#include <string>
+#include <map>
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+
+using namespace std;
+
+map<string,string> g_buildVars;
+
+string
+get_build_var(const string& buildTop, const string& name, bool quiet)
+{
+ int err;
+
+ map<string,string>::iterator it = g_buildVars.find(name);
+ if (it == g_buildVars.end()) {
+ Command cmd("make");
+ cmd.AddArg("--no-print-directory");
+ cmd.AddArg("-C");
+ cmd.AddArg(buildTop);
+ cmd.AddArg("-f");
+ cmd.AddArg("build/core/config.mk");
+ cmd.AddArg(string("dumpvar-") + name);
+ cmd.AddEnv("CALLED_FROM_SETUP", "true");
+ cmd.AddEnv("BUILD_SYSTEM", "build/core");
+
+ string output = trim(get_command_output(cmd, &err, quiet));
+ if (err == 0) {
+ g_buildVars[name] = output;
+ return output;
+ } else {
+ return string();
+ }
+ } else {
+ return it->second;
+ }
+}
+
+string
+sniff_device_name(const string& buildOut, const string& product)
+{
+ string match("ro.build.product=" + product);
+
+ string base(buildOut + "/target/product");
+ DIR* dir = opendir(base.c_str());
+ if (dir == NULL) {
+ return string();
+ }
+
+ dirent* entry;
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_name[0] == '.') {
+ continue;
+ }
+ if (entry->d_type == DT_DIR) {
+ string filename(base + "/" + entry->d_name + "/system/build.prop");
+ vector<string> lines;
+ split_lines(&lines, read_file(filename));
+ for (size_t i=0; i<lines.size(); i++) {
+ if (lines[i] == match) {
+ return entry->d_name;
+ }
+ }
+ }
+ }
+
+ closedir(dir);
+ return string();
+}
+
+void
+json_error(const string& filename, const char* error, bool quiet)
+{
+ if (!quiet) {
+ print_error("Unable to parse module info file (%s): %s", error, filename.c_str());
+ print_error("Have you done a full build?");
+ }
+ exit(1);
+}
+
+static void
+get_values(const Json::Value& json, const string& name, vector<string>* result)
+{
+ Json::Value nullValue;
+
+ const Json::Value& value = json.get(name, nullValue);
+ if (!value.isArray()) {
+ return;
+ }
+
+ const int N = value.size();
+ for (int i=0; i<N; i++) {
+ const Json::Value& child = value[i];
+ if (child.isString()) {
+ result->push_back(child.asString());
+ }
+ }
+}
+
+void
+read_modules(const string& buildOut, const string& device, map<string,Module>* result, bool quiet)
+{
+ string filename(string(buildOut + "/target/product/") + device + "/module-info.json");
+ std::ifstream stream(filename, std::ifstream::binary);
+
+ if (stream.fail()) {
+ if (!quiet) {
+ print_error("Unable to open module info file: %s", filename.c_str());
+ print_error("Have you done a full build?");
+ }
+ exit(1);
+ }
+
+ Json::Value json;
+ Json::Reader reader;
+ if (!reader.parse(stream, json)) {
+ json_error(filename, "can't parse json format", quiet);
+ return;
+ }
+
+ if (!json.isObject()) {
+ json_error(filename, "root element not an object", quiet);
+ return;
+ }
+
+ vector<string> names = json.getMemberNames();
+ const int N = names.size();
+ for (int i=0; i<N; i++) {
+ const string& name = names[i];
+
+ const Json::Value& value = json[name];
+ if (!value.isObject()) {
+ continue;
+ }
+
+ Module module;
+
+ module.name = name;
+ get_values(value, "class", &module.classes);
+ get_values(value, "path", &module.paths);
+ get_values(value, "installed", &module.installed);
+
+ // Only keep classes we can handle
+ for (ssize_t i = module.classes.size() - 1; i >= 0; i--) {
+ string cl = module.classes[i];
+ if (!(cl == "JAVA_LIBRARIES" || cl == "EXECUTABLES" || cl == "SHARED_LIBRARIES"
+ || cl == "APPS")) {
+ module.classes.erase(module.classes.begin() + i);
+ }
+ }
+ if (module.classes.size() == 0) {
+ continue;
+ }
+
+ // Only target modules (not host)
+ for (ssize_t i = module.installed.size() - 1; i >= 0; i--) {
+ string fn = module.installed[i];
+ if (!starts_with(fn, buildOut + "/target/")) {
+ module.installed.erase(module.installed.begin() + i);
+ }
+ }
+ if (module.installed.size() == 0) {
+ continue;
+ }
+
+ (*result)[name] = module;
+ }
+}
+
+int
+build_goals(const vector<string>& goals)
+{
+ Command cmd("make");
+ cmd.AddArg("-f");
+ cmd.AddArg("build/core/main.mk");
+ for (size_t i=0; i<goals.size(); i++) {
+ cmd.AddArg(goals[i]);
+ }
+
+ return run_command(cmd);
+}
+
diff --git a/tools/bit/make.h b/tools/bit/make.h
new file mode 100644
index 0000000..bb83c6e
--- /dev/null
+++ b/tools/bit/make.h
@@ -0,0 +1,50 @@
+/*
+ * 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 MAKE_H
+#define MAKE_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+struct Module
+{
+ string name;
+ vector<string> classes;
+ vector<string> paths;
+ vector<string> installed;
+};
+
+string get_build_var(const string& buildTop, const string& name, bool quiet);
+
+/**
+ * Poke around in the out directory and try to find a device name that matches
+ * our product. This is faster than running get_build_var and good enough for
+ * tab completion.
+ *
+ * Returns the empty string if we can't find one.
+ */
+string sniff_device_name(const string& buildOut, const string& product);
+
+void read_modules(const string& buildOut, const string& buildDevice,
+ map<string,Module>* modules, bool quiet);
+
+int build_goals(const vector<string>& goals);
+
+#endif // MAKE_H
diff --git a/tools/bit/print.cpp b/tools/bit/print.cpp
new file mode 100644
index 0000000..790e0b4
--- /dev/null
+++ b/tools/bit/print.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "print.h"
+
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "util.h"
+
+bool g_stdoutIsTty;
+char const* g_escapeBold;
+char const* g_escapeRedBold;
+char const* g_escapeGreenBold;
+char const* g_escapeYellowBold;
+char const* g_escapeUnderline;
+char const* g_escapeEndColor;
+char const* g_escapeClearLine;
+
+void
+init_print()
+{
+ if (isatty(fileno(stdout))) {
+ g_stdoutIsTty = true;
+ g_escapeBold = "\033[1m";
+ g_escapeRedBold = "\033[91m\033[1m";
+ g_escapeGreenBold = "\033[92m\033[1m";
+ g_escapeYellowBold = "\033[93m\033[1m";
+ g_escapeUnderline = "\033[4m";
+ g_escapeEndColor = "\033[0m";
+ g_escapeClearLine = "\033[K";
+ } else {
+ g_stdoutIsTty = false;
+ g_escapeBold = "";
+ g_escapeRedBold = "";
+ g_escapeGreenBold = "";
+ g_escapeYellowBold = "";
+ g_escapeUnderline = "";
+ g_escapeEndColor = "";
+ g_escapeClearLine = "";
+ }
+}
+
+void
+print_status(const char* format, ...)
+{
+ printf("\n%s%s", g_escapeBold, g_escapeUnderline);
+
+ va_list args;
+ va_start(args, format);
+ vfprintf(stdout, format, args);
+ va_end(args);
+
+ printf("%s\n", g_escapeEndColor);
+}
+
+void
+print_command(const Command& command)
+{
+ fputs(g_escapeBold, stdout);
+ for (map<string,string>::const_iterator it=command.env.begin(); it!=command.env.end(); it++) {
+ fputs(it->first.c_str(), stdout);
+ fputc('=', stdout);
+ fputs(escape_for_commandline(it->second.c_str()).c_str(), stdout);
+ putc(' ', stdout);
+ }
+ fputs(command.prog.c_str(), stdout);
+ for (vector<string>::const_iterator it=command.args.begin(); it!=command.args.end(); it++) {
+ putc(' ', stdout);
+ fputs(escape_for_commandline(it->c_str()).c_str(), stdout);
+ }
+ fputs(g_escapeEndColor, stdout);
+ fputc('\n', stdout);
+}
+
+void
+print_error(const char* format, ...)
+{
+ fputs(g_escapeRedBold, stderr);
+
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+
+ fputs(g_escapeEndColor, stderr);
+ fputc('\n', stderr);
+}
+
+void
+print_warning(const char* format, ...)
+{
+ fputs(g_escapeYellowBold, stderr);
+
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+
+ fputs(g_escapeEndColor, stderr);
+ fputc('\n', stderr);
+}
+
+void
+print_one_line(const char* format, ...)
+{
+ if (g_stdoutIsTty) {
+ struct winsize ws;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
+ int size = ws.ws_col + 1;
+ char* buf = (char*)malloc(size);
+
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buf, size, format, args);
+ va_end(args);
+
+ printf("%s%s\r", buf, g_escapeClearLine);
+ free(buf);
+
+ fflush(stdout);
+ } else {
+ va_list args;
+ va_start(args, format);
+ vfprintf(stdout, format, args);
+ va_end(args);
+ printf("\n");
+ }
+}
+
+void
+check_error(int err)
+{
+ if (err != 0) {
+ fputc('\n', stderr);
+ print_error("Stopping due to errors.");
+ exit(1);
+ }
+}
+
+
diff --git a/tools/bit/print.h b/tools/bit/print.h
new file mode 100644
index 0000000..b6c3e9a
--- /dev/null
+++ b/tools/bit/print.h
@@ -0,0 +1,39 @@
+/*
+ * 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 PRINT_H
+#define PRINT_H
+
+#include "command.h"
+
+extern bool g_stdoutIsTty;
+extern char const* g_escapeBold;
+extern char const* g_escapeRedBold;
+extern char const* g_escapeGreenBold;
+extern char const* g_escapeYellowBold;
+extern char const* g_escapeUnderline;
+extern char const* g_escapeEndColor;
+extern char const* g_escapeClearLine;
+
+void init_print();
+void print_status(const char* format, ...);
+void print_command(const Command& command);
+void print_error(const char* format, ...);
+void print_warning(const char* format, ...);
+void print_one_line(const char* format, ...);
+void check_error(int err);
+
+#endif // PRINT_H
diff --git a/tools/bit/util.cpp b/tools/bit/util.cpp
new file mode 100644
index 0000000..fc93bcb
--- /dev/null
+++ b/tools/bit/util.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <string.h>
+#include <unistd.h>
+
+
+FileInfo::FileInfo()
+{
+ memset(this, 0, sizeof(FileInfo));
+}
+
+FileInfo::FileInfo(const FileInfo& that)
+{
+ memcpy(this, &that, sizeof(FileInfo));
+}
+
+FileInfo::FileInfo(const string& filename)
+{
+ struct stat st;
+ int err = stat(filename.c_str(), &st);
+ if (err != 0) {
+ memset(this, 0, sizeof(FileInfo));
+ } else {
+ exists = true;
+ mtime = st.st_mtime;
+ ctime = st.st_ctime;
+ size = st.st_size;
+ }
+}
+
+bool
+FileInfo::operator==(const FileInfo& that) const
+{
+ return exists == that.exists
+ && mtime == that.mtime
+ && ctime == that.ctime
+ && size == that.size;
+}
+
+bool
+FileInfo::operator!=(const FileInfo& that) const
+{
+ return exists != that.exists
+ || mtime != that.mtime
+ || ctime != that.ctime
+ || size != that.size;
+}
+
+FileInfo::~FileInfo()
+{
+}
+
+TrackedFile::TrackedFile()
+ :filename(),
+ fileInfo()
+{
+}
+
+TrackedFile::TrackedFile(const TrackedFile& that)
+{
+ filename = that.filename;
+ fileInfo = that.fileInfo;
+}
+
+TrackedFile::TrackedFile(const string& file)
+ :filename(file),
+ fileInfo(file)
+{
+}
+
+TrackedFile::~TrackedFile()
+{
+}
+
+bool
+TrackedFile::HasChanged() const
+{
+ FileInfo updated(filename);
+ return !updated.exists || fileInfo != updated;
+}
+
+void
+get_directory_contents(const string& name, map<string,FileInfo>* results)
+{
+ int err;
+ DIR* dir = opendir(name.c_str());
+ if (dir == NULL) {
+ return;
+ }
+
+ dirent* entry;
+ while ((entry = readdir(dir)) != NULL) {
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
+ continue;
+ }
+ if (entry->d_type == DT_DIR) {
+ string subdir = name + "/" + entry->d_name;
+ get_directory_contents(subdir, results);
+ } else if (entry->d_type == DT_LNK || entry->d_type == DT_REG) {
+ string filename(name + "/" + entry->d_name);
+ (*results)[filename] = FileInfo(filename);
+ }
+ }
+
+ closedir(dir);
+}
+
+bool
+directory_contents_differ(const map<string,FileInfo>& before, const map<string,FileInfo>& after)
+{
+ if (before.size() != after.size()) {
+ return true;
+ }
+ map<string,FileInfo>::const_iterator b = before.begin();
+ map<string,FileInfo>::const_iterator a = after.begin();
+ while (b != before.end() && a != after.end()) {
+ if (b->first != a->first) {
+ return true;
+ }
+ if (a->second != b->second) {
+ return true;
+ }
+ a++;
+ b++;
+ }
+ return false;
+}
+
+string
+escape_quotes(const char* str)
+{
+ string result;
+ while (*str) {
+ if (*str == '"') {
+ result += '\\';
+ result += '"';
+ } else {
+ result += *str;
+ }
+ }
+ return result;
+}
+
+string
+escape_for_commandline(const char* str)
+{
+ if (strchr(str, '"') != NULL || strchr(str, ' ') != NULL
+ || strchr(str, '\t') != NULL) {
+ return escape_quotes(str);
+ } else {
+ return str;
+ }
+}
+
+static bool
+spacechr(char c)
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r';
+}
+
+string
+trim(const string& str)
+{
+ const ssize_t N = (ssize_t)str.size();
+ ssize_t begin = 0;
+ while (begin < N && spacechr(str[begin])) {
+ begin++;
+ }
+ ssize_t end = N - 1;
+ while (end >= begin && spacechr(str[end])) {
+ end--;
+ }
+ return string(str, begin, end-begin+1);
+}
+
+bool
+starts_with(const string& str, const string& prefix)
+{
+ return str.compare(0, prefix.length(), prefix) == 0;
+}
+
+bool
+ends_with(const string& str, const string& suffix)
+{
+ if (str.length() < suffix.length()) {
+ return false;
+ } else {
+ return str.compare(str.length()-suffix.length(), suffix.length(), suffix) == 0;
+ }
+}
+
+void
+split_lines(vector<string>* result, const string& str)
+{
+ const int N = str.length();
+ int begin = 0;
+ int end = 0;
+ for (; end < N; end++) {
+ const char c = str[end];
+ if (c == '\r' || c == '\n') {
+ if (begin != end) {
+ result->push_back(string(str, begin, end-begin));
+ }
+ begin = end+1;
+ }
+ }
+ if (begin != end) {
+ result->push_back(string(str, begin, end-begin));
+ }
+}
+
+string
+read_file(const string& filename)
+{
+ FILE* file = fopen(filename.c_str(), "r");
+ if (file == NULL) {
+ return string();
+ }
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ char* buf = (char*)malloc(size);
+ fread(buf, 1, size, file);
+
+ string result(buf, size);
+
+ free(buf);
+ fclose(file);
+
+ return result;
+}
+
+
diff --git a/tools/bit/util.h b/tools/bit/util.h
new file mode 100644
index 0000000..718f147
--- /dev/null
+++ b/tools/bit/util.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+struct FileInfo
+{
+ bool exists;
+ time_t mtime;
+ time_t ctime;
+ off_t size;
+
+ FileInfo();
+ FileInfo(const FileInfo& that);
+ explicit FileInfo(const string& filename);
+ ~FileInfo();
+
+ bool operator==(const FileInfo& that) const;
+ bool operator!=(const FileInfo& that) const;
+};
+
+
+/**
+ * Record for a file that we are watching
+ */
+struct TrackedFile {
+ string filename;
+ FileInfo fileInfo;
+
+ TrackedFile();
+ TrackedFile(const TrackedFile& that);
+ explicit TrackedFile(const string& filename);
+ ~TrackedFile();
+
+ // Returns if the file has changed. If it doesn't currently exist, returns true.
+ bool HasChanged() const;
+};
+
+/**
+ * Get FileInfo structures recursively for all the files and symlinks in a directory.
+ * Does not traverse symlinks, but it does record them.
+ */
+void get_directory_contents(const string& dir, map<string,FileInfo>* results);
+
+bool directory_contents_differ(const map<string,FileInfo>& before,
+ const map<string,FileInfo>& after);
+
+string escape_quotes(const char* str);
+
+string escape_for_commandline(const char* str);
+
+string trim(const string& trim);
+
+bool starts_with(const string& str, const string& prefix);
+
+bool ends_with(const string& str, const string& suffix);
+
+void split_lines(vector<string>* result, const string& str);
+
+string read_file(const string& filename);
+
+#endif // UTIL_H
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index 090cee8..e1fc5ec 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -92,8 +92,7 @@
@Nullable
public static Bitmap_Delegate getDelegate(@Nullable Bitmap bitmap) {
- // refSkPixelRef is a hack to get the native pointer: see #nativeRefPixelRef()
- return bitmap == null ? null : getDelegate(bitmap.refSkPixelRef());
+ return bitmap == null ? null : getDelegate(bitmap.getNativeInstance());
}
/**
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 09ab657..db5b119 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -413,7 +413,8 @@
}
@Override
- public int[] setNewConfiguration(Configuration arg0) throws RemoteException {
+ public int[] setNewDisplayOverrideConfiguration(Configuration arg0, int displayId)
+ throws RemoteException {
// TODO Auto-generated method stub
return null;
}
@@ -487,7 +488,7 @@
}
@Override
- public Configuration updateOrientationFromAppTokens(Configuration arg0, IBinder arg1)
+ public Configuration updateOrientationFromAppTokens(Configuration arg0, IBinder arg1, int arg2)
throws RemoteException {
// TODO Auto-generated method stub
return null;
@@ -604,7 +605,7 @@
}
@Override
- public void getStableInsets(Rect outInsets) throws RemoteException {
+ public void getStableInsets(int displayId, Rect outInsets) throws RemoteException {
}
@Override
diff --git a/tools/streaming_proto/main.cpp b/tools/streaming_proto/main.cpp
index d286213..5435728 100644
--- a/tools/streaming_proto/main.cpp
+++ b/tools/streaming_proto/main.cpp
@@ -191,38 +191,55 @@
switch (field.type()) {
case FieldDescriptorProto::TYPE_DOUBLE:
result |= FIELD_TYPE_DOUBLE;
+ break;
case FieldDescriptorProto::TYPE_FLOAT:
result |= FIELD_TYPE_FLOAT;
+ break;
case FieldDescriptorProto::TYPE_INT64:
result |= FIELD_TYPE_INT64;
+ break;
case FieldDescriptorProto::TYPE_UINT64:
result |= FIELD_TYPE_UINT64;
+ break;
case FieldDescriptorProto::TYPE_INT32:
result |= FIELD_TYPE_INT32;
+ break;
case FieldDescriptorProto::TYPE_FIXED64:
result |= FIELD_TYPE_FIXED64;
+ break;
case FieldDescriptorProto::TYPE_FIXED32:
result |= FIELD_TYPE_FIXED32;
+ break;
case FieldDescriptorProto::TYPE_BOOL:
result |= FIELD_TYPE_BOOL;
+ break;
case FieldDescriptorProto::TYPE_STRING:
result |= FIELD_TYPE_STRING;
+ break;
case FieldDescriptorProto::TYPE_MESSAGE:
result |= FIELD_TYPE_OBJECT;
+ break;
case FieldDescriptorProto::TYPE_BYTES:
result |= FIELD_TYPE_BYTES;
+ break;
case FieldDescriptorProto::TYPE_UINT32:
result |= FIELD_TYPE_UINT32;
+ break;
case FieldDescriptorProto::TYPE_ENUM:
result |= FIELD_TYPE_ENUM;
+ break;
case FieldDescriptorProto::TYPE_SFIXED32:
result |= FIELD_TYPE_SFIXED32;
+ break;
case FieldDescriptorProto::TYPE_SFIXED64:
result |= FIELD_TYPE_SFIXED64;
+ break;
case FieldDescriptorProto::TYPE_SINT32:
result |= FIELD_TYPE_SINT32;
+ break;
case FieldDescriptorProto::TYPE_SINT64:
result |= FIELD_TYPE_SINT64;
+ break;
default:
;
}
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.aidl b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.aidl
new file mode 100644
index 0000000..6b1cea8
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2;
+
+parcelable PasspointConfiguration;
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
new file mode 100644
index 0000000..18aae53
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2;
+
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Class representing Passpoint configuration. This contains configurations specified in
+ * PerProviderSubscription (PPS) Management Object (MO) tree.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * Currently, only HomeSP and Credential subtrees are supported.
+ *
+ * @hide
+ */
+public final class PasspointConfiguration implements Parcelable {
+ public HomeSP homeSp = null;
+ public Credential credential = null;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(homeSp, flags);
+ dest.writeParcelable(credential, flags);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof PasspointConfiguration)) {
+ return false;
+ }
+ PasspointConfiguration that = (PasspointConfiguration) thatObject;
+ return (homeSp == null ? that.homeSp == null : homeSp.equals(that.homeSp)) &&
+ (credential == null ? that.credential == null :
+ credential.equals(that.credential));
+ }
+
+ public static final Creator<PasspointConfiguration> CREATOR =
+ new Creator<PasspointConfiguration>() {
+ @Override
+ public PasspointConfiguration createFromParcel(Parcel in) {
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = in.readParcelable(null);
+ config.credential = in.readParcelable(null);
+ return config;
+ }
+ @Override
+ public PasspointConfiguration[] newArray(int size) {
+ return new PasspointConfiguration[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
new file mode 100644
index 0000000..65a49ea
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
@@ -0,0 +1,786 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.omadm;
+
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.xml.sax.SAXException;
+
+/**
+ * Utility class for converting OMA-DM (Open Mobile Alliance's Device Management)
+ * PPS-MO (PerProviderSubscription Management Object) XML tree to a
+ * {@link PasspointConfiguration} object.
+ *
+ * Currently this only supports PerProviderSubscription/HomeSP and
+ * PerProviderSubscription/Credential subtree for Hotspot 2.0 Release 1 support.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * Below is a sample XML string for a Release 1 PPS MO tree:
+ *
+ * <MgmtTree xmlns="syncml:dmddf1.2">
+ * <VerDTD>1.2</VerDTD>
+ * <Node>
+ * <NodeName>PerProviderSubscription</NodeName>
+ * <RTProperties>
+ * <Type>
+ * <DDFName>urn:wfa:mo:hotspot2dot0perprovidersubscription:1.0</DDFName>
+ * </Type>
+ * </RTProperties>
+ * <Node>
+ * <NodeName>i001</NodeName>
+ * <Node>
+ * <NodeName>HomeSP</NodeName>
+ * <Node>
+ * <NodeName>FriendlyName</NodeName>
+ * <Value>Century House</Value>
+ * </Node>
+ * <Node>
+ * <NodeName>FQDN</NodeName>
+ * <Value>mi6.co.uk</Value>
+ * </Node>
+ * <Node>
+ * <NodeName>RoamingConsortiumOI</NodeName>
+ * <Value>112233,445566</Value>
+ * </Node>
+ * </Node>
+ * <Node>
+ * <NodeName>Credential</NodeName>
+ * <Node>
+ * <NodeName>Realm</NodeName>
+ * <Value>shaken.stirred.com</Value>
+ * </Node>
+ * <Node>
+ * <NodeName>UsernamePassword</NodeName>
+ * <Node>
+ * <NodeName>Username</NodeName>
+ * <Value>james</Value>
+ * </Node>
+ * <Node>
+ * <NodeName>Password</NodeName>
+ * <Value>Ym9uZDAwNw==</Value>
+ * </Node>
+ * <Node>
+ * <NodeName>EAPMethod</NodeName>
+ * <Node>
+ * <NodeName>EAPType</NodeName>
+ * <Value>21</Value>
+ * </Node>
+ * <Node>
+ * <NodeName>InnerMethod</NodeName>
+ * <Value>MS-CHAP-V2</Value>
+ * </Node>
+ * </Node>
+ * </Node>
+ * </Node>
+ * </Node>
+ * </Node>
+ * </MgmtTree>
+ *
+ * @hide
+ */
+public final class PPSMOParser {
+ private static final String TAG = "PPSMOParser";
+
+ /**
+ * XML tags expected in the PPS MO (PerProviderSubscription Management Object) XML tree.
+ */
+ private static final String TAG_MANAGEMENT_TREE = "MgmtTree";
+ private static final String TAG_VER_DTD = "VerDTD";
+ private static final String TAG_NODE = "Node";
+ private static final String TAG_NODE_NAME = "NodeName";
+ private static final String TAG_RT_PROPERTIES = "RTProperties";
+ private static final String TAG_TYPE = "Type";
+ private static final String TAG_DDF_NAME = "DDFName";
+ private static final String TAG_VALUE = "Value";
+
+ /**
+ * Name for PerProviderSubscription node.
+ */
+ private static final String NODE_PER_PROVIDER_SUBSCRIPTION = "PerProviderSubscription";
+
+ /**
+ * Fields under HomeSP subtree.
+ */
+ private static final String NODE_HOMESP = "HomeSP";
+ private static final String NODE_FQDN = "FQDN";
+ private static final String NODE_FRIENDLY_NAME = "FriendlyName";
+ private static final String NODE_ROAMING_CONSORTIUM_OI = "RoamingConsortiumOI";
+
+ /**
+ * Fields under Credential subtree.
+ */
+ private static final String NODE_CREDENTIAL = "Credential";
+ private static final String NODE_USERNAME_PASSWORD = "UsernamePassword";
+ private static final String NODE_USERNAME = "Username";
+ private static final String NODE_PASSWORD = "Password";
+ private static final String NODE_EAP_METHOD = "EAPMethod";
+ private static final String NODE_EAP_TYPE = "EAPType";
+ private static final String NODE_INNER_METHOD = "InnerMethod";
+ private static final String NODE_DIGITAL_CERTIFICATE = "DigitalCertificate";
+ private static final String NODE_CERTIFICATE_TYPE = "CertificateType";
+ private static final String NODE_CERT_SHA256_FINGERPRINT = "CertSHA256FingerPrint";
+ private static final String NODE_REALM = "Realm";
+ private static final String NODE_SIM = "SIM";
+ private static final String NODE_SIM_IMSI = "IMSI";
+
+ /**
+ * URN (Unique Resource Name) for PerProviderSubscription Management Object Tree.
+ */
+ private static final String PPS_MO_URN =
+ "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0";
+
+ /**
+ * Exception for generic parsing errors.
+ */
+ private static class ParsingException extends Exception {
+ public ParsingException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * Class representing a node within the PerProviderSubscription tree.
+ * This is used to flatten out and eliminate the extra layering in the XMLNode tree,
+ * to make the data parsing easier and cleaner.
+ *
+ * A PPSNode can be an internal or a leaf node, but not both.
+ *
+ */
+ private static abstract class PPSNode {
+ private final String mName;
+ public PPSNode(String name) {
+ mName = name;
+ }
+
+ /**
+ * @return the name of the node
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Applies for internal node only.
+ *
+ * @return the list of children nodes.
+ */
+ public abstract List<PPSNode> getChildren();
+
+ /**
+ * Applies for leaf node only.
+ *
+ * @return the string value of the node
+ */
+ public abstract String getValue();
+
+ /**
+ * @return a flag indicating if this is a leaf or an internal node
+ */
+ public abstract boolean isLeaf();
+ }
+
+ /**
+ * Class representing a leaf node in a PPS (PerProviderSubscription) tree.
+ */
+ private static class LeafNode extends PPSNode {
+ private final String mValue;
+ public LeafNode(String nodeName, String value) {
+ super(nodeName);
+ mValue = value;
+ }
+
+ @Override
+ public String getValue() {
+ return mValue;
+ }
+
+ @Override
+ public List<PPSNode> getChildren() {
+ return null;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+ }
+
+ /**
+ * Class representing an internal node in a PPS (PerProviderSubscription) tree.
+ */
+ private static class InternalNode extends PPSNode {
+ private final List<PPSNode> mChildren;
+ public InternalNode(String nodeName, List<PPSNode> children) {
+ super(nodeName);
+ mChildren = children;
+ }
+
+ @Override
+ public String getValue() {
+ return null;
+ }
+
+ @Override
+ public List<PPSNode> getChildren() {
+ return mChildren;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return false;
+ }
+ }
+
+ /**
+ * Convert a XML string representation of a PPS MO (PerProviderSubscription
+ * Management Object) tree to a {@link PasspointConfiguration} object.
+ *
+ * @param xmlString XML string representation of a PPS MO tree
+ * @return {@link PasspointConfiguration} or null
+ */
+ public static PasspointConfiguration parseMOText(String xmlString) {
+ // Convert the XML string to a XML tree.
+ XMLParser xmlParser = new XMLParser();
+ XMLNode root = null;
+ try {
+ root = xmlParser.parse(xmlString);
+ } catch(IOException | SAXException e) {
+ return null;
+ }
+ if (root == null) {
+ return null;
+ }
+
+ // Verify root node is a "MgmtTree" node.
+ if (root.getTag() != TAG_MANAGEMENT_TREE) {
+ Log.e(TAG, "Root is not a MgmtTree");
+ return null;
+ }
+
+ String verDtd = null; // Used for detecting duplicate VerDTD element.
+ PasspointConfiguration config = null;
+ for (XMLNode child : root.getChildren()) {
+ switch(child.getTag()) {
+ case TAG_VER_DTD:
+ if (verDtd != null) {
+ Log.e(TAG, "Duplicate VerDTD element");
+ return null;
+ }
+ verDtd = child.getText();
+ break;
+ case TAG_NODE:
+ if (config != null) {
+ Log.e(TAG, "Unexpected multiple Node element under MgmtTree");
+ return null;
+ }
+ try {
+ config = parsePpsNode(child);
+ } catch (ParsingException e) {
+ Log.e(TAG, e.getMessage());
+ return null;
+ }
+ break;
+ default:
+ Log.e(TAG, "Unknown node: " + child.getTag());
+ return null;
+ }
+ }
+ return config;
+ }
+
+ /**
+ * Parse a PerProviderSubscription node. Below is the format of the XML tree (with
+ * each XML element represent a node in the tree):
+ *
+ * <Node>
+ * <NodeName>PerProviderSubscription</NodeName>
+ * <RTProperties>
+ * ...
+ * </RTPProperties>
+ * <Node>
+ * ...
+ * </Node>
+ * </Node>
+ *
+ * @param node XMLNode that contains PerProviderSubscription node.
+ * @return PasspointConfiguration or null
+ * @throws ParsingException
+ */
+ private static PasspointConfiguration parsePpsNode(XMLNode node)
+ throws ParsingException {
+ PasspointConfiguration config = null;
+ String nodeName = null;
+ for (XMLNode child : node.getChildren()) {
+ switch (child.getTag()) {
+ case TAG_NODE_NAME:
+ if (nodeName != null) {
+ throw new ParsingException("Duplicant NodeName: " + child.getText());
+ }
+ nodeName = child.getText();
+ if (!TextUtils.equals(nodeName, NODE_PER_PROVIDER_SUBSCRIPTION)) {
+ throw new ParsingException("Unexpected NodeName: " + nodeName);
+ }
+ break;
+ case TAG_NODE:
+ // Only one PerProviderSubscription instance is expected and allowed.
+ if (config != null) {
+ throw new ParsingException("Multiple PPS instance");
+ }
+ // Convert the XML tree to a PPS tree.
+ PPSNode ppsInstanceRoot = buildPpsNode(child);
+ config = parsePpsInstance(ppsInstanceRoot);
+ break;
+ case TAG_RT_PROPERTIES:
+ // Parse and verify URN stored in the RT (Run Time) Properties.
+ String urn = parseUrn(child);
+ if (!TextUtils.equals(urn, PPS_MO_URN)) {
+ throw new ParsingException("Unknown URN: " + urn);
+ }
+ break;
+ default:
+ throw new ParsingException("Unknown tag under PPS node: " + child.getTag());
+ }
+ }
+ return config;
+ }
+
+ /**
+ * Parse the URN stored in the RTProperties. Below is the format of the RTPProperties node:
+ *
+ * <RTProperties>
+ * <Type>
+ * <DDFName>urn:...</DDFName>
+ * </Type>
+ * </RTProperties>
+ *
+ * @param node XMLNode that contains RTProperties node.
+ * @return URN String of URN.
+ * @throws ParsingException
+ */
+ private static String parseUrn(XMLNode node) throws ParsingException {
+ if (node.getChildren().size() != 1)
+ throw new ParsingException("Expect RTPProperties node to only have one child");
+
+ XMLNode typeNode = node.getChildren().get(0);
+ if (typeNode.getChildren().size() != 1) {
+ throw new ParsingException("Expect Type node to only have one child");
+ }
+ if (!TextUtils.equals(typeNode.getTag(), TAG_TYPE)) {
+ throw new ParsingException("Unexpected tag for Type: " + typeNode.getTag());
+ }
+
+ XMLNode ddfNameNode = typeNode.getChildren().get(0);
+ if (!ddfNameNode.getChildren().isEmpty()) {
+ throw new ParsingException("Expect DDFName node to have no child");
+ }
+ if (!TextUtils.equals(ddfNameNode.getTag(), TAG_DDF_NAME)) {
+ throw new ParsingException("Unexpected tag for DDFName: " + ddfNameNode.getTag());
+ }
+
+ return ddfNameNode.getText();
+ }
+
+ /**
+ * Convert a XML tree represented by XMLNode to a PPS (PerProviderSubscription) instance tree
+ * represented by PPSNode. This flattens out the XML tree to allow easier and cleaner parsing
+ * of the PPS configuration data. Only three types of XML tag are expected: "NodeName",
+ * "Node", and "Value".
+ *
+ * The original XML tree (each XML element represent a node):
+ *
+ * <Node>
+ * <NodeName>root</NodeName>
+ * <Node>
+ * <NodeName>child1</NodeName>
+ * <Value>value1</Value>
+ * </Node>
+ * <Node>
+ * <NodeName>child2</NodeName>
+ * <Node>
+ * <NodeName>grandchild1</NodeName>
+ * ...
+ * </Node>
+ * </Node>
+ * ...
+ * </Node>
+ *
+ * The converted PPS tree:
+ *
+ * [root] --- [child1, value1]
+ * |
+ * ---------[child2] --------[grandchild1] --- ...
+ *
+ * @param node XMLNode pointed to the root of a XML tree
+ * @return PPSNode pointing to the root of a PPS tree
+ * @throws ParsingException
+ */
+ private static PPSNode buildPpsNode(XMLNode node) throws ParsingException {
+ String nodeName = null;
+ String nodeValue = null;
+ List<PPSNode> childNodes = new ArrayList<PPSNode>();
+ // Names of parsed child nodes, use for detecting multiple child nodes with the same name.
+ Set<String> parsedNodes = new HashSet<String>();
+
+ for (XMLNode child : node.getChildren()) {
+ String tag = child.getTag();
+ if (TextUtils.equals(tag, TAG_NODE_NAME)) {
+ if (nodeName != null) {
+ throw new ParsingException("Duplicate NodeName node");
+ }
+ nodeName = child.getText();
+ } else if (TextUtils.equals(tag, TAG_NODE)) {
+ PPSNode ppsNode = buildPpsNode(child);
+ if (parsedNodes.contains(ppsNode.getName())) {
+ throw new ParsingException("Duplicate node: " + ppsNode.getName());
+ }
+ parsedNodes.add(ppsNode.getName());
+ childNodes.add(ppsNode);
+ } else if (TextUtils.equals(tag, TAG_VALUE)) {
+ if (nodeValue != null) {
+ throw new ParsingException("Duplicate Value node");
+ }
+ nodeValue = child.getText();
+ } else {
+ throw new ParsingException("Unknown tag: " + tag);
+ }
+ }
+
+ if (nodeName == null) {
+ throw new ParsingException("Invalid node: missing NodeName");
+ }
+ if (nodeValue == null && childNodes.size() == 0) {
+ throw new ParsingException("Invalid node: " + nodeName +
+ " missing both value and children");
+ }
+ if (nodeValue != null && childNodes.size() > 0) {
+ throw new ParsingException("Invalid node: " + nodeName +
+ " contained both value and children");
+ }
+
+ if (nodeValue != null) {
+ return new LeafNode(nodeName, nodeValue);
+ }
+ return new InternalNode(nodeName, childNodes);
+ }
+
+ /**
+ * Return the value of a PPSNode. An exception will be thrown if the given node
+ * is not a leaf node.
+ *
+ * @param node PPSNode to retrieve the value from
+ * @return String representing the value of the node
+ * @throws ParsingException
+ */
+ private static String getPpsNodeValue(PPSNode node) throws ParsingException {
+ if (!node.isLeaf()) {
+ throw new ParsingException("Cannot get value from a non-leaf node: " + node.getName());
+ }
+ return node.getValue();
+ }
+
+ /**
+ * Parse a PPS (PerProviderSubscription) configurations from a PPS tree.
+ *
+ * @param root PPSNode representing the root of the PPS tree
+ * @return PasspointConfiguration
+ * @throws ParsingException
+ */
+ private static PasspointConfiguration parsePpsInstance(PPSNode root)
+ throws ParsingException {
+ if (root.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for PPS instance");
+ }
+
+ PasspointConfiguration config = new PasspointConfiguration();
+ for (PPSNode child : root.getChildren()) {
+ switch(child.getName()) {
+ case NODE_HOMESP:
+ config.homeSp = parseHomeSP(child);
+ break;
+ case NODE_CREDENTIAL:
+ config.credential = parseCredential(child);
+ break;
+ default:
+ throw new ParsingException("Unknown node: " + child.getName());
+ }
+ }
+ return config;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/HomeSP subtree.
+ *
+ * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP subtree
+ * @return HomeSP
+ * @throws ParsingException
+ */
+ private static HomeSP parseHomeSP(PPSNode node) throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for HomeSP");
+ }
+
+ HomeSP homeSp = new HomeSP();
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_FQDN:
+ homeSp.fqdn = getPpsNodeValue(child);
+ break;
+ case NODE_FRIENDLY_NAME:
+ homeSp.friendlyName = getPpsNodeValue(child);
+ break;
+ case NODE_ROAMING_CONSORTIUM_OI:
+ homeSp.roamingConsortiumOIs =
+ parseRoamingConsortiumOI(getPpsNodeValue(child));
+ break;
+ default:
+ throw new ParsingException("Unknown node under HomeSP: " + child.getName());
+ }
+ }
+ return homeSp;
+ }
+
+ /**
+ * Parse the roaming consortium OI string, which contains a list of OIs separated by ",".
+ *
+ * @param oiStr string containing list of OIs (Organization Identifiers) separated by ","
+ * @return long[]
+ * @throws ParsingException
+ */
+ private static long[] parseRoamingConsortiumOI(String oiStr)
+ throws ParsingException {
+ String[] oiStrArray = oiStr.split(",");
+ long[] oiArray = new long[oiStrArray.length];
+ for (int i = 0; i < oiStrArray.length; i++) {
+ try {
+ oiArray[i] = Long.parseLong(oiStrArray[i], 16);
+ } catch (NumberFormatException e) {
+ throw new ParsingException("Invalid OI: " + oiStrArray[i]);
+ }
+ }
+ return oiArray;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Credential subtree.
+ *
+ * @param node PPSNode representing the root of the PerProviderSubscription/Credential subtree
+ * @return Credential
+ * @throws ParsingException
+ */
+ private static Credential parseCredential(PPSNode node) throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for HomeSP");
+ }
+
+ Credential credential = new Credential();
+ for (PPSNode child: node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_USERNAME_PASSWORD:
+ credential.userCredential = parseUserCredential(child);
+ break;
+ case NODE_DIGITAL_CERTIFICATE:
+ credential.certCredential = parseCertificateCredential(child);
+ break;
+ case NODE_REALM:
+ credential.realm = getPpsNodeValue(child);
+ break;
+ case NODE_SIM:
+ credential.simCredential = parseSimCredential(child);
+ break;
+ default:
+ throw new ParsingException("Unknown node under Credential: " +
+ child.getName());
+ }
+ }
+ return credential;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Credential/UsernamePassword subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Credential/UsernamePassword subtree
+ * @return Credential.UserCredential
+ * @throws ParsingException
+ */
+ private static Credential.UserCredential parseUserCredential(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for UsernamePassword");
+ }
+
+ Credential.UserCredential userCred = new Credential.UserCredential();
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_USERNAME:
+ userCred.username = getPpsNodeValue(child);
+ break;
+ case NODE_PASSWORD:
+ userCred.password = getPpsNodeValue(child);
+ break;
+ case NODE_EAP_METHOD:
+ parseEAPMethod(child, userCred);
+ break;
+ default:
+ throw new ParsingException("Unknown node under UsernamPassword: " +
+ child.getName());
+ }
+ }
+ return userCred;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Credential/UsernamePassword/EAPMethod
+ * subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Credential/UsernamePassword/EAPMethod subtree
+ * @param userCred UserCredential to be updated with EAP method values.
+ * @throws ParsingException
+ */
+ private static void parseEAPMethod(PPSNode node, Credential.UserCredential userCred)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for EAPMethod");
+ }
+
+ for (PPSNode child : node.getChildren()) {
+ switch(child.getName()) {
+ case NODE_EAP_TYPE:
+ userCred.eapType = parseInteger(getPpsNodeValue(child));
+ break;
+ case NODE_INNER_METHOD:
+ userCred.nonEapInnerMethod = getPpsNodeValue(child);
+ break;
+ default:
+ throw new ParsingException("Unknown node under EAPMethod: " + child.getName());
+ }
+ }
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Credential/DigitalCertificate subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Credential/DigitalCertificate subtree
+ * @return Credential.CertificateCredential
+ * @throws ParsingException
+ */
+ private static Credential.CertificateCredential parseCertificateCredential(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for DigitalCertificate");
+ }
+
+ Credential.CertificateCredential certCred = new Credential.CertificateCredential();
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_CERTIFICATE_TYPE:
+ certCred.certType = getPpsNodeValue(child);
+ break;
+ case NODE_CERT_SHA256_FINGERPRINT:
+ certCred.certSha256FingerPrint = parseHexString(getPpsNodeValue(child));
+ break;
+ default:
+ throw new ParsingException("Unknown node under DigitalCertificate: " +
+ child.getName());
+ }
+ }
+ return certCred;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Credential/SIM subtree.
+ *
+ * @param node PPSNode representing the root of the PerProviderSubscription/Credential/SIM
+ * subtree
+ * @return Credential.SimCredential
+ * @throws ParsingException
+ */
+ private static Credential.SimCredential parseSimCredential(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for SIM");
+ }
+
+ Credential.SimCredential simCred = new Credential.SimCredential();
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_SIM_IMSI:
+ simCred.imsi = getPpsNodeValue(child);
+ break;
+ case NODE_EAP_TYPE:
+ simCred.eapType = parseInteger(getPpsNodeValue(child));
+ break;
+ default:
+ throw new ParsingException("Unknown node under SIM: " + child.getName());
+ }
+ }
+ return simCred;
+ }
+
+ /**
+ * Convert a hex string to a byte array.
+ *
+ * @param str String containing hex values
+ * @return byte[]
+ * @throws ParsingException
+ */
+ private static byte[] parseHexString(String str) throws ParsingException {
+ if ((str.length() & 1) == 1) {
+ throw new ParsingException("Odd length hex string: " + str.length());
+ }
+
+ byte[] result = new byte[str.length() / 2];
+ for (int i = 0; i < result.length; i++) {
+ int index = i * 2;
+ try {
+ result[i] = (byte) Integer.parseInt(str.substring(index, index + 2), 16);
+ } catch (NumberFormatException e) {
+ throw new ParsingException("Invalid hex string: " + str);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Parse an integer string.
+ *
+ * @param value String of integer value
+ * @return int
+ * @throws ParsingException
+ */
+ private static int parseInteger(String value) throws ParsingException {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new ParsingException("Invalid integer value: " + value);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java b/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java
new file mode 100644
index 0000000..e87698c
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.omadm;
+
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class represent a node in an XML tree. Each node is an XML element.
+ * Used by {@link XMLParser} for parsing/converting each XML element to XMLNode.
+ *
+ * @hide
+ */
+public class XMLNode {
+ private final String mTag;
+ private final List<XMLNode> mChildren;
+ private final XMLNode mParent;
+ private StringBuilder mTextBuilder;
+ private String mText;
+
+ public XMLNode(XMLNode parent, String tag) {
+ mTag = tag;
+ mParent = parent;
+ mChildren = new ArrayList<>();
+ mTextBuilder = new StringBuilder();
+ mText = null;
+ }
+
+ /**
+ * Adding a text to this node. Invoked by {@link XMLParser#characters}.
+ *
+ * @param text String to be added
+ */
+ public void addText(String text) {
+ mTextBuilder.append(text);
+ }
+
+ /**
+ * Adding a child node to this node. Invoked by {@link XMLParser#startElement}.
+ *
+ * @param child XMLNode to be added
+ */
+ public void addChild(XMLNode child) {
+ mChildren.add(child);
+ }
+
+ /**
+ * Invoked when the end of the XML element is detected. Used for further processing
+ * of the text enclosed within this XML element. Invoked by {@link XMLParser#endElement}.
+ */
+ public void close() {
+ // Remove the leading and the trailing whitespaces.
+ mText = mTextBuilder.toString().trim();
+ mTextBuilder = null;
+ }
+
+ public String getTag() {
+ return mTag;
+ }
+
+ public XMLNode getParent() {
+ return mParent;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ public List<XMLNode> getChildren() {
+ return mChildren;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof XMLNode)) {
+ return false;
+ }
+ XMLNode that = (XMLNode) thatObject;
+
+ return TextUtils.equals(mTag, that.mTag) &&
+ TextUtils.equals(mText, that.mText) &&
+ mChildren.equals(that.mChildren);
+ }
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/XMLParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/XMLParser.java
new file mode 100644
index 0000000..948052c
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/XMLParser.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.omadm;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import android.text.TextUtils;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+/**
+ * Class for parsing an XML string to an XML tree represented by {@link XMLNode}.
+ *
+ * The original XML string:
+ * <root>
+ * <tag1>text1</tag1>
+ * <tag2>
+ * <tag3>text3</tag3>
+ * </tag2>
+ * </root>
+ *
+ * The XML tree representation:
+ * [root]
+ * |
+ * |
+ * [tag1, text1]-----|-----[tag2]
+ * |
+ * |
+ * [tag3, text3]
+ *
+ * @hide
+ */
+public class XMLParser extends DefaultHandler {
+ private XMLNode mRoot = null;
+ private XMLNode mCurrent = null;
+
+ public XMLNode parse(String text) throws IOException, SAXException {
+ if (TextUtils.isEmpty(text)) {
+ throw new IOException("XML string not provided");
+ }
+
+ // Reset pointers.
+ mRoot = null;
+ mCurrent = null;
+
+ try {
+ SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+ parser.parse(new InputSource(new StringReader(text)), this);
+ return mRoot;
+ } catch (ParserConfigurationException pce) {
+ throw new SAXException(pce);
+ }
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ XMLNode parent = mCurrent;
+
+ mCurrent = new XMLNode(parent, qName);
+
+ if (mRoot == null) {
+ mRoot = mCurrent;
+ } else if (parent == null) {
+ throw new SAXException("More than one root nodes");
+ } else {
+ parent.addChild(mCurrent);
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (!qName.equals(mCurrent.getTag())) {
+ throw new SAXException("End tag '" + qName + "' doesn't match current node: " +
+ mCurrent);
+ }
+
+ mCurrent.close();
+ mCurrent = mCurrent.getParent();
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ mCurrent.addText(new String(ch, start, length));
+ }
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.aidl b/wifi/java/android/net/wifi/hotspot2/pps/Credential.aidl
new file mode 100644
index 0000000..3d8e833
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.pps;
+
+parcelable Credential;
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
new file mode 100644
index 0000000..92dbd8a
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -0,0 +1,376 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.pps;
+
+import android.net.wifi.ParcelUtil;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+/**
+ * Class representing Credential subtree in the PerProviderSubscription (PPS)
+ * Management Object (MO) tree.
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * In addition to the fields in the Credential subtree, this will also maintain necessary
+ * information for the private key and certificates associated with this credential.
+ *
+ * Currently we only support the nodes that are used by Hotspot 2.0 Release 1.
+ *
+ * @hide
+ */
+public final class Credential implements Parcelable {
+ /**
+ * The realm associated with this credential. It will be used to determine
+ * if this credential can be used to authenticate with a given hotspot by
+ * comparing the realm specified in that hotspot's ANQP element.
+ */
+ public String realm = null;
+
+ /**
+ * Username-password based credential.
+ * Contains the fields under PerProviderSubscription/Credential/UsernamePassword subtree.
+ */
+ public static final class UserCredential implements Parcelable {
+ /**
+ * Username of the credential.
+ */
+ public String username = null;
+
+ /**
+ * Base64-encoded password.
+ */
+ public String password = null;
+
+ /**
+ * EAP (Extensible Authentication Protocol) method type.
+ * Refer to http://www.iana.org/assignments/eap-numbers/eap-numbers.xml#eap-numbers-4
+ * for valid values.
+ * Using Integer.MIN_VALUE to indicate unset value.
+ */
+ public int eapType = Integer.MIN_VALUE;
+
+ /**
+ * Non-EAP inner authentication method.
+ */
+ public String nonEapInnerMethod = null;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(username);
+ dest.writeString(password);
+ dest.writeInt(eapType);
+ dest.writeString(nonEapInnerMethod);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof UserCredential)) {
+ return false;
+ }
+
+ UserCredential that = (UserCredential) thatObject;
+ return TextUtils.equals(username, that.username) &&
+ TextUtils.equals(password, that.password) &&
+ eapType == that.eapType &&
+ TextUtils.equals(nonEapInnerMethod, that.nonEapInnerMethod);
+ }
+
+ public static final Creator<UserCredential> CREATOR =
+ new Creator<UserCredential>() {
+ @Override
+ public UserCredential createFromParcel(Parcel in) {
+ UserCredential userCredential = new UserCredential();
+ userCredential.username = in.readString();
+ userCredential.password = in.readString();
+ userCredential.eapType = in.readInt();
+ userCredential.nonEapInnerMethod = in.readString();
+ return userCredential;
+ }
+
+ @Override
+ public UserCredential[] newArray(int size) {
+ return new UserCredential[size];
+ }
+ };
+ }
+ public UserCredential userCredential = null;
+
+ /**
+ * Certificate based credential.
+ * Contains fields under PerProviderSubscription/Credential/DigitalCertificate subtree.
+ */
+ public static final class CertificateCredential implements Parcelable {
+ /**
+ * Certificate type. Valid values are "802.1ar" and "x509v3".
+ */
+ public String certType = null;
+
+ /**
+ * The SHA-256 fingerprint of the certificate.
+ */
+ public byte[] certSha256FingerPrint = null;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(certType);
+ dest.writeByteArray(certSha256FingerPrint);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof CertificateCredential)) {
+ return false;
+ }
+
+ CertificateCredential that = (CertificateCredential) thatObject;
+ return TextUtils.equals(certType, that.certType) &&
+ Arrays.equals(certSha256FingerPrint, that.certSha256FingerPrint);
+ }
+
+ public static final Creator<CertificateCredential> CREATOR =
+ new Creator<CertificateCredential>() {
+ @Override
+ public CertificateCredential createFromParcel(Parcel in) {
+ CertificateCredential certCredential = new CertificateCredential();
+ certCredential.certType = in.readString();
+ certCredential.certSha256FingerPrint = in.createByteArray();
+ return certCredential;
+ }
+
+ @Override
+ public CertificateCredential[] newArray(int size) {
+ return new CertificateCredential[size];
+ }
+ };
+ }
+ public CertificateCredential certCredential = null;
+
+ /**
+ * SIM (Subscriber Identify Module) based credential.
+ * Contains fields under PerProviderSubscription/Credential/SIM subtree.
+ */
+ public static final class SimCredential implements Parcelable {
+ /**
+ * International Mobile device Subscriber Identity.
+ */
+ public String imsi = null;
+
+ /**
+ * EAP (Extensible Authentication Protocol) method type for using SIM credential.
+ * Refer to http://www.iana.org/assignments/eap-numbers/eap-numbers.xml#eap-numbers-4
+ * for valid values.
+ * Using Integer.MIN_VALUE to indicate unset value.
+ */
+ public int eapType = Integer.MIN_VALUE;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof SimCredential)) {
+ return false;
+ }
+
+ SimCredential that = (SimCredential) thatObject;
+ return TextUtils.equals(imsi, that.imsi) &&
+ eapType == that.eapType;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(imsi);
+ dest.writeInt(eapType);
+ }
+
+ public static final Creator<SimCredential> CREATOR =
+ new Creator<SimCredential>() {
+ @Override
+ public SimCredential createFromParcel(Parcel in) {
+ SimCredential simCredential = new SimCredential();
+ simCredential.imsi = in.readString();
+ simCredential.eapType = in.readInt();
+ return simCredential;
+ }
+
+ @Override
+ public SimCredential[] newArray(int size) {
+ return new SimCredential[size];
+ }
+ };
+ }
+ public SimCredential simCredential = null;
+
+ /**
+ * CA (Certificate Authority) X509 certificate.
+ */
+ public X509Certificate caCertificate = null;
+
+ /**
+ * Client side X509 certificate chain.
+ */
+ public X509Certificate[] clientCertificateChain = null;
+
+ /**
+ * Client side private key.
+ */
+ public PrivateKey clientPrivateKey = null;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(realm);
+ dest.writeParcelable(userCredential, flags);
+ dest.writeParcelable(certCredential, flags);
+ dest.writeParcelable(simCredential, flags);
+ ParcelUtil.writeCertificate(dest, caCertificate);
+ ParcelUtil.writeCertificates(dest, clientCertificateChain);
+ ParcelUtil.writePrivateKey(dest, clientPrivateKey);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof Credential)) {
+ return false;
+ }
+
+ Credential that = (Credential) thatObject;
+ return TextUtils.equals(realm, that.realm) &&
+ (userCredential == null ? that.userCredential == null :
+ userCredential.equals(that.userCredential)) &&
+ (certCredential == null ? that.certCredential == null :
+ certCredential.equals(that.certCredential)) &&
+ (simCredential == null ? that.simCredential == null :
+ simCredential.equals(that.simCredential)) &&
+ isX509CertificateEquals(caCertificate, that.caCertificate) &&
+ isX509CertificatesEquals(clientCertificateChain, that.clientCertificateChain) &&
+ isPrivateKeyEquals(clientPrivateKey, that.clientPrivateKey);
+ }
+
+ public static final Creator<Credential> CREATOR =
+ new Creator<Credential>() {
+ @Override
+ public Credential createFromParcel(Parcel in) {
+ Credential credential = new Credential();
+ credential.realm = in.readString();
+ credential.userCredential = in.readParcelable(null);
+ credential.certCredential = in.readParcelable(null);
+ credential.simCredential = in.readParcelable(null);
+ credential.caCertificate = ParcelUtil.readCertificate(in);
+ credential.clientCertificateChain = ParcelUtil.readCertificates(in);
+ credential.clientPrivateKey = ParcelUtil.readPrivateKey(in);
+ return credential;
+ }
+
+ @Override
+ public Credential[] newArray(int size) {
+ return new Credential[size];
+ }
+ };
+
+ private static boolean isPrivateKeyEquals(PrivateKey key1, PrivateKey key2) {
+ if (key1 == null && key2 == null) {
+ return true;
+ }
+
+ /* Return false if only one of them is null */
+ if (key1 == null || key2 == null) {
+ return false;
+ }
+
+ return TextUtils.equals(key1.getAlgorithm(), key2.getAlgorithm()) &&
+ Arrays.equals(key1.getEncoded(), key2.getEncoded());
+ }
+
+ private static boolean isX509CertificateEquals(X509Certificate cert1, X509Certificate cert2) {
+ if (cert1 == null && cert2 == null) {
+ return true;
+ }
+
+ /* Return false if only one of them is null */
+ if (cert1 == null || cert2 == null) {
+ return false;
+ }
+
+ boolean result = false;
+ try {
+ result = Arrays.equals(cert1.getEncoded(), cert2.getEncoded());
+ } catch (CertificateEncodingException e) {
+ /* empty, return false. */
+ }
+ return result;
+ }
+
+ private static boolean isX509CertificatesEquals(X509Certificate[] certs1,
+ X509Certificate[] certs2) {
+ if (certs1 == null && certs2 == null) {
+ return true;
+ }
+
+ /* Return false if only one of them is null */
+ if (certs1 == null || certs2 == null) {
+ return false;
+ }
+
+ if (certs1.length != certs2.length) {
+ return false;
+ }
+
+ for (int i = 0; i < certs1.length; i++) {
+ if (!isX509CertificateEquals(certs1[i], certs2[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
new file mode 100644
index 0000000..62d5603
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.pps;
+
+parcelable HomeSP;
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
new file mode 100644
index 0000000..2acc8be
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.pps;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import java.util.Arrays;
+
+/**
+ * Class representing HomeSP subtree in PerProviderSubscription (PPS)
+ * Management Object (MO) tree.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * Currently we only support the nodes that are used by Hotspot 2.0 Release 1.
+ *
+ * @hide
+ */
+public final class HomeSP implements Parcelable {
+ /**
+ * FQDN (Fully Qualified Domain Name) of this home service provider.
+ */
+ public String fqdn = null;
+
+ /**
+ * Friendly name of this home service provider.
+ */
+ public String friendlyName = null;
+
+ /**
+ * List of Organization Identifiers (OIs) identifying a roaming consortium of
+ * which this provider is a member.
+ */
+ public long[] roamingConsortiumOIs = null;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(fqdn);
+ dest.writeString(friendlyName);
+ dest.writeLongArray(roamingConsortiumOIs);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof HomeSP)) {
+ return false;
+ }
+ HomeSP that = (HomeSP) thatObject;
+
+ return TextUtils.equals(fqdn, that.fqdn) &&
+ TextUtils.equals(friendlyName, that.friendlyName) &&
+ Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs);
+ }
+
+ public static final Creator<HomeSP> CREATOR =
+ new Creator<HomeSP>() {
+ @Override
+ public HomeSP createFromParcel(Parcel in) {
+ HomeSP homeSp = new HomeSP();
+ homeSp.fqdn = in.readString();
+ homeSp.friendlyName = in.readString();
+ homeSp.roamingConsortiumOIs = in.createLongArray();
+ return homeSp;
+ }
+
+ @Override
+ public HomeSP[] newArray(int size) {
+ return new HomeSP[size];
+ }
+ };
+}
diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml
new file mode 100644
index 0000000..53d38ad
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription.xml
@@ -0,0 +1,80 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+ <VerDTD>1.2</VerDTD>
+ <Node>
+ <NodeName>PerProviderSubscription</NodeName>
+ <RTProperties>
+ <Type>
+ <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+ </Type>
+ </RTProperties>
+ <Node>
+ <NodeName>i001</NodeName>
+ <Node>
+ <NodeName>HomeSP</NodeName>
+ <Node>
+ <NodeName>FriendlyName</NodeName>
+ <Value>Century House</Value>
+ </Node>
+ <Node>
+ <NodeName>FQDN</NodeName>
+ <Value>mi6.co.uk</Value>
+ </Node>
+ <Node>
+ <NodeName>RoamingConsortiumOI</NodeName>
+ <Value>112233,445566</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>Credential</NodeName>
+ <Node>
+ <NodeName>Realm</NodeName>
+ <Value>shaken.stirred.com</Value>
+ </Node>
+ <Node>
+ <NodeName>UsernamePassword</NodeName>
+ <Node>
+ <NodeName>Username</NodeName>
+ <Value>james</Value>
+ </Node>
+ <Node>
+ <NodeName>Password</NodeName>
+ <Value>Ym9uZDAwNw==</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPMethod</NodeName>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>21</Value>
+ </Node>
+ <Node>
+ <NodeName>InnerMethod</NodeName>
+ <Value>MS-CHAP-V2</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>DigitalCertificate</NodeName>
+ <Node>
+ <NodeName>CertificateType</NodeName>
+ <Value>x509v3</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256FingerPrint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SIM</NodeName>
+ <Node>
+ <NodeName>IMSI</NodeName>
+ <Value>imsi</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>24</Value>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_DuplicateHomeSP.xml b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateHomeSP.xml
new file mode 100644
index 0000000..e13eb2a
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateHomeSP.xml
@@ -0,0 +1,95 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+ <VerDTD>1.2</VerDTD>
+ <Node>
+ <NodeName>PerProviderSubscription</NodeName>
+ <RTProperties>
+ <Type>
+ <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+ </Type>
+ </RTProperties>
+ <Node>
+ <NodeName>i001</NodeName>
+ <Node>
+ <NodeName>HomeSP</NodeName>
+ <Node>
+ <NodeName>FriendlyName</NodeName>
+ <Value>Century House</Value>
+ </Node>
+ <Node>
+ <NodeName>FQDN</NodeName>
+ <Value>mi6.co.uk</Value>
+ </Node>
+ <Node>
+ <NodeName>RoamingConsortiumOI</NodeName>
+ <Value>112233,445566</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>HomeSP</NodeName>
+ <Node>
+ <NodeName>FriendlyName</NodeName>
+ <Value>Century House</Value>
+ </Node>
+ <Node>
+ <NodeName>FQDN</NodeName>
+ <Value>mi6.co.uk</Value>
+ </Node>
+ <Node>
+ <NodeName>RoamingConsortiumOI</NodeName>
+ <Value>112233,445566</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>Credential</NodeName>
+ <Node>
+ <NodeName>Realm</NodeName>
+ <Value>shaken.stirred.com</Value>
+ </Node>
+ <Node>
+ <NodeName>UsernamePassword</NodeName>
+ <Node>
+ <NodeName>Username</NodeName>
+ <Value>james</Value>
+ </Node>
+ <Node>
+ <NodeName>Password</NodeName>
+ <Value>Ym9uZDAwNw==</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPMethod</NodeName>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>21</Value>
+ </Node>
+ <Node>
+ <NodeName>InnerMethod</NodeName>
+ <Value>MS-CHAP-V2</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>DigitalCertificate</NodeName>
+ <Node>
+ <NodeName>CertificateType</NodeName>
+ <Value>x509v3</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256FingerPrint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SIM</NodeName>
+ <Node>
+ <NodeName>IMSI</NodeName>
+ <Value>imsi</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>24</Value>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_DuplicateValue.xml b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateValue.xml
new file mode 100644
index 0000000..8719ffa
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateValue.xml
@@ -0,0 +1,81 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+ <VerDTD>1.2</VerDTD>
+ <Node>
+ <NodeName>PerProviderSubscription</NodeName>
+ <RTProperties>
+ <Type>
+ <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+ </Type>
+ </RTProperties>
+ <Node>
+ <NodeName>i001</NodeName>
+ <Node>
+ <NodeName>HomeSP</NodeName>
+ <Node>
+ <NodeName>FriendlyName</NodeName>
+ <Value>Century House</Value>
+ <Value>Century House</Value>
+ </Node>
+ <Node>
+ <NodeName>FQDN</NodeName>
+ <Value>mi6.co.uk</Value>
+ </Node>
+ <Node>
+ <NodeName>RoamingConsortiumOI</NodeName>
+ <Value>112233,445566</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>Credential</NodeName>
+ <Node>
+ <NodeName>Realm</NodeName>
+ <Value>shaken.stirred.com</Value>
+ </Node>
+ <Node>
+ <NodeName>UsernamePassword</NodeName>
+ <Node>
+ <NodeName>Username</NodeName>
+ <Value>james</Value>
+ </Node>
+ <Node>
+ <NodeName>Password</NodeName>
+ <Value>Ym9uZDAwNw==</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPMethod</NodeName>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>21</Value>
+ </Node>
+ <Node>
+ <NodeName>InnerMethod</NodeName>
+ <Value>MS-CHAP-V2</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>DigitalCertificate</NodeName>
+ <Node>
+ <NodeName>CertificateType</NodeName>
+ <Value>x509v3</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256FingerPrint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SIM</NodeName>
+ <Node>
+ <NodeName>IMSI</NodeName>
+ <Value>imsi</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>24</Value>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_InvalidName.xml b/wifi/tests/assets/pps/PerProviderSubscription_InvalidName.xml
new file mode 100644
index 0000000..c761237
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_InvalidName.xml
@@ -0,0 +1,80 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+ <VerDTD>1.2</VerDTD>
+ <Node>
+ <NodeName>PerProviderSubscription</NodeName>
+ <RTProperties>
+ <Type>
+ <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+ </Type>
+ </RTProperties>
+ <Node>
+ <NodeName>i001</NodeName>
+ <Node>
+ <NodeName>HomeSP</NodeName>
+ <Node>
+ <NodeName>FriendName</NodeName>
+ <Value>Century House</Value>
+ </Node>
+ <Node>
+ <NodeName>FQDN</NodeName>
+ <Value>mi6.co.uk</Value>
+ </Node>
+ <Node>
+ <NodeName>RoamingConsortiumOI</NodeName>
+ <Value>112233,445566</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>Credential</NodeName>
+ <Node>
+ <NodeName>Realm</NodeName>
+ <Value>shaken.stirred.com</Value>
+ </Node>
+ <Node>
+ <NodeName>UsernamePassword</NodeName>
+ <Node>
+ <NodeName>Username</NodeName>
+ <Value>james</Value>
+ </Node>
+ <Node>
+ <NodeName>Password</NodeName>
+ <Value>Ym9uZDAwNw==</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPMethod</NodeName>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>21</Value>
+ </Node>
+ <Node>
+ <NodeName>InnerMethod</NodeName>
+ <Value>MS-CHAP-V2</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>DigitalCertificate</NodeName>
+ <Node>
+ <NodeName>CertificateType</NodeName>
+ <Value>x509v3</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256FingerPrint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SIM</NodeName>
+ <Node>
+ <NodeName>IMSI</NodeName>
+ <Value>imsi</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>24</Value>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_InvalidNode.xml b/wifi/tests/assets/pps/PerProviderSubscription_InvalidNode.xml
new file mode 100644
index 0000000..6b807af
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_InvalidNode.xml
@@ -0,0 +1,84 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+ <VerDTD>1.2</VerDTD>
+ <Node>
+ <NodeName>PerProviderSubscription</NodeName>
+ <RTProperties>
+ <Type>
+ <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+ </Type>
+ </RTProperties>
+ <Node>
+ <NodeName>i001</NodeName>
+ <Node>
+ <NodeName>HomeSP</NodeName>
+ <Node>
+ <NodeName>FriendlyName</NodeName>
+ <Value>Century House</Value>
+ </Node>
+ <Node>
+ <NodeName>FQDN</NodeName>
+ <Value>mi6.co.uk</Value>
+ <Node>
+ <NodeName>InvalidNode</NodeName>
+ <Value>Test</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>RoamingConsortiumOI</NodeName>
+ <Value>112233,445566</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>Credential</NodeName>
+ <Node>
+ <NodeName>Realm</NodeName>
+ <Value>shaken.stirred.com</Value>
+ </Node>
+ <Node>
+ <NodeName>UsernamePassword</NodeName>
+ <Node>
+ <NodeName>Username</NodeName>
+ <Value>james</Value>
+ </Node>
+ <Node>
+ <NodeName>Password</NodeName>
+ <Value>Ym9uZDAwNw==</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPMethod</NodeName>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>21</Value>
+ </Node>
+ <Node>
+ <NodeName>InnerMethod</NodeName>
+ <Value>MS-CHAP-V2</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>DigitalCertificate</NodeName>
+ <Node>
+ <NodeName>CertificateType</NodeName>
+ <Value>x509v3</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256FingerPrint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SIM</NodeName>
+ <Node>
+ <NodeName>IMSI</NodeName>
+ <Value>imsi</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>24</Value>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_MissingName.xml b/wifi/tests/assets/pps/PerProviderSubscription_MissingName.xml
new file mode 100644
index 0000000..ed06b47
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_MissingName.xml
@@ -0,0 +1,79 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+ <VerDTD>1.2</VerDTD>
+ <Node>
+ <NodeName>PerProviderSubscription</NodeName>
+ <RTProperties>
+ <Type>
+ <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+ </Type>
+ </RTProperties>
+ <Node>
+ <NodeName>i001</NodeName>
+ <Node>
+ <Node>
+ <NodeName>FriendlyName</NodeName>
+ <Value>Century House</Value>
+ </Node>
+ <Node>
+ <NodeName>FQDN</NodeName>
+ <Value>mi6.co.uk</Value>
+ </Node>
+ <Node>
+ <NodeName>RoamingConsortiumOI</NodeName>
+ <Value>112233,445566</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>Credential</NodeName>
+ <Node>
+ <NodeName>Realm</NodeName>
+ <Value>shaken.stirred.com</Value>
+ </Node>
+ <Node>
+ <NodeName>UsernamePassword</NodeName>
+ <Node>
+ <NodeName>Username</NodeName>
+ <Value>james</Value>
+ </Node>
+ <Node>
+ <NodeName>Password</NodeName>
+ <Value>Ym9uZDAwNw==</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPMethod</NodeName>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>21</Value>
+ </Node>
+ <Node>
+ <NodeName>InnerMethod</NodeName>
+ <Value>MS-CHAP-V2</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>DigitalCertificate</NodeName>
+ <Node>
+ <NodeName>CertificateType</NodeName>
+ <Value>x509v3</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256FingerPrint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SIM</NodeName>
+ <Node>
+ <NodeName>IMSI</NodeName>
+ <Value>imsi</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>24</Value>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/PerProviderSubscription_MissingValue.xml b/wifi/tests/assets/pps/PerProviderSubscription_MissingValue.xml
new file mode 100644
index 0000000..f7e35dd
--- /dev/null
+++ b/wifi/tests/assets/pps/PerProviderSubscription_MissingValue.xml
@@ -0,0 +1,79 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+ <VerDTD>1.2</VerDTD>
+ <Node>
+ <NodeName>PerProviderSubscription</NodeName>
+ <RTProperties>
+ <Type>
+ <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+ </Type>
+ </RTProperties>
+ <Node>
+ <NodeName>i001</NodeName>
+ <Node>
+ <NodeName>HomeSP</NodeName>
+ <Node>
+ <NodeName>FriendlyName</NodeName>
+ </Node>
+ <Node>
+ <NodeName>FQDN</NodeName>
+ <Value>mi6.co.uk</Value>
+ </Node>
+ <Node>
+ <NodeName>RoamingConsortiumOI</NodeName>
+ <Value>112233,445566</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>Credential</NodeName>
+ <Node>
+ <NodeName>Realm</NodeName>
+ <Value>shaken.stirred.com</Value>
+ </Node>
+ <Node>
+ <NodeName>UsernamePassword</NodeName>
+ <Node>
+ <NodeName>Username</NodeName>
+ <Value>james</Value>
+ </Node>
+ <Node>
+ <NodeName>Password</NodeName>
+ <Value>Ym9uZDAwNw==</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPMethod</NodeName>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>21</Value>
+ </Node>
+ <Node>
+ <NodeName>InnerMethod</NodeName>
+ <Value>MS-CHAP-V2</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>DigitalCertificate</NodeName>
+ <Node>
+ <NodeName>CertificateType</NodeName>
+ <Value>x509v3</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256FingerPrint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SIM</NodeName>
+ <Node>
+ <NodeName>IMSI</NodeName>
+ <Value>imsi</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>24</Value>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+</MgmtTree>
diff --git a/wifi/tests/assets/pps/README.txt b/wifi/tests/assets/pps/README.txt
new file mode 100644
index 0000000..369c0a9
--- /dev/null
+++ b/wifi/tests/assets/pps/README.txt
@@ -0,0 +1,7 @@
+PerProviderSubscription.xml - valid PPS XML file
+PerProviderSubscription_DuplicateHomeSP.xml - containing multiple HomeSP node
+PerProviderSubscription_DuplicateValue.xml - FriendlyName node contains multiple Value
+PerProviderSubscription_MissingValue.xml - FriendlyName node is missing Value
+PerProviderSubscription_MissingName.xml - HomeSP node is missing NodeName
+PerProviderSubscription_InvalidNode.xml - FQDN node contains both Value and a child node
+PerProviderSubscription_InvalidName.xml - FriendlyName node have a typo in its name
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
new file mode 100644
index 0000000..be11f0e
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.net.wifi.hotspot2;
+
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.PasspointConfiguration}.
+ */
+@SmallTest
+public class PasspointConfigurationTest {
+
+ private static HomeSP createHomeSp() {
+ HomeSP homeSp = new HomeSP();
+ homeSp.fqdn = "fqdn";
+ homeSp.friendlyName = "friendly name";
+ homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66};
+ return homeSp;
+ }
+
+ private static Credential createCredential() {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ cred.userCredential = null;
+ cred.certCredential = null;
+ cred.simCredential = null;
+ cred.caCertificate = null;
+ cred.clientCertificateChain = null;
+ cred.clientPrivateKey = null;
+ return cred;
+ }
+
+ private static void verifyParcel(PasspointConfiguration writeConfig) throws Exception {
+ Parcel parcel = Parcel.obtain();
+ writeConfig.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ PasspointConfiguration readConfig =
+ PasspointConfiguration.CREATOR.createFromParcel(parcel);
+ assertTrue(readConfig.equals(writeConfig));
+ }
+
+ @Test
+ public void verifyParcelWithDefault() throws Exception {
+ verifyParcel(new PasspointConfiguration());
+ }
+
+ @Test
+ public void verifyParcelWithHomeSPAndCredential() throws Exception {
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = createHomeSp();
+ config.credential = createCredential();
+ verifyParcel(config);
+ }
+
+ @Test
+ public void verifyParcelWithHomeSPOnly() throws Exception {
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = createHomeSp();
+ verifyParcel(config);
+ }
+
+ @Test
+ public void verifyParcelWithCredentialOnly() throws Exception {
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.credential = createCredential();
+ verifyParcel(config);
+ }
+}
\ No newline at end of file
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
new file mode 100644
index 0000000..10b0267
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.omadm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.hotspot2.omadm.PPSMOParser;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.omadm.PPSMOParser}.
+ */
+@SmallTest
+public class PPSMOParserTest {
+ private static final String VALID_PPS_MO_XML_FILE = "assets/pps/PerProviderSubscription.xml";
+ private static final String PPS_MO_XML_FILE_DUPLICATE_HOMESP =
+ "assets/pps/PerProviderSubscription_DuplicateHomeSP.xml";
+ private static final String PPS_MO_XML_FILE_DUPLICATE_VALUE =
+ "assets/pps/PerProviderSubscription_DuplicateValue.xml";
+ private static final String PPS_MO_XML_FILE_MISSING_VALUE =
+ "assets/pps/PerProviderSubscription_MissingValue.xml";
+ private static final String PPS_MO_XML_FILE_MISSING_NAME =
+ "assets/pps/PerProviderSubscription_MissingName.xml";
+ private static final String PPS_MO_XML_FILE_INVALID_NODE =
+ "assets/pps/PerProviderSubscription_InvalidNode.xml";
+ private static final String PPS_MO_XML_FILE_INVALID_NAME =
+ "assets/pps/PerProviderSubscription_InvalidName.xml";
+
+ /**
+ * Read the content of the given resource file into a String.
+ *
+ * @param filename String name of the file
+ * @return String
+ * @throws IOException
+ */
+ private String loadResourceFile(String filename) throws IOException {
+ InputStream in = getClass().getClassLoader().getResourceAsStream(filename);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ StringBuilder builder = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ builder.append(line).append("\n");
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Generate a {@link PasspointConfiguration} that matches the configuration specified in the
+ * XML file {@link #VALID_PPS_MO_XML_FILE}.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ private PasspointConfiguration generateConfigurationFromPPSMOTree() {
+ PasspointConfiguration config = new PasspointConfiguration();
+
+ // HomeSP configuration.
+ config.homeSp = new HomeSP();
+ config.homeSp.friendlyName = "Century House";
+ config.homeSp.fqdn = "mi6.co.uk";
+ config.homeSp.roamingConsortiumOIs = new long[] {0x112233L, 0x445566L};
+
+ // Credential configuration.
+ config.credential = new Credential();
+ config.credential.realm = "shaken.stirred.com";
+ config.credential.userCredential = new Credential.UserCredential();
+ config.credential.userCredential.username = "james";
+ config.credential.userCredential.password = "Ym9uZDAwNw==";
+ config.credential.userCredential.eapType = 21;
+ config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2";
+ config.credential.certCredential = new Credential.CertificateCredential();
+ config.credential.certCredential.certType = "x509v3";
+ config.credential.certCredential.certSha256FingerPrint = new byte[32];
+ Arrays.fill(config.credential.certCredential.certSha256FingerPrint, (byte)0x1f);
+ config.credential.simCredential = new Credential.SimCredential();
+ config.credential.simCredential.imsi = "imsi";
+ config.credential.simCredential.eapType = 24;
+ return config;
+ }
+
+ /**
+ * Parse and verify all supported fields under PPS MO tree (currently only fields under
+ * HomeSP and Credential subtree).
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseValidPPSMOTree() throws Exception {
+ String ppsMoTree = loadResourceFile(VALID_PPS_MO_XML_FILE);
+ PasspointConfiguration expectedConfig = generateConfigurationFromPPSMOTree();
+ PasspointConfiguration actualConfig = PPSMOParser.parseMOText(ppsMoTree);
+ assertTrue(actualConfig.equals(expectedConfig));
+ }
+
+ @Test
+ public void parseNullPPSMOTree() throws Exception {
+ assertEquals(null, PPSMOParser.parseMOText(null));
+ }
+
+ @Test
+ public void parseEmptyPPSMOTree() throws Exception {
+ assertEquals(null, PPSMOParser.parseMOText(new String()));
+ }
+
+ @Test
+ public void parsePPSMOTreeWithDuplicateHomeSP() throws Exception {
+ assertEquals(null, PPSMOParser.parseMOText(
+ loadResourceFile(PPS_MO_XML_FILE_DUPLICATE_HOMESP)));
+ }
+
+ @Test
+ public void parsePPSMOTreeWithDuplicateValue() throws Exception {
+ assertEquals(null, PPSMOParser.parseMOText(
+ loadResourceFile(PPS_MO_XML_FILE_DUPLICATE_VALUE)));
+ }
+
+ @Test
+ public void parsePPSMOTreeWithMissingValue() throws Exception {
+ assertEquals(null, PPSMOParser.parseMOText(
+ loadResourceFile(PPS_MO_XML_FILE_MISSING_VALUE)));
+ }
+
+ @Test
+ public void parsePPSMOTreeWithMissingName() throws Exception {
+ assertEquals(null, PPSMOParser.parseMOText(
+ loadResourceFile(PPS_MO_XML_FILE_MISSING_NAME)));
+ }
+
+ @Test
+ public void parsePPSMOTreeWithInvalidNode() throws Exception {
+ assertEquals(null, PPSMOParser.parseMOText(
+ loadResourceFile(PPS_MO_XML_FILE_INVALID_NODE)));
+ }
+
+ @Test
+ public void parsePPSMOTreeWithInvalidName() throws Exception {
+ assertEquals(null, PPSMOParser.parseMOText(
+ loadResourceFile(PPS_MO_XML_FILE_INVALID_NAME)));
+ }
+}
+
+
+
+
+
+
+
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
new file mode 100644
index 0000000..c2dcec6
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.omadm;
+
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.hotspot2.omadm.XMLNode;
+import android.net.wifi.hotspot2.omadm.XMLParser;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.omadm.XMLParser}.
+ */
+@SmallTest
+public class XMLParserTest {
+ XMLParser mParser;
+
+ private static XMLNode createNode(XMLNode parent, String tag, String text) {
+ XMLNode node = new XMLNode(parent, tag);
+ node.addText(text);
+ if (parent != null)
+ parent.addChild(node);
+ node.close();
+ return node;
+ }
+
+ /**
+ * Setup before tests.
+ */
+ @Before
+ public void setUp() throws Exception {
+ mParser = new XMLParser();
+ }
+
+ @Test(expected = IOException.class)
+ public void parseNullXML() throws Exception {
+ mParser.parse(null);
+ }
+
+ @Test(expected = IOException.class)
+ public void parseEmptyXML() throws Exception {
+ mParser.parse(new String());
+ }
+
+ @Test(expected = SAXException.class)
+ public void parseMalformedXML() throws Exception {
+ String malformedXmlTree = "<root><child1>test1</child2></root>";
+ mParser.parse(malformedXmlTree);
+ }
+
+ @Test
+ public void parseValidXMLTree() throws Exception {
+ String xmlTree = "<root><child1>test1</child1><child2>test2</child2></root>";
+
+ // Construct the expected XML tree.
+ XMLNode expectedRoot = createNode(null, "root", "");
+ createNode(expectedRoot, "child1", "test1");
+ createNode(expectedRoot, "child2", "test2");
+
+ XMLNode actualRoot = mParser.parse(xmlTree);
+ assertTrue(actualRoot.equals(expectedRoot));
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
new file mode 100644
index 0000000..68ac4ef
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.net.wifi.hotspot2.pps;
+
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.FakeKeys;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.pps.CredentialTest}.
+ */
+@SmallTest
+public class CredentialTest {
+ private static Credential createCredential(Credential.UserCredential userCred,
+ Credential.CertificateCredential certCred,
+ Credential.SimCredential simCred,
+ X509Certificate caCert,
+ X509Certificate[] clientCertificateChain,
+ PrivateKey clientPrivateKey) {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ cred.userCredential = userCred;
+ cred.certCredential = certCred;
+ cred.simCredential = simCred;
+ cred.caCertificate = caCert;
+ cred.clientCertificateChain = clientCertificateChain;
+ cred.clientPrivateKey = clientPrivateKey;
+ return cred;
+ }
+
+ private static Credential createCredentialWithCertificateCredential() {
+ Credential.CertificateCredential certCred = new Credential.CertificateCredential();
+ certCred.certType = "x509v3";
+ certCred.certSha256FingerPrint = new byte[256];
+ return createCredential(null, certCred, null, FakeKeys.CA_CERT0,
+ new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
+ }
+
+ private static Credential createCredentialWithSimCredential() {
+ Credential.SimCredential simCred = new Credential.SimCredential();
+ simCred.imsi = "imsi";
+ simCred.eapType = 1;
+ return createCredential(null, null, simCred, null, null, null);
+ }
+
+ private static Credential createCredentialWithUserCredential() {
+ Credential.UserCredential userCred = new Credential.UserCredential();
+ userCred.username = "username";
+ userCred.password = "password";
+ userCred.eapType = 1;
+ userCred.nonEapInnerMethod = "MS-CHAP";
+ return createCredential(userCred, null, null, FakeKeys.CA_CERT0,
+ new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
+ }
+
+ private static void verifyParcel(Credential writeCred) {
+ Parcel parcel = Parcel.obtain();
+ writeCred.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ Credential readCred = Credential.CREATOR.createFromParcel(parcel);
+ assertTrue(readCred.equals(writeCred));
+ }
+
+ @Test
+ public void verifyParcelWithDefault() throws Exception {
+ verifyParcel(new Credential());
+ }
+
+ @Test
+ public void verifyParcelWithCertificateCredential() throws Exception {
+ verifyParcel(createCredentialWithCertificateCredential());
+ }
+
+ @Test
+ public void verifyParcelWithSimCredential() throws Exception {
+ verifyParcel(createCredentialWithSimCredential());
+ }
+
+ @Test
+ public void verifyParcelWithUserCredential() throws Exception {
+ verifyParcel(createCredentialWithUserCredential());
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java
new file mode 100644
index 0000000..0d2da64
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.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.net.wifi.hotspot2.pps;
+
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.HashMap;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.pps.HomeSP}.
+ */
+@SmallTest
+public class HomeSPTest {
+ private static HomeSP createHomeSp() {
+ HomeSP homeSp = new HomeSP();
+ homeSp.fqdn = "fqdn";
+ homeSp.friendlyName = "friendly name";
+ homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66};
+ return homeSp;
+ }
+
+ private static void verifyParcel(HomeSP writeHomeSp) throws Exception {
+ Parcel parcel = Parcel.obtain();
+ writeHomeSp.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ HomeSP readHomeSp = HomeSP.CREATOR.createFromParcel(parcel);
+ assertTrue(readHomeSp.equals(writeHomeSp));
+ }
+
+ @Test
+ public void verifyParcelWithEmptyHomeSP() throws Exception {
+ verifyParcel(new HomeSP());
+ }
+
+ @Test
+ public void verifyParcelWithValidHomeSP() throws Exception {
+ verifyParcel(createHomeSp());
+ }
+}