Merge "Re-order draw methods to make update-api happy"
diff --git a/Android.mk b/Android.mk
index 4fea0d4..5fef91e 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 \
@@ -597,6 +598,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/api/system-current.txt b/api/system-current.txt
index 7805ae3..573b1f2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -37556,8 +37556,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 {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index d91472b..112b614b 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);
@@ -1421,7 +1421,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 +1434,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 +1497,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 +2061,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();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3a8b6c7..fbbfec3 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);
}
}
@@ -996,43 +998,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 +1182,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 +1263,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 +1331,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);
}
}
@@ -3097,8 +3111,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 +3144,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 +4919,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 +4947,7 @@
}
break;
}
- case IApplicationThread.PACKAGE_REPLACED:
+ case ApplicationThreadConstants.PACKAGE_REPLACED:
{
if (packages == null) {
break;
@@ -5232,10 +5247,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/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/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
new file mode 100644
index 0000000..2dd3b1a
--- /dev/null
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -0,0 +1,162 @@
+/*
+ * 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;
+ /**
+ * 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/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/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 38788dc..111085d 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2060,7 +2060,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;
@@ -2082,7 +2082,7 @@
}
public void onBluetoothServiceDown() {
- if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
+ if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
try {
mServiceLock.writeLock().lock();
@@ -2110,7 +2110,7 @@
}
public void onBrEdrDown() {
- if (VDBG) Log.i(TAG, "on QBrEdrDown: ");
+ if (DBG) Log.i(TAG, "onBrEdrDown:");
}
};
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/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/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/provider/Settings.java b/core/java/android/provider/Settings.java
index 0f4afae..875b9af 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8062,11 +8062,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/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/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 9ed1588..6e3fbf7 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -548,7 +548,7 @@
bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType,
GraphicsJNI::defaultColorSpace()));
- PixelRef* nativeBitmap = GraphicsJNI::allocateHeapPixelRef(&bitmap, NULL);
+ sk_sp<PixelRef> nativeBitmap = PixelRef::allocateHeapPixelRef(&bitmap, NULL);
if (!nativeBitmap) {
return NULL;
}
@@ -558,8 +558,7 @@
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,
@@ -832,7 +831,7 @@
}
// Map the bitmap in place from the ashmem region if possible otherwise copy.
- PixelRef* nativeBitmap;
+ sk_sp<PixelRef> 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 +852,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<PixelRef>(GraphicsJNI::mapAshmemPixelRef(env, bitmap.get(),
+ ctable, dupFd, const_cast<void*>(blob.data()), size, !isMutable));
SkSafeUnref(ctable);
if (!nativeBitmap) {
close(dupFd);
@@ -880,7 +879,7 @@
#endif
// Copy the pixels into a new buffer.
- nativeBitmap = GraphicsJNI::allocateHeapPixelRef(bitmap.get(), ctable);
+ nativeBitmap = PixelRef::allocateHeapPixelRef(bitmap.get(), ctable);
SkSafeUnref(ctable);
if (!nativeBitmap) {
blob.release();
@@ -895,7 +894,7 @@
blob.release();
}
- return createBitmap(env, nativeBitmap,
+ return createBitmap(env, nativeBitmap.release(),
getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 82b70fc..a866bae 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -399,160 +399,8 @@
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,
SkColorTable* ctable, int fd, void* addr, size_t size, bool readOnly) {
const SkImageInfo& info = bitmap->info();
@@ -597,7 +445,7 @@
///////////////////////////////////////////////////////////////////////////////
bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
- mStorage.reset(GraphicsJNI::allocateHeapPixelRef(bitmap, ctable));
+ mStorage = android::PixelRef::allocateHeapPixelRef(bitmap, ctable);
return !!mStorage;
}
@@ -702,8 +550,7 @@
}
bool AshmemPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
- JNIEnv* env = vm2env(mJavaVM);
- mStorage.reset(GraphicsJNI::allocateAshmemPixelRef(env, bitmap, ctable));
+ mStorage = android::PixelRef::allocateAshmemPixelRef(bitmap, ctable);
return !!mStorage;
}
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index cf617bd..343ed42 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -72,11 +72,6 @@
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,
SkColorTable* ctable, int fd, void* addr, size_t size, bool readOnly);
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 776175f..c5b9d64 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -446,7 +446,8 @@
GraphicsJNI::defaultColorSpace());
SkBitmap bitmap;
bitmap.setInfo(info);
- if (!GraphicsJNI::allocatePixels(env, &bitmap, NULL)) {
+ sk_sp<PixelRef> pixelRef = PixelRef::allocateHeapPixelRef(&bitmap, NULL);
+ if (!pixelRef) {
return;
}
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 6dd7a6a..bc2604e 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);
@@ -1170,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,
@@ -1234,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,
@@ -1299,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/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/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/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/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/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/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/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 09d5252..a4c1d1b 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();
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 4483e1b..627a1af 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.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
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index aac9036..017af2a 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -48,7 +48,8 @@
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");
}
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 cb9056f..84eced8 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -74,7 +74,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 +99,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 +129,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;
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index a52abfc..f172473 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -65,23 +65,6 @@
bounds->mBottom = skBounds.fBottom;
}
-const void* MinikinFontSkia::GetTable(uint32_t tag, size_t* size,
- minikin::MinikinDestroyFunc* destroy) {
- // we don't have a buffer to the font data, copy to own buffer
- const size_t tableSize = mTypeface->getTableSize(tag);
- *size = tableSize;
- if (tableSize == 0) {
- return nullptr;
- }
- void* buf = malloc(tableSize);
- if (buf == nullptr) {
- return nullptr;
- }
- mTypeface->getTableData(tag, 0, tableSize, buf);
- *destroy = free;
- return buf;
-}
-
SkTypeface *MinikinFontSkia::GetSkTypeface() const {
return mTypeface.get();
}
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index 1ea99fd..3ee916c 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -37,8 +37,6 @@
void GetBounds(minikin::MinikinRect* bounds, uint32_t glyph_id,
const minikin::MinikinPaint &paint) const;
- const void* GetTable(uint32_t tag, size_t* size, minikin::MinikinDestroyFunc* destroy);
-
SkTypeface* GetSkTypeface() const;
sk_sp<SkTypeface> RefSkTypeface() const;
diff --git a/libs/hwui/hwui/PixelRef.cpp b/libs/hwui/hwui/PixelRef.cpp
index 795de6b..949ee65 100644
--- a/libs/hwui/hwui/PixelRef.cpp
+++ b/libs/hwui/hwui/PixelRef.cpp
@@ -23,6 +23,84 @@
namespace android {
+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;
+}
+
+typedef sk_sp<PixelRef> (*AllocPixeRef)(size_t allocSize, const SkImageInfo& info, size_t rowBytes,
+ SkColorTable* ctable);
+
+static sk_sp<PixelRef> allocatePixelRef(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;
+ 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();
+ 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<PixelRef> PixelRef::allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
+ return allocatePixelRef(bitmap, ctable, &PixelRef::allocateHeapPixelRef);
+}
+
+sk_sp<PixelRef> PixelRef::allocateAshmemPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
+ return allocatePixelRef(bitmap, ctable, &PixelRef::allocateAshmemPixelRef);
+}
+
+sk_sp<PixelRef> PixelRef::allocateHeapPixelRef(size_t size, const SkImageInfo& info, size_t rowBytes,
+ SkColorTable* ctable) {
+ void* addr = calloc(size, 1);
+ if (!addr) {
+ return nullptr;
+ }
+ return sk_sp<PixelRef>(new PixelRef(addr, size, info, rowBytes, ctable));
+}
+
+sk_sp<PixelRef> PixelRef::allocateAshmemPixelRef(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<PixelRef>(new PixelRef(addr, fd, size, info, rowBytes, ctable));
+}
+
void PixelRef::reconfigure(const SkImageInfo& newInfo, size_t rowBytes, SkColorTable* ctable) {
if (kIndex_8_SkColorType != newInfo.colorType()) {
ctable = nullptr;
diff --git a/libs/hwui/hwui/PixelRef.h b/libs/hwui/hwui/PixelRef.h
index 3f8aea6e..0baf4b8 100644
--- a/libs/hwui/hwui/PixelRef.h
+++ b/libs/hwui/hwui/PixelRef.h
@@ -33,6 +33,14 @@
class ANDROID_API PixelRef : public SkPixelRef {
public:
+ static sk_sp<PixelRef> allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
+ static sk_sp<PixelRef> allocateHeapPixelRef(size_t allocSize, const SkImageInfo& info,
+ size_t rowBytes, SkColorTable* ctable);
+
+ static sk_sp<PixelRef> allocateAshmemPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
+ static sk_sp<PixelRef> allocateAshmemPixelRef(size_t allocSize, const SkImageInfo& info,
+ size_t rowBytes, SkColorTable* ctable);
+
PixelRef(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes,
SkColorTable* ctable);
PixelRef(void* address, void* context, FreeFunc freeFunc,
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 51c0a05..168c23d 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/PixelRef.h>
#include <renderstate/RenderState.h>
#include <renderthread/RenderThread.h>
#include <Snapshot.h>
@@ -128,7 +129,7 @@
SkImageInfo info = SkImageInfo::Make(width, height,
colorType, kPremul_SkAlphaType, colorSpace);
bitmap.setInfo(info);
- bitmap.allocPixels(info);
+ PixelRef::allocateHeapPixelRef(&bitmap, nullptr);
return bitmap;
}
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index 1f4788a..49c4da6e 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());
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/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/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 672f88d..977ac18 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -222,4 +222,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/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index afc524c..3084b9b 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 = 133;
private final int mUserId;
@@ -2479,6 +2479,19 @@
currentVersion = 132;
}
+ if (currentVersion == 132) {
+ // Version 132: 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 = 133;
+ }
+
if (currentVersion != newVersion) {
Slog.wtf("SettingsProvider", "warning: upgrading settings database to version "
+ newVersion + " left it at "
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 6cbb5c6..0dc05e6 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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 cc41f0f..755438e 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -570,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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-be-rBY/strings.xml b/packages/SystemUI/res/values-be-rBY/strings.xml
index 9f23d02..207e223 100644
--- a/packages/SystemUI/res/values-be-rBY/strings.xml
+++ b/packages/SystemUI/res/values-be-rBY/strings.xml
@@ -568,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 3065bee..5e1ca4b 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 54d2816..5ccf4a0 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -564,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index b9bbea0..4efffc56 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 6f1f28c..c71d67e 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 7f2e2a0..47a49da 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 7b65a92..c5e2500e 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 7032677..17b6802 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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 068c2ca..cb691c5 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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 d88f37f..97f5f5e 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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 511872a..03e599c 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -566,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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 07b0d50..0ae421d 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -564,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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 b9c96b6..6ad19f9 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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 07b0d50..0ae421d 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -564,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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 316c7cf..70ef6ea 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -566,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 0951594..43aa36d 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index eae7400..886a138 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -568,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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 e022165..78ad148 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -564,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index d85fcbf..6be736a 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -562,8 +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>
- <!-- no translation found for keyboard_shortcut_group_applications_sms (638701213803242744) -->
- <skip />
+ <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/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/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index f7d61835..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/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/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 517dbe3..cebde98 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2578,6 +2578,21 @@
// 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/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/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 3ad71ea..067f932 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
@@ -275,6 +275,7 @@
mContext.registerReceiver(mReceiver, filter);
loadStoredNameAndAddress();
if (isBluetoothPersistedStateOn()) {
+ if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
mEnableExternal = true;
}
@@ -301,8 +302,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 +321,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);
@@ -1430,7 +1434,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 +1746,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..8883c4b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2255,11 +2255,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 +2288,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/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..1490c91 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.app.ApplicationThreadConstants;
import android.os.IDeviceIdentifiersPolicyService;
import com.android.internal.telephony.TelephonyIntents;
import com.google.android.collect.Lists;
@@ -82,7 +83,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;
@@ -6507,11 +6507,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;
@@ -15155,7 +15155,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 +15191,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 +15724,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 +15757,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 +16161,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 +17346,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 +17362,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 +18034,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 +18060,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 +18126,7 @@
return ActivityManager.BROADCAST_SUCCESS;
}
mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
- sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REPLACED,
+ sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,
new String[] {ssp}, userId);
}
break;
@@ -21881,8 +21896,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 +22220,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);
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index d89f02d..b7618a1 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) {
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index cd7481c..63a4e86 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3324,8 +3324,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);
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index 878c0e7a..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/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/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/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ee7c6d2..56eaa83 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*/);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3681123..7299bdf 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -44,6 +44,7 @@
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_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
@@ -171,13 +172,20 @@
// the display's direct children should be allowed.
private boolean mRemovingDisplay = false;
+ private final WindowLayersController mLayersController;
+ 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.
*/
- DisplayContent(Display display, WindowManagerService service) {
+ DisplayContent(Display display, WindowManagerService service,
+ WindowLayersController layersController) {
mDisplay = display;
mDisplayId = display.getDisplayId();
+ mLayersController = layersController;
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -691,6 +699,24 @@
}
}
+ 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);
+ }
+ }
+
void prepareFreezingTaskBounds() {
for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.get(stackNdx);
@@ -798,6 +824,11 @@
mDimLayerController.dump(prefix + " ", pw);
pw.println();
mDividerControllerLocked.dump(prefix + " ", pw);
+
+ if (mInputMethodAnimLayerAdjustment != 0) {
+ pw.println(subPrefix
+ + "mInputMethodAnimLayerAdjustment=" + mInputMethodAnimLayerAdjustment);
+ }
}
@Override
@@ -1113,6 +1144,14 @@
}
}
+ /** Updates the layer assignment of windows on this display. */
+ void assignWindowLayers(boolean setLayoutNeeded) {
+ mLayersController.assignWindowLayers(mWindows);
+ if (setLayoutNeeded) {
+ setLayoutNeeded();
+ }
+ }
+
/**
* Z-orders the display window list so that:
* <ul>
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 5854197..8b5f62e 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);
@@ -413,7 +414,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 +443,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 +475,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 +487,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 +701,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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 05ffe2d..bddf6e2 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -165,8 +165,11 @@
ParcelFileDescriptor mSurfaceTraceFd;
RemoteEventTrace mRemoteEventTrace;
+ private final WindowLayersController mLayersController;
+
RootWindowContainer(WindowManagerService service) {
mService = service;
+ mLayersController = new WindowLayersController(mService);
}
WindowState computeFocusedWindow() {
@@ -211,7 +214,7 @@
}
private DisplayContent createDisplayContent(final Display display) {
- final DisplayContent dc = new DisplayContent(display, mService);
+ final DisplayContent dc = new DisplayContent(display, mService, mLayersController);
final int displayId = display.getDisplayId();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
@@ -1171,9 +1174,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*/);
}
}
@@ -1250,8 +1253,7 @@
if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0
&& mService.mWallpaperControllerLocked.adjustWallpaperWindows()) {
- mService.mLayersController.assignLayersLocked(windows);
- dc.setLayoutNeeded();
+ dc.assignWindowLayers(true /*setLayoutNeeded*/);
}
if (isDefaultDisplay
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index edd3a3b..8009207 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();
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 0841231..972a0cb 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -742,8 +742,7 @@
}
if (adjust && adjustWallpaperWindows()) {
- mService.mLayersController.assignLayersLocked(windows);
- dc.setLayoutNeeded();
+ dc.assignWindowLayers(true /*setLayoutNeeded*/);
}
}
@@ -769,6 +768,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/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..2b5603d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -675,8 +675,6 @@
WallpaperController mWallpaperControllerLocked;
- final WindowLayersController mLayersController;
-
boolean mAnimateWallpaperWithTarget;
// TODO: Move to RootWindowContainer
@@ -685,19 +683,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 +706,7 @@
DragState mDragState = null;
// For frozen screen animations.
- int mExitAnimId, mEnterAnimId;
+ private int mExitAnimId, mEnterAnimId;
boolean mAnimationScheduled;
@@ -972,13 +970,17 @@
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);
@@ -1120,7 +1122,8 @@
// 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();
+ final DisplayContent dc = getDefaultDisplayContentLocked();
+ final WindowList windows = dc.getWindowList();
WindowState w = null;
int i;
for (i = windows.size() - 1; i >= 0; --i) {
@@ -1235,10 +1238,10 @@
mInputMethodTarget = w;
mInputMethodTargetWaitingAnim = false;
if (w.mAppToken != null) {
- mLayersController.setInputMethodAnimLayerAdjustment(
+ dc.setInputMethodAnimLayerAdjustment(
w.mAppToken.mAppAnimator.animLayerAdjustment);
} else {
- mLayersController.setInputMethodAnimLayerAdjustment(0);
+ dc.setInputMethodAnimLayerAdjustment(0);
}
}
@@ -1260,7 +1263,7 @@
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);
+ dc.setInputMethodAnimLayerAdjustment(0);
}
return -1;
}
@@ -1426,7 +1429,7 @@
}
if (needAssignLayers) {
- mLayersController.assignLayersLocked(windows);
+ getDefaultDisplayContentLocked().assignWindowLayers(false /* setLayoutNeeded */);
}
return true;
@@ -1804,9 +1807,9 @@
moveInputMethodWindowsIfNeededLocked(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*/);
@@ -2019,8 +2022,7 @@
if (windows != null) {
windows.remove(win);
if (!mWindowPlacerLocked.isInLayout()) {
- mLayersController.assignLayersLocked(windows);
- win.setDisplayLayoutNeeded();
+ win.getDisplayContent().assignWindowLayers(true /* setLayoutNeeded */);
mWindowPlacerLocked.performSurfacePlacement();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
@@ -2398,7 +2400,7 @@
// 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());
+ win.getDisplayContent().assignWindowLayers(false /* setLayoutNeeded */);
}
if (wallpaperMayMove) {
@@ -3719,7 +3721,7 @@
if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/)) {
- mLayersController.assignLayersLocked(displayContent.getWindowList());
+ displayContent.assignWindowLayers(false /* setLayoutNeeded */);
}
mInputMonitor.setUpdateInputWindowsNeededLw();
@@ -8098,7 +8100,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 */);
}
}
@@ -8758,7 +8760,6 @@
}
mWindowPlacerLocked.dump(pw, " ");
mWallpaperControllerLocked.dump(pw, " ");
- mLayersController.dump(pw, " ");
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fa4dbc4..617c145 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1986,6 +1986,17 @@
return 0;
}
+ int getSpecialWindowAnimLayerAdjustment() {
+ int specialAdjustment = 0;
+ if (mIsImWindow) {
+ specialAdjustment = getDisplayContent().mInputMethodAnimLayerAdjustment;
+ } else if (mIsWallpaper) {
+ specialAdjustment = mService.mWallpaperControllerLocked.getAnimLayerAdjustment();
+ }
+
+ return mLayer + specialAdjustment;
+ }
+
void scheduleAnimationIfDimming() {
final DisplayContent dc = getDisplayContent();
if (dc == null) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 53fb99b..12e5bce 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -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/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 285a75c..336bc68 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -532,7 +532,7 @@
// TODO(multidisplay): IMEs are only supported on the default display.
if (windows == mService.getDefaultWindowListLocked()
&& !mService.moveInputMethodWindowsIfNeededLocked(true)) {
- mService.mLayersController.assignLayersLocked(windows);
+ mService.getDefaultDisplayContentLocked().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..862e8f0 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;
@@ -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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 88d8dd0..5106c32 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;
@@ -130,6 +131,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 +319,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;
@@ -5891,6 +5899,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 +6086,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 +6224,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 +6427,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 +6447,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;
@@ -9483,4 +9497,9 @@
return false;
}
}
+
+ private boolean isAdb() {
+ final int callingUid = mInjector.binderGetCallingUid();
+ return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 0f180af..a921e8a 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -236,6 +236,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 +292,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 +381,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 +392,10 @@
return new Network(mNetworkAgent.netId);
}
+ public ConditionVariable getPreventReconnectReceived() {
+ return mPreventReconnectReceived;
+ }
+
public ConditionVariable getDisconnectedCV() {
return mDisconnected;
}
@@ -597,6 +602,7 @@
@Override
protected CaptivePortalProbeResult isCaptivePortal() {
+ if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); }
return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null);
}
}
@@ -743,6 +749,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 +1719,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 +1900,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/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/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/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/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index c3075b3..e58fe70 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -570,7 +570,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..19388e99 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/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/tools/aapt2/.clang-format b/tools/aapt2/.clang-format
new file mode 100644
index 0000000..71c5ef2
--- /dev/null
+++ b/tools/aapt2/.clang-format
@@ -0,0 +1,3 @@
+BasedOnStyle: Google
+ColumnLimit: 100
+
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index a9794a4..2cbe117 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -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> minSdkVersion;
- /**
- * The Version code of the app.
- */
- Maybe<uint32_t> versionCode;
+ /**
+ * The Version code of the app.
+ */
+ Maybe<uint32_t> versionCode;
- /**
- * The revision code of the app.
- */
- Maybe<uint32_t> revisionCode;
+ /**
+ * The revision code of the app.
+ */
+ Maybe<uint32_t> revisionCode;
};
-} // 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..6598d63 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -31,854 +31,869 @@
static const char* kWildcardName = "any";
const ConfigDescription& ConfigDescription::defaultConfig() {
- static ConfigDescription config = {};
- return config;
+ 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, '-');
+ std::vector<std::string> parts = util::splitAndLowercase(str, '-');
- ConfigDescription config;
- ssize_t partsConsumed = 0;
- LocaleValue locale;
+ ConfigDescription config;
+ ssize_t partsConsumed = 0;
+ LocaleValue locale;
- const auto partsEnd = parts.end();
- auto partIter = parts.begin();
+ const auto partsEnd = parts.end();
+ auto partIter = parts.begin();
- if (str.size() == 0) {
- goto success;
+ if (str.size() == 0) {
+ goto success;
+ }
+
+ if (parseMcc(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
}
+ }
- if (parseMcc(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
+ if (parseMnc(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ 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.
+ 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.
+ return false;
success:
- if (out != NULL) {
- applyVersionForCompatibility(&config);
- *out = config;
- }
- return true;
+ if (out != NULL) {
+ applyVersionForCompatibility(&config);
+ *out = config;
+ }
+ return true;
}
-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;
- }
+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;
- }
+ if (minSdk > config->sdkVersion) {
+ config->sdkVersion = minSdk;
+ }
}
ConfigDescription ConfigDescription::copyWithoutSdkVersion() const {
- ConfigDescription copy = *this;
- copy.sdkVersion = 0;
- return copy;
+ 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);
+ if (*this == defaultConfig() || *this == o) {
+ return true;
+ }
+ return matchWithDensity(o) && !o.matchWithDensity(*this) &&
+ !isMoreSpecificThan(o) && !o.hasHigherPrecedenceThan(*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::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);
+ // 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);
+ 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..bb54886 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -29,148 +29,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..66b7d47 100644
--- a/tools/aapt2/ConfigDescription_test.cpp
+++ b/tools/aapt2/ConfigDescription_test.cpp
@@ -23,76 +23,79 @@
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..965db9e 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -17,8 +17,8 @@
#include "Debug.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "util/Util.h"
#include "ValueVisitor.h"
+#include "util/Util.h"
#include <algorithm>
#include <iostream>
@@ -31,275 +31,279 @@
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->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(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& parentRef = style->parent.value();
+ std::cout << " parent=";
+ if (parentRef.name) {
+ if (parentRef.privateReference) {
+ std::cout << "*";
}
+ std::cout << parentRef.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 (parentRef.id) {
+ std::cout << parentRef.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*> 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& 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.showSources && !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);
+ assert(iter != names.end() && *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& targetStyle) {
+ 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> 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;
+ }
+
+ 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());
+ }
}
-
- 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& 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 << "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";
- }
+ 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 << mPrefix;
+ std::cerr << "E: ";
+ if (!el->namespaceUri.empty()) {
+ std::cerr << el->namespaceUri << ":";
+ }
+ std::cerr << el->name << " (line=" << el->lineNumber << ")\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 << mPrefix << " A: ";
+ if (!attr.namespaceUri.empty()) {
+ std::cerr << attr.namespaceUri << ":";
+ }
+ 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 previousSize = mPrefix.size();
+ mPrefix += " ";
+ xml::Visitor::visit(el);
+ mPrefix.resize(previousSize);
+ }
- const size_t previousSize = mPrefix.size();
- mPrefix += " ";
- xml::Visitor::visit(ns);
- mPrefix.resize(previousSize);
- }
+ void visit(xml::Namespace* ns) override {
+ std::cerr << mPrefix;
+ std::cerr << "N: " << ns->namespacePrefix << "=" << ns->namespaceUri
+ << " (line=" << ns->lineNumber << ")\n";
- void visit(xml::Text* text) override {
- std::cerr << mPrefix;
- std::cerr << "T: '" << text->text << "'\n";
- }
+ const size_t previousSize = mPrefix.size();
+ mPrefix += " ";
+ xml::Visitor::visit(ns);
+ mPrefix.resize(previousSize);
+ }
-private:
- std::string mPrefix;
+ void visit(xml::Text* text) override {
+ std::cerr << mPrefix;
+ std::cerr << "T: '" << text->text << "'\n";
+ }
+
+ private:
+ std::string mPrefix;
};
-} // namespace
+} // namespace
void Debug::dumpXml(xml::XmlResource* doc) {
- XmlPrinter printer;
- doc->root->accept(&printer);
+ XmlPrinter printer;
+ doc->root->accept(&printer);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index c0fcbf1..bd92ec1 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -27,17 +27,18 @@
namespace aapt {
struct DebugPrintTableOptions {
- bool showSources = false;
+ bool showSources = 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& targetStyle);
+ 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..d39cf4c 100644
--- a/tools/aapt2/Diagnostics.h
+++ b/tools/aapt2/Diagnostics.h
@@ -29,119 +29,112 @@
namespace aapt {
struct DiagMessageActual {
- Source source;
- std::string message;
+ Source source;
+ std::string message;
};
struct DiagMessage {
-private:
- Source mSource;
- std::stringstream mMessage;
+ private:
+ Source mSource;
+ std::stringstream mMessage;
-public:
- DiagMessage() = default;
+ public:
+ DiagMessage() = default;
- explicit DiagMessage(const StringPiece& src) : mSource(src) {
- }
+ explicit DiagMessage(const StringPiece& src) : mSource(src) {}
- explicit DiagMessage(const Source& src) : mSource(src) {
- }
+ explicit DiagMessage(const Source& src) : mSource(src) {}
- explicit DiagMessage(size_t line) : mSource(Source().withLine(line)) {
- }
+ explicit DiagMessage(size_t line) : mSource(Source().withLine(line)) {}
- template <typename T>
- DiagMessage& operator<<(const T& value) {
- mMessage << value;
- return *this;
- }
+ template <typename T>
+ DiagMessage& operator<<(const T& value) {
+ mMessage << value;
+ return *this;
+ }
- DiagMessageActual build() const {
- return DiagMessageActual{ mSource, mMessage.str() };
- }
+ DiagMessageActual build() const {
+ return DiagMessageActual{mSource, mMessage.str()};
+ }
};
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& actualMsg) 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:
+ mNumErrors++;
+ if (mNumErrors > 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 (!actualMsg.source.path.empty()) {
+ std::cerr << actualMsg.source << ": ";
+ }
+ std::cerr << tag << ": " << actualMsg.message << "." << std::endl;
+ }
- DISALLOW_COPY_AND_ASSIGN(StdErrDiagnostics);
+ private:
+ size_t mNumErrors = 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)
+ : mSource(src), mDiag(diag) {}
- void log(Level level, DiagMessageActual& actualMsg) override {
- actualMsg.source.path = mSource.path;
- mDiag->log(level, actualMsg);
- }
+ void log(Level level, DiagMessageActual& actualMsg) override {
+ actualMsg.source.path = mSource.path;
+ mDiag->log(level, actualMsg);
+ }
-private:
- Source mSource;
- IDiagnostics* mDiag;
+ private:
+ Source mSource;
+ IDiagnostics* mDiag;
- 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..8f6f783 100644
--- a/tools/aapt2/DominatorTree.cpp
+++ b/tools/aapt2/DominatorTree.cpp
@@ -14,76 +14,78 @@
* limitations under the License.
*/
-#include "ConfigDescription.h"
#include "DominatorTree.h"
+#include "ConfigDescription.h"
#include <algorithm>
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) {
+ mProductRoots[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);
- }
+ for (auto& entry : mProductRoots) {
+ 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));
+ 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::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 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;
}
- // 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 (hasDominatedChildren) {
+ mChildren.erase(
+ std::remove_if(mChildren.begin(), mChildren.end(),
+ [](const std::unique_ptr<Node>& child) -> bool {
+ return child == nullptr;
+ }),
+ mChildren.end());
+ }
+ // 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;
}
- // 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));
- return true;
+ }
+ // The new config is not dominated by a child, so add it here.
+ newChild->mParent = this;
+ mChildren.push_back(std::move(newChild));
+ return true;
}
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);
+ // 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..6d45b5d 100644
--- a/tools/aapt2/DominatorTree.h
+++ b/tools/aapt2/DominatorTree.h
@@ -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)
+ : mValue(value), mParent(parent) {}
- inline ResourceConfigValue* value() const {
- return mValue;
- }
+ inline ResourceConfigValue* value() const { return mValue; }
- inline Node* parent() const {
- return mParent;
- }
+ inline Node* parent() const { return mParent; }
- inline bool isRootNode() const {
- return !mValue;
- }
+ inline bool isRootNode() const { return !mValue; }
- 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 mChildren;
}
-private:
- DISALLOW_COPY_AND_ASSIGN(DominatorTree);
+ bool tryAddChild(std::unique_ptr<Node> newChild);
- std::map<std::string, Node> mProductRoots;
+ 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;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DominatorTree);
+
+ std::map<std::string, Node> mProductRoots;
};
-} // 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 fb850e4da..a42d2f7 100644
--- a/tools/aapt2/DominatorTree_test.cpp
+++ b/tools/aapt2/DominatorTree_test.cpp
@@ -27,125 +27,132 @@
namespace {
class PrettyPrinter : public DominatorTree::Visitor {
-public:
- explicit PrettyPrinter(const int indent = 2) : mIndent(indent) {
- }
+ public:
+ explicit PrettyPrinter(const int indent = 2) : mIndent(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) {
+ mBuffer.str("");
+ mBuffer.clear();
+ tree->accept(this);
+ return mBuffer.str();
+ }
+
+ 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);
}
+ }
-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 mBuffer;
+ const int mIndent = 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 defaultConfig = {};
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ const ConfigDescription sw600dpLandConfig =
+ 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>(defaultConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
+ configs.push_back(
+ util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
- 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 defaultConfig = {};
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ const ConfigDescription sw600dpLandConfig =
+ 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>(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"));
- 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 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");
- 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>(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, ""));
- 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..cb16196 100644
--- a/tools/aapt2/Flags.cpp
+++ b/tools/aapt2/Flags.cpp
@@ -25,155 +25,167 @@
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;
- };
+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;
+ mFlags.push_back(
+ Flag{name.toString(), description.toString(), func, true, 1, false});
+ return *this;
}
-Flags& Flags::requiredFlagList(const StringPiece& name, const StringPiece& description,
+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;
- };
+ 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;
+ mFlags.push_back(
+ Flag{name.toString(), description.toString(), func, true, 1, false});
+ return *this;
}
-Flags& Flags::optionalFlag(const StringPiece& name, const StringPiece& description,
+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;
- };
+ 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;
+ mFlags.push_back(
+ Flag{name.toString(), description.toString(), func, false, 1, false});
+ return *this;
}
-Flags& Flags::optionalFlagList(const StringPiece& name, const StringPiece& description,
+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;
- };
+ 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;
+ mFlags.push_back(
+ Flag{name.toString(), description.toString(), func, false, 1, false});
+ return *this;
}
-Flags& Flags::optionalFlagList(const StringPiece& name, const StringPiece& description,
+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;
- };
+ 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;
+ 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;
- };
+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;
+ 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;
+ constexpr size_t kWidth = 50;
- *out << command << " [options]";
- for (const Flag& flag : mFlags) {
- if (flag.required) {
- *out << " " << flag.name << " arg";
- }
+ *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";
}
- *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 = " ";
- }
+ // 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();
+ }
+ *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,
+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 (size_t i = 0; i < args.size(); i++) {
+ StringPiece arg = args[i];
+ if (*(arg.data()) != '-') {
+ mArgs.push_back(arg.toString());
+ continue;
}
- for (const Flag& flag : mFlags) {
- if (flag.required && !flag.parsed) {
- *outError << "missing required flag " << flag.name << "\n\n";
+ 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;
+ }
}
- return true;
+
+ 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;
+ }
+ }
+ return true;
}
-const std::vector<std::string>& Flags::getArgs() {
- return mArgs;
-}
+const std::vector<std::string>& Flags::getArgs() { return mArgs; }
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Flags.h b/tools/aapt2/Flags.h
index b092855..4a0efdb 100644
--- a/tools/aapt2/Flags.h
+++ b/tools/aapt2/Flags.h
@@ -29,42 +29,45 @@
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 numArgs;
- bool parsed;
- };
+ bool parsed;
+ };
- std::vector<Flag> mFlags;
- std::vector<std::string> mArgs;
+ std::vector<Flag> mFlags;
+ std::vector<std::string> mArgs;
};
-} // 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..a0f7641 100644
--- a/tools/aapt2/Locale.cpp
+++ b/tools/aapt2/Locale.cpp
@@ -17,8 +17,8 @@
#include "Locale.h"
#include "util/Util.h"
-#include <algorithm>
#include <ctype.h>
+#include <algorithm>
#include <string>
#include <vector>
@@ -27,225 +27,226 @@
using android::ResTable_config;
void LocaleValue::setLanguage(const char* languageChars) {
- size_t i = 0;
- while ((*languageChars) != '\0') {
- language[i++] = ::tolower(*languageChars);
- languageChars++;
- }
+ size_t i = 0;
+ while ((*languageChars) != '\0') {
+ language[i++] = ::tolower(*languageChars);
+ languageChars++;
+ }
}
void LocaleValue::setRegion(const char* regionChars) {
- size_t i = 0;
- while ((*regionChars) != '\0') {
- region[i++] = ::toupper(*regionChars);
- regionChars++;
- }
+ size_t i = 0;
+ while ((*regionChars) != '\0') {
+ region[i++] = ::toupper(*regionChars);
+ regionChars++;
+ }
}
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++;
+ 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++;
- }
+ 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);
+ 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);
+ 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, '_');
+ // 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;
- }
- }
+ 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;
- }
+ 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;
- }
+ // 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;
- }
+ 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;
- }
+ // 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;
- }
+ 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;
- }
+ 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;
- }
+ if (!valid || numTags > 4) {
+ return false;
+ }
- return true;
+ 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::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());
+ 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());
+ // 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());
- setRegion(subtags[2].c_str());
- setVariant(subtags[3].c_str());
- } else {
- return -1;
- }
+ 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());
- ++iter;
+ // 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 {
- if ((part.length() == 2 || part.length() == 3)
- && isAlpha(part) && part != "car") {
- setLanguage(part.c_str());
- ++iter;
-
- if (iter != end) {
- const std::string& regionPart = *iter;
- if (regionPart.c_str()[0] == 'r' && regionPart.length() == 3) {
- setRegion(regionPart.c_str() + 1);
- ++iter;
- }
- }
- }
+ return -1;
}
- return static_cast<ssize_t>(iter - startIter);
+ ++iter;
+
+ } else {
+ if ((part.length() == 2 || part.length() == 3) && isAlpha(part) &&
+ part != "car") {
+ setLanguage(part.c_str());
+ ++iter;
+
+ if (iter != end) {
+ const std::string& regionPart = *iter;
+ if (regionPart.c_str()[0] == 'r' && regionPart.length() == 3) {
+ setRegion(regionPart.c_str() + 1);
+ ++iter;
+ }
+ }
+ }
+ }
+
+ return static_cast<ssize_t>(iter - startIter);
}
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));
- }
+ 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);
+ 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..0f75850 100644
--- a/tools/aapt2/Locale.h
+++ b/tools/aapt2/Locale.h
@@ -29,86 +29,84 @@
* 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 setLanguage(const char* language);
+ void setRegion(const char* language);
+ void setScript(const char* script);
+ void setVariant(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..b82d2b9 100644
--- a/tools/aapt2/Locale_test.cpp
+++ b/tools/aapt2/Locale_test.cpp
@@ -22,61 +22,73 @@
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..8aedd44 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -28,9 +28,9 @@
static const char* sMinorVersion = "2";
int printVersion() {
- std::cerr << "Android Asset Packaging Tool (aapt) "
- << sMajorVersion << "." << sMinorVersion << std::endl;
- return 0;
+ std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
+ << sMinorVersion << std::endl;
+ return 0;
}
extern int compile(const std::vector<StringPiece>& args);
@@ -38,35 +38,36 @@
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..6d244aa 100644
--- a/tools/aapt2/NameMangler.h
+++ b/tools/aapt2/NameMangler.h
@@ -26,69 +26,71 @@
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 targetPackageName;
- /**
- * 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> packagesToMangle;
};
class NameMangler {
-private:
- NameManglerPolicy mPolicy;
+ private:
+ NameManglerPolicy mPolicy;
-public:
- explicit NameMangler(NameManglerPolicy policy) : mPolicy(policy) {
+ public:
+ explicit NameMangler(NameManglerPolicy policy) : mPolicy(policy) {}
+
+ Maybe<ResourceName> mangleName(const ResourceName& name) {
+ if (mPolicy.targetPackageName == name.package ||
+ mPolicy.packagesToMangle.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 mangledEntryName = mangleEntry(name.package, name.entry);
+ return ResourceName(mPolicy.targetPackageName, name.type, mangledEntryName);
+ }
- 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() || mPolicy.targetPackageName == package) {
+ return false;
+ }
+ return mPolicy.packagesToMangle.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* outName, std::string* outPackage) {
+ size_t pivot = outName->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;
- }
-
- /**
- * 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;
- }
+ outPackage->assign(outName->data(), pivot);
+ outName->assign(outName->data() + pivot + 1, outName->size() - (pivot + 1));
+ return true;
+ }
};
-} // 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..b02986d 100644
--- a/tools/aapt2/NameMangler_test.cpp
+++ b/tools/aapt2/NameMangler_test.cpp
@@ -22,25 +22,25 @@
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 mangledName = NameMangler::mangleEntry(package, name);
+ EXPECT_EQ(mangledName, "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 unmangledPackage;
+ std::string unmangledName = mangledName;
+ ASSERT_TRUE(NameMangler::unmangle(&unmangledName, &unmangledPackage));
+ EXPECT_EQ(unmangledName, "Platform.AppCompat");
+ EXPECT_EQ(unmangledPackage, "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..6805631 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -23,74 +23,97 @@
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 {};
+ 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;
+ 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..30739db 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -37,29 +37,29 @@
* 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);
@@ -75,17 +75,17 @@
* a resource in the ResourceTable.
*/
struct ResourceName {
- std::string package;
- ResourceType type;
- std::string entry;
+ std::string package;
+ ResourceType type;
+ std::string entry;
- ResourceName() : type(ResourceType::kRaw) {}
- ResourceName(const StringPiece& p, ResourceType t, const StringPiece& e);
+ ResourceName() : type(ResourceType::kRaw) {}
+ 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 isValid() 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;
+ 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 isValid() 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 resId); // 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 isValid() const;
+ uint8_t packageId() const;
+ uint8_t typeId() const;
+ uint16_t entryId() 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> exportedSymbols;
};
/**
- * 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(const ResourceId& rhs) : id(rhs.id) {}
-inline ResourceId::ResourceId(uint32_t resId) : id(resId) {
-}
+inline ResourceId::ResourceId(uint32_t resId) : id(resId) {}
-inline ResourceId::ResourceId(uint8_t p, uint8_t t, uint16_t e) : id((p << 24) | (t << 16) | e) {
-}
+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;
+ return (id & 0xff000000u) != 0 && (id & 0x00ff0000u) != 0;
}
inline uint8_t ResourceId::packageId() const {
- return static_cast<uint8_t>(id >> 24);
+ return static_cast<uint8_t>(id >> 24);
}
inline uint8_t ResourceId::typeId() const {
- return static_cast<uint8_t>(id >> 16);
+ return static_cast<uint8_t>(id >> 16);
}
inline uint16_t ResourceId::entryId() const {
- return static_cast<uint16_t>(id);
+ 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& 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;
}
//
// 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();
+ 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();
+ 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);
+ return ResourceName(package, type, entry);
}
inline bool ResourceNameRef::isValid() const {
- return !package.empty() && !entry.empty();
+ 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..51aed13 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -28,445 +28,488 @@
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;
+ 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;
+ 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;
}
- 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> symbolState;
+ std::string comment;
+ std::unique_ptr<Value> value;
+ std::list<ParsedResource> childResources;
};
// 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 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();
+ }
- 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->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->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->childResources) {
+ 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)
+ : mDiag(diag),
+ mTable(table),
+ mSource(source),
+ mConfig(config),
+ mOptions(options) {}
/**
* Build a string from XML that converts nested elements into Span objects.
*/
-bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::string* outRawString,
+bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser,
+ std::string* outRawString,
StyleString* outStyleString) {
- std::vector<Span> spanStack;
+ std::vector<Span> spanStack;
- 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;
+ 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;
+ }
- depth--;
- if (depth == 0) {
- break;
- }
+ depth--;
+ if (depth == 0) {
+ break;
+ }
- spanStack.back().lastChar = builder.utf16Len() - 1;
- outStyleString->spans.push_back(spanStack.back());
- spanStack.pop_back();
+ 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::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);
+ } 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() << "'");
}
- }
- assert(spanStack.empty() && "spans haven't been fully processed");
+ continue;
+ }
+ depth++;
- outStyleString->str = builder.str();
- return !error;
+ // 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);
- break;
- };
-
- if (parser->getEvent() == xml::XmlPullParser::Event::kBadDocument) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "xml parser error: " << parser->getLastError());
- return false;
+ 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;
}
- return !error;
+
+ if (!parser->getElementNamespace().empty() ||
+ parser->getElementName() != "resources") {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "root element must be <resources>");
+ return false;
+ }
+
+ error |= !parseResources(parser);
+ break;
+ };
+
+ if (parser->getEvent() == xml::XmlPullParser::Event::kBadDocument) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "xml parser error: " << parser->getLastError());
+ return false;
+ }
+ return !error;
}
bool ResourceParser::parseResources(xml::XmlPullParser* parser) {
- std::set<ResourceName> strippedResources;
+ 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 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;
}
- // 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 (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;
}
- return !error;
+ 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;
+ }
+ }
+
+ // 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;
+ }
+ }
+
+ return !error;
}
+bool ResourceParser::parseResource(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ struct ItemTypeFormat {
+ ResourceType type;
+ uint32_t format;
+ };
-bool ResourceParser::parseResource(xml::XmlPullParser* parser, ParsedResource* outResource) {
- struct ItemTypeFormat {
- ResourceType type;
- uint32_t format;
- };
+ using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*,
+ ParsedResource*)>;
- 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 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)},
+ });
- 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();
- std::string resourceType = parser->getElementName();
+ // The value format accepted for this resource.
+ uint32_t resourceFormat = 0u;
- // 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;
- }
- }
+ 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;
}
- // 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() << "'");
- return false;
-}
-
-bool ResourceParser::parseItem(xml::XmlPullParser* parser, ParsedResource* outResource,
- const uint32_t format) {
- if (format == android::ResTable_map::TYPE_STRING) {
- return parseString(parser, outResource);
- }
-
- outResource->value = parseXml(parser, format, kNoRawString);
- if (!outResource->value) {
- mDiag->error(DiagMessage(outResource->source) << "invalid " << outResource->name.type);
+ 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() << "'");
+ return false;
+}
+
+bool ResourceParser::parseItem(xml::XmlPullParser* parser,
+ ParsedResource* outResource,
+ const uint32_t format) {
+ if (format == android::ResTable_map::TYPE_STRING) {
+ return parseString(parser, outResource);
+ }
+
+ outResource->value = parseXml(parser, format, kNoRawString);
+ if (!outResource->value) {
+ mDiag->error(DiagMessage(outResource->source) << "invalid "
+ << outResource->name.type);
+ return false;
+ }
+ return true;
}
/**
@@ -476,771 +519,834 @@
* 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,
+std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser,
+ const uint32_t typeMask,
const bool allowRawValue) {
- const size_t beginXmlLine = parser->getLineNumber();
+ const size_t beginXmlLine = parser->getLineNumber();
- 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 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}));
+ }
+ 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* 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 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 = 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();
+ }
- outResource->value = parseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
- if (!outResource->value) {
- mDiag->error(DiagMessage(outResource->source) << "not a valid string");
- return false;
- }
+ 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 (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);
- }
+ 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;
}
- } else if (StyledString* stringValue = valueCast<StyledString>(outResource->value.get())) {
- stringValue->setTranslateable(translateable);
+ mDiag->warn(msg);
+ }
}
- return true;
+
+ } 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;
- }
+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;
- }
+ 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;
+ 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 (Maybe<StringPiece> maybeIdStr =
+ xml::findNonEmptyAttribute(parser, "id")) {
+ Maybe<ResourceId> maybeId =
+ ResourceUtils::parseResourceId(maybeIdStr.value());
if (!maybeId) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid resource ID '" << maybeIdStr.value() << "' in <public-group>");
- return false;
+ mDiag->error(DiagMessage(outResource->source) << "invalid resource ID '"
+ << maybeId.value()
+ << "' in <public>");
+ return false;
}
+ outResource->id = maybeId.value();
+ }
- ResourceId nextId = maybeId.value();
+ if (*parsedType == ResourceType::kId) {
+ // An ID marked as public is also the definition of an ID.
+ outResource->value = util::make_unique<Id>();
+ }
- 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;
+ outResource->symbolState = SymbolState::kPublic;
+ return true;
}
-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;
- }
+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;
-}
+ }
-bool ResourceParser::parseAddResource(xml::XmlPullParser* parser, ParsedResource* outResource) {
- if (parseSymbolImpl(parser, outResource)) {
- outResource->symbolState = SymbolState::kUndefined;
- return true;
- }
+ 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;
+ }
-bool ResourceParser::parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource) {
- return parseAttrImpl(parser, outResource, 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::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
- bool weak) {
- outResource->name.type = ResourceType::kAttr;
-
- // 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();
- }
-
- uint32_t typeMask = 0;
-
- 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* outResource) {
+ if (parseSymbolImpl(parser, outResource)) {
+ outResource->symbolState = SymbolState::kPrivate;
return true;
+ }
+ return false;
}
-Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(xml::XmlPullParser* parser,
- const StringPiece& tag) {
- const Source source = mSource.withLine(parser->getLineNumber());
+bool ResourceParser::parseAddResource(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ if (parseSymbolImpl(parser, outResource)) {
+ outResource->symbolState = SymbolState::kUndefined;
+ return true;
+ }
+ return false;
+}
- Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
- if (!maybeName) {
- mDiag->error(DiagMessage(source) << "no attribute 'name' found for tag <" << tag << ">");
- return {};
+bool ResourceParser::parseAttr(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ return parseAttrImpl(parser, outResource, false);
+}
+
+bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser,
+ ParsedResource* outResource, bool weak) {
+ outResource->name.type = ResourceType::kAttr;
+
+ // 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();
+ }
+
+ uint32_t typeMask = 0;
+
+ 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);
+ }
}
- Maybe<StringPiece> maybeValue = xml::findNonEmptyAttribute(parser, "value");
- if (!maybeValue) {
- mDiag->error(DiagMessage(source) << "no attribute 'value' found for tag <" << tag << ">");
- return {};
+ 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);
+ }
}
- 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 {};
+ 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;
}
- return Attribute::Symbol{
- Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())), val.data };
+ 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);
+ return true;
+}
+
+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());
+ 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");
+ 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)});
+ return true;
+}
+
+bool ResourceParser::parseStyle(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ outResource->name.type = ResourceType::kStyle;
+
+ std::unique_ptr<Style> style = util::make_unique<Style>();
+
+ 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;
+ }
+
+ // Transform the namespace prefix to the actual package name, and mark the
+ // reference as
+ // private if appropriate.
+ transformReferenceFromNamespace(parser, "", &style->parent.value());
}
- Maybe<Reference> maybeKey = ResourceUtils::parseXmlAttributeName(maybeName.value());
- if (!maybeKey) {
- mDiag->error(DiagMessage(source) << "invalid attribute name '" << maybeName.value() << "'");
- return false;
+ } 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)));
+ }
+ }
+
+ 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;
}
- transformReferenceFromNamespace(parser, "", &maybeKey.value());
- maybeKey.value().setSource(source);
+ const std::string& elementNamespace = parser->getElementNamespace();
+ const std::string& elementName = parser->getElementName();
+ if (elementNamespace == "" && elementName == "item") {
+ error |= !parseStyleItem(parser, style.get());
- std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
- if (!value) {
- mDiag->error(DiagMessage(source) << "could not parse style item");
- return false;
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << ":" << elementName << ">");
+ error = true;
}
+ }
- style->entries.push_back(Style::Entry{ std::move(maybeKey.value()), std::move(value) });
- return true;
+ if (error) {
+ return false;
+ }
+
+ outResource->value = std::move(style);
+ return true;
}
-bool ResourceParser::parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource) {
- outResource->name.type = ResourceType::kStyle;
-
- std::unique_ptr<Style> style = util::make_unique<Style>();
-
- 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;
- }
-
- // Transform the namespace prefix to the actual package name, and mark the reference as
- // private if appropriate.
- transformReferenceFromNamespace(parser, "", &style->parent.value());
- }
-
- } 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)));
- }
- }
-
- 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 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 (error) {
- return false;
- }
-
- outResource->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* outResource) {
- return parseArrayImpl(parser, outResource, 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* outResource) {
- return parseArrayImpl(parser, outResource, 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* outResource) {
- return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_STRING);
-}
-
-bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
+bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser,
+ ParsedResource* outResource,
const uint32_t typeMask) {
- outResource->name.type = ResourceType::kArray;
+ outResource->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 = 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;
}
- array->setTranslateable(translateable);
+ translateable = maybeTranslateable.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->getDepth();
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text and comments.
+ continue;
}
- if (error) {
- return false;
- }
+ 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));
- outResource->value = std::move(array);
- return true;
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "unknown tag <" << elementNamespace << ":" << elementName
+ << ">");
+ error = true;
+ }
+ }
+
+ if (error) {
+ return false;
+ }
+
+ outResource->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* outResource) {
+ outResource->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->getDepth();
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text and comments.
+ continue;
}
- if (error) {
- return false;
- }
+ 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;
+ }
- outResource->value = std::move(plural);
- return true;
+ 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;
+ }
+ }
+
+ if (error) {
+ return false;
+ }
+
+ outResource->value = std::move(plural);
+ return true;
}
bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser,
ParsedResource* outResource) {
- outResource->name.type = ResourceType::kStyleable;
+ outResource->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.
+ outResource->symbolState = 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 (outResource->config != ConfigDescription::defaultConfig()) {
+ mDiag->warn(DiagMessage(outResource->source)
+ << "ignoring configuration '" << outResource->config
+ << "' for styleable " << outResource->name.entry);
+ outResource->config = ConfigDescription::defaultConfig();
+ }
+
+ std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+
+ 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;
}
- std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+ 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;
+ }
- 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> maybeRef =
+ ResourceUtils::parseXmlAttributeName(maybeName.value());
+ if (!maybeRef) {
+ mDiag->error(DiagMessage(itemSource) << "<attr> tag has invalid name '"
+ << maybeName.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& childRef = maybeRef.value();
+ xml::transformReferenceFromNamespace(parser, "", &childRef);
- // 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 childResource;
+ childResource.name = childRef.name.value();
+ childResource.source = itemSource;
+ childResource.comment = std::move(comment);
- Reference& childRef = maybeRef.value();
- xml::transformReferenceFromNamespace(parser, "", &childRef);
+ if (!parseAttrImpl(parser, &childResource, 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.
+ childRef.setComment(childResource.comment);
+ childRef.setSource(itemSource);
+ styleable->entries.push_back(std::move(childRef));
- if (!parseAttrImpl(parser, &childResource, true)) {
- error = true;
- continue;
- }
+ outResource->childResources.push_back(std::move(childResource));
- // 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(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource)
+ << "unknown tag <" << elementNamespace << ":" << elementName
+ << ">");
+ error = true;
}
- if (error) {
- return false;
- }
+ comment = {};
+ }
- outResource->value = std::move(styleable);
- return true;
+ if (error) {
+ return false;
+ }
+
+ outResource->value = std::move(styleable);
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index ece3090..644ed49 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -33,79 +33,92 @@
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 errorOnPositionalArguments = 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 = {});
- ResourceParser(const ResourceParser&) = delete; // No copy.
+ ResourceParser(const ResourceParser&) = delete; // No copy.
- bool parse(xml::XmlPullParser* parser);
+ bool parse(xml::XmlPullParser* parser);
-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);
+ 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 `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);
+ /*
+ * 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* outResource);
+ bool parseResources(xml::XmlPullParser* parser);
+ bool parseResource(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseItem(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t format);
- bool parseString(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseItem(xml::XmlPullParser* parser, ParsedResource* outResource,
+ uint32_t format);
+ bool parseString(xml::XmlPullParser* parser, ParsedResource* outResource);
- 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);
+ 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* mDiag;
+ ResourceTable* mTable;
+ Source mSource;
+ ConfigDescription mConfig;
+ ResourceParserOptions mOptions;
};
-} // 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..b6d57c0 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -26,512 +26,559 @@
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 xmlParser(input);
+ ASSERT_FALSE(parser.parse(&xmlParser));
}
struct ResourceParserTest : public ::testing::Test {
- ResourceTable mTable;
- std::unique_ptr<IAaptContext> mContext;
+ ResourceTable mTable;
+ std::unique_ptr<IAaptContext> mContext;
- void SetUp() override {
- mContext = 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(mContext->getDiagnostics(), &mTable, 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();
+ }
};
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>(&mTable, "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>(&mTable, "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>(&mTable, "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 expectedStr = "This is my aunt\u2019s string";
+ EXPECT_EQ(expectedStr, *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].firstChar);
+ EXPECT_EQ(23u, str->value->spans[0].lastChar);
}
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>(&mTable, "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>(&mTable, "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>(&mTable, "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>(&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);
}
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>(&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);
}
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>(&mTable, "attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
- 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>(&mTable, "attr/bar");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
}
-// 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 watchConfig = 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, watchConfig));
- 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>(&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_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>(&mTable, "attr/foo"));
+ EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, "attr/baz"));
+ EXPECT_NE(nullptr, test::getValue<Styleable>(&mTable, "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>(&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);
}
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>(&mTable, "attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
}
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>(&mTable, "attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask);
}
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* 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);
- 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(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(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(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(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(enumAttr->symbols[2].symbol.name);
+ EXPECT_EQ(enumAttr->symbols[2].symbol.name.value().entry, "baz");
+ EXPECT_EQ(enumAttr->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* 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);
- 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(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(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(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(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(flagAttr->symbols[2].symbol.name);
+ EXPECT_EQ(flagAttr->symbols[2].symbol.name.value().entry, "baz");
+ EXPECT_EQ(flagAttr->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> flagValue =
+ ResourceUtils::tryParseFlagSymbol(flagAttr, "baz|bat");
+ ASSERT_NE(nullptr, flagValue);
+ EXPECT_EQ(flagValue->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>(&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());
- 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>(&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());
}
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>(&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());
}
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>(&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());
}
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>(&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);
}
-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>(&mTable, "style/foo.bar");
+ ASSERT_NE(nullptr, style);
+ AAPT_EXPECT_FALSE(style->parent);
+ EXPECT_FALSE(style->parentInferred);
}
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>(&mTable, "style/foo");
+ ASSERT_NE(nullptr, style);
+ AAPT_ASSERT_TRUE(style->parent);
+ EXPECT_TRUE(style->parent.value().privateReference);
}
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>(&mTable, "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 =
+ mTable.findResource(test::parseNameOrDie("styleable/foo"));
+ AAPT_ASSERT_TRUE(result);
+ EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
- Attribute* attr = test::getValue<Attribute>(&mTable, "attr/bar");
- ASSERT_NE(attr, nullptr);
- EXPECT_TRUE(attr->isWeak());
+ Attribute* attr = test::getValue<Attribute>(&mTable, "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>(&mTable, "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>(&mTable, "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>(&mTable, "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>(&mTable, "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>(&mTable, "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].privateReference);
+ 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].privateReference);
+ 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>(&mTable, "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>(&mTable, "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>(&mTable, "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>(&mTable, "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>(&mTable, "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 +588,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>(&mTable, "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>(&mTable, "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 +610,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>(&mTable, "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 +626,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>(
+ &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"));
}
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 =
+ mTable.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(),
+ 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);
+
+ result = mTable.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(0x01010040), actualId);
-
- result = mTable.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);
+ EXPECT_EQ(ResourceId(0x01010041), actualId);
}
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 =
+ 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);
}
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>(&mTable, "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..c52c91c 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -14,266 +14,282 @@
* 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 <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 lessThanType(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;
+ 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;
+ 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::findPackageById(uint8_t id) {
- for (auto& package : packages) {
- if (package->id && package->id.value() == id) {
- return package.get();
- }
+ 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;
+ }
+
+ if (id && package->id && package->id.value() != id.value()) {
+ return nullptr;
+ }
+ 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();
- }
+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();
+ 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();
- }
- return nullptr;
+ 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 nullptr;
}
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();
+ 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();
}
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;
+ 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;
}
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();
+ 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();
}
ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config) {
- return findValue(config, StringPiece());
+ 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,
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());
- }
+ 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;
+ 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;
}
-
- 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 (!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;
+ }
+ return CollisionResult::kConflict;
}
static constexpr const char* kValidNameChars = "._-";
@@ -284,8 +300,8 @@
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,
@@ -294,8 +310,8 @@
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, resId, config, product, std::move(value),
+ kValidNameChars, resolveValueCollision, diag);
}
bool ResourceTable::addFileReference(const ResourceNameRef& name,
@@ -303,31 +319,29 @@
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* 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::addResourceAllowMangled(const ResourceNameRef& name,
@@ -335,8 +349,8 @@
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,
@@ -345,220 +359,193 @@
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& 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");
- 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)
- << "'");
- return false;
- }
+ 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) << "'");
+ return false;
+ }
- 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;
- }
+ 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;
- }
+ 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;
- }
+ 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.
+ 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;
- } 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::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;
- }
+ case CollisionResult::kKeepOriginal:
+ break;
}
+ }
- if (resId.isValid()) {
- package->id = resId.packageId();
- type->id = resId.typeId();
- entry->id = resId.entryId();
- }
- return true;
+ if (resId.isValid()) {
+ package->id = resId.packageId();
+ type->id = resId.typeId();
+ entry->id = resId.entryId();
+ }
+ return true;
}
-bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId& resId,
+bool ResourceTable::setSymbolState(const ResourceNameRef& name,
+ const ResourceId& resId,
const Symbol& symbol, IDiagnostics* diag) {
- return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
+ return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
}
bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name,
const ResourceId& resId,
- const Symbol& symbol, IDiagnostics* diag) {
- return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag);
+ const Symbol& symbol,
+ IDiagnostics* diag) {
+ return setSymbolStateImpl(name, resId, 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& resId,
+ const Symbol& symbol,
+ const char* validChars,
IDiagnostics* diag) {
- assert(diag && "diagnostics can't be nullptr");
+ assert(diag && "diagnostics can't be 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 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;
+ }
- 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 (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;
+ }
- 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 (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;
+ }
- 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 (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;
+ }
- if (resId.isValid()) {
- package->id = resId.packageId();
- type->id = resId.typeId();
- entry->id = resId.entryId();
- }
+ if (resId.isValid()) {
+ package->id = resId.packageId();
+ type->id = resId.typeId();
+ entry->id = resId.entryId();
+ }
- // 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->symbolStatus.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->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);
+ 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..ebaad41 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 symbolStatus;
- /**
- * 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 symbolStatus;
- /**
- * 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& resId,
+ 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& resId,
+ 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& resId, 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: `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;
-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& resId,
+ const ConfigDescription& config,
+ const StringPiece& product, std::unique_ptr<Value> value,
+ const char* validChars,
+ const CollisionResolverFunc& conflictResolver,
+ IDiagnostics* diag);
+
+ bool addFileReferenceImpl(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source, const StringPiece& path,
+ io::IFile* file, const char* validChars,
IDiagnostics* diag);
- DISALLOW_COPY_AND_ASSIGN(ResourceTable);
+ bool setSymbolStateImpl(const ResourceNameRef& name, const ResourceId& resId,
+ const Symbol& symbol, const char* validChars,
+ 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..a64ad3e 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 languageConfig;
+ memcpy(languageConfig.language, "pl", sizeof(languageConfig.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"), languageConfig, "",
+ 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", languageConfig));
}
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..b41be4b 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "NameMangler.h"
#include "ResourceUtils.h"
+#include "NameMangler.h"
#include "SdkConstants.h"
#include "flatten/ResourceTypeExtensions.h"
#include "util/Files.h"
@@ -27,188 +27,196 @@
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& nameIn) {
+ ResourceName nameOut;
+ if (!nameIn.package) {
+ return {};
+ }
- nameOut.package = util::utf16ToUtf8(StringPiece16(nameIn.package, nameIn.packageLen));
+ nameOut.package =
+ util::utf16ToUtf8(StringPiece16(nameIn.package, nameIn.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 (nameIn.type) {
+ type = parseResourceType(
+ util::utf16ToUtf8(StringPiece16(nameIn.type, nameIn.typeLen)));
+ } else if (nameIn.type8) {
+ type = parseResourceType(StringPiece(nameIn.type8, nameIn.typeLen));
+ } else {
+ return {};
+ }
- if (!type) {
- return {};
- }
+ if (!type) {
+ return {};
+ }
- nameOut.type = *type;
+ nameOut.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 (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;
}
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 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;
}
- outEntry->assign(start, end - start);
+ current++;
+ }
+ outEntry->assign(start, end - start);
- return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty());
+ return !(hasPackageSeparator && outPackage->empty()) &&
+ !(hasTypeSeparator && outType->empty());
}
-bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef, bool* outPrivate) {
- if (str.empty()) {
- return false;
+bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef,
+ bool* outPrivate) {
+ 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* parsedType = parseResourceType(type);
+ if (!parsedType) {
+ return false;
+ }
+
+ if (entry.empty()) {
+ return false;
+ }
+
+ if (outRef) {
+ outRef->package = package;
+ outRef->type = *parsedType;
+ outRef->entry = entry;
+ }
+
+ if (outPrivate) {
+ *outPrivate = priv;
+ }
+ return true;
+}
+
+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;
}
- size_t offset = 0;
- bool priv = false;
- if (str.data()[0] == '*') {
- priv = true;
- offset = 1;
+ ResourceNameRef name;
+ if (!parseResourceName(
+ trimmedStr.substr(offset, trimmedStr.size() - offset), &name,
+ &priv)) {
+ return false;
}
- StringPiece package;
- StringPiece type;
- StringPiece entry;
- if (!extractResourceName(str.substr(offset, str.size() - offset), &package, &type, &entry)) {
- return false;
+ if (create && priv) {
+ return false;
}
- const ResourceType* parsedType = parseResourceType(type);
- if (!parsedType) {
- return false;
- }
-
- if (entry.empty()) {
- return false;
+ if (create && name.type != ResourceType::kId) {
+ return false;
}
if (outRef) {
- outRef->package = package;
- outRef->type = *parsedType;
- outRef->entry = entry;
+ *outRef = name;
+ }
+
+ if (outCreate) {
+ *outCreate = create;
}
if (outPrivate) {
- *outPrivate = priv;
+ *outPrivate = priv;
}
return true;
-}
-
-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;
+ }
+ return false;
}
bool isReference(const StringPiece& str) {
- return parseReference(str, nullptr, nullptr, nullptr);
+ 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;
- }
+ 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);
+ return parseAttributeReference(str, nullptr);
}
/*
@@ -219,414 +227,421 @@
* <[*]package>:[style/]<entry>
* [[*]package:style/]<entry>
*/
-Maybe<Reference> parseStyleParentReference(const StringPiece& str, std::string* outError) {
- if (str.empty()) {
- return {};
+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 {};
}
+ }
- StringPiece name = str;
+ if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
+ std::stringstream err;
+ err << "invalid parent reference '" << str << "'";
+ *outError = err.str();
+ return {};
+ }
- 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;
+ 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;
+ 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++;
+ 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++;
+ }
- 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));
+ 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;
- }
+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);
+ if (parseAttributeReference(str, &ref)) {
+ if (outCreate) {
+ *outCreate = false;
}
- return {};
+ return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
+ }
+ 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 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);
}
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);
- }
+ 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);
}
- return {};
+ }
+ return {};
}
std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr,
const StringPiece& str) {
- android::Res_value flags = { };
- flags.dataType = android::Res_value::TYPE_INT_HEX;
- flags.data = 0u;
+ 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);
- }
-
- for (StringPiece part : util::tokenize(str, '|')) {
- StringPiece trimmedPart = util::trimWhitespace(part);
-
- 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 trimmedPart = util::trimWhitespace(part);
+
+ 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 {};
+ }
+ }
+ 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;
- } else {
- *outError = true;
- return 0xffffffffu;
- }
+ 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 {
+ *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 {};
- }
+ 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);
+ 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 {};
+ 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 {};
+ 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));
+ 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;
- }
- }
+ 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 {};
+ }
+ return {};
}
Maybe<int> parseSdkVersion(const StringPiece& str) {
- StringPiece trimmedStr(util::trimWhitespace(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);
- }
+ 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 {};
+ // 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 (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);
+ if (maybeResult.value()) {
+ value.data = 0xffffffffu;
+ } else {
+ value.data = 0;
}
- return {};
+ 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 {};
- }
- return util::make_unique<BinaryPrimitive>(value);
+ 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);
}
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::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) {
+ 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);
- }
+ 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);
+ }
- 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 && onCreateReference) {
+ onCreateReference(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 (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 (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 (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 (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 (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);
}
+ }
- 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 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);
+ }
}
- return {};
+ }
+ return {};
}
/**
@@ -634,48 +649,50 @@
* 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;
- }
+ 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;
+ }
- 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 (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 (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 (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);
}
- 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& resFile,
+ const NameMangler* mangler) {
+ std::stringstream out;
+ out << "res/" << resFile.name.type;
+ if (resFile.config != ConfigDescription{}) {
+ out << "-" << resFile.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(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();
}
-} // namespace ResourceUtils
-} // namespace aapt
+} // namespace ResourceUtils
+} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 555203b..cebe47c 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -41,15 +41,18 @@
StringPiece* outType, StringPiece* outEntry);
/**
- * 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
+ * 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.
*/
bool parseResourceName(const StringPiece& str, ResourceNameRef* outResource,
bool* outPrivate = nullptr);
/*
- * Returns true if the string was parsed as a reference (@[+][package:]type/name), with
+ * Returns true if the string was parsed as a reference
+ * (@[+][package:]type/name), with
* `outReference` set to the parsed reference.
*
* If '+' was present in the reference, `outCreate` is set to true.
@@ -59,28 +62,34 @@
bool* outCreate = nullptr, bool* outPrivate = 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);
/*
- * Returns true if the string was parsed as an attribute reference (?[package:][type/]name),
+ * Returns true if the string was parsed as an attribute reference
+ * (?[package:][type/]name),
* with `outReference` set to the parsed reference.
*/
-bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outReference);
+bool parseAttributeReference(const StringPiece& str,
+ ResourceNameRef* outReference);
/**
- * Returns true if the string is in the form of an attribute reference(?[package:][type/]name).
+ * Returns true if the string is in the form of an attribute
+ * reference(?[package:][type/]name).
*/
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);
@@ -100,18 +109,22 @@
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* outError);
/*
- * 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
@@ -119,14 +132,18 @@
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* outCreate = nullptr);
/*
- * Returns a BinaryPrimitve object representing @null or @empty if the string was parsed
+ * Returns a BinaryPrimitve object representing @null or @empty if the string
+ * was parsed
* as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str);
@@ -138,13 +155,15 @@
std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing a boolean if the string was parsed
+ * Returns a BinaryPrimitve object representing a boolean if the string was
+ * parsed
* as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing an integer if the string was parsed
+ * Returns a BinaryPrimitve object representing an integer if the string was
+ * parsed
* as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece& str);
@@ -156,45 +175,51 @@
std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing an enum symbol if the string was parsed
+ * Returns a BinaryPrimitve object representing an enum symbol if the string was
+ * parsed
* as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing a flag symbol if the string was parsed
+ * Returns a BinaryPrimitve object representing a flag symbol if the string was
+ * parsed
* as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* enumAttr,
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
* 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 = {});
+ const StringPiece& value, const Attribute* attr,
+ const std::function<void(const ResourceName&)>& onCreateReference = {});
std::unique_ptr<Item> tryParseItemForAttribute(
- const StringPiece& value, uint32_t typeMask,
- const std::function<void(const ResourceName&)>& onCreateReference = {});
+ const StringPiece& value, uint32_t typeMask,
+ const std::function<void(const ResourceName&)>& onCreateReference = {});
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& resFile,
+ const NameMangler* mangler);
-} // 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..eb62b1b 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -14,179 +14,188 @@
* 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 actualPriv = false;
+ EXPECT_TRUE(ResourceUtils::parseResourceName("android:color/foo", &actual,
+ &actualPriv));
+ EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
+ EXPECT_FALSE(actualPriv);
- 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, &actualPriv));
+ EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, "foo"), actual);
+ EXPECT_FALSE(actualPriv);
- 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,
+ &actualPriv));
+ EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
+ EXPECT_TRUE(actualPriv);
- EXPECT_FALSE(ResourceUtils::parseResourceName(StringPiece(), &actual, &actualPriv));
+ EXPECT_FALSE(
+ ResourceUtils::parseResourceName(StringPiece(), &actual, &actualPriv));
}
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 privateRef = false;
+ EXPECT_TRUE(ResourceUtils::parseReference("@color/foo", &actual, &create,
+ &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_FALSE(privateRef);
}
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 privateRef = false;
+ EXPECT_TRUE(ResourceUtils::parseReference("@android:color/foo", &actual,
+ &create, &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_FALSE(privateRef);
}
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 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);
}
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 privateRef = false;
+ EXPECT_TRUE(ResourceUtils::parseReference("@+android:id/foo", &actual,
+ &create, &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_TRUE(create);
+ EXPECT_FALSE(privateRef);
}
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 privateRef = false;
+ EXPECT_TRUE(ResourceUtils::parseReference("@*android:id/foo", &actual,
+ &create, &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_TRUE(privateRef);
}
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 privateRef = false;
+ ResourceNameRef actual;
+ EXPECT_FALSE(ResourceUtils::parseReference("@+android:color/foo", &actual,
+ &create, &privateRef));
}
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 errStr;
+ Maybe<Reference> ref =
+ ResourceUtils::parseStyleParentReference("@android:style/foo", &errStr);
+ 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", &errStr);
+ 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", &errStr);
+ 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", &errStr);
+ 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", &errStr);
+ 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", &errStr);
+ 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", &errStr);
+ 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", &errStr);
+ 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", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ EXPECT_TRUE(ref.value().privateReference);
}
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..60590b6 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -14,14 +14,14 @@
* limitations under the License.
*/
+#include "ResourceValues.h"
#include "Resource.h"
#include "ResourceUtils.h"
-#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "util/Util.h"
-#include <algorithm>
#include <androidfw/ResourceTypes.h>
+#include <algorithm>
#include <limits>
#include <set>
@@ -29,742 +29,745 @@
template <typename Derived>
void BaseValue<Derived>::accept(RawValueVisitor* visitor) {
- visitor->visit(static_cast<Derived*>(this));
+ visitor->visit(static_cast<Derived*>(this));
}
template <typename Derived>
void BaseItem<Derived>::accept(RawValueVisitor* visitor) {
- visitor->visit(static_cast<Derived*>(this));
+ 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;
+ 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;
+ 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;
+ 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;
+ *out << "(raw string) " << *value;
}
-Reference::Reference() : referenceType(Type::kResource) {
-}
+Reference::Reference() : referenceType(Type::kResource) {}
-Reference::Reference(const ResourceNameRef& n, Type t) :
- name(n.toResourceName()), referenceType(t) {
-}
+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 ResourceId& i, Type type)
+ : id(i), referenceType(type) {}
-Reference::Reference(const ResourceNameRef& n, const ResourceId& i) :
- name(n.toResourceName()), id(i), referenceType(Type::kResource) {
-}
+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;
+ 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;
+ 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);
+ return new Reference(*this);
}
void Reference::print(std::ostream* out) const {
- *out << "(reference) ";
- if (referenceType == Reference::Type::kResource) {
- *out << "@";
- if (privateReference) {
- *out << "*";
- }
- } else {
- *out << "?";
+ *out << "(reference) ";
+ if (referenceType == Reference::Type::kResource) {
+ *out << "@";
+ if (privateReference) {
+ *out << "*";
}
+ } else {
+ *out << "?";
+ }
- if (name) {
- *out << name.value();
- }
+ if (name) {
+ *out << name.value();
+ }
- if (id && !Res_INTERNALID(id.value().id)) {
- *out << " " << id.value();
- }
+ if (id && !Res_INTERNALID(id.value().id)) {
+ *out << " " << id.value();
+ }
}
bool Id::equals(const Value* value) const {
- return valueCast<Id>(value) != nullptr;
+ 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;
+ 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);
-}
+Id* Id::clone(StringPool* /*newPool*/) const { return new Id(*this); }
-void Id::print(std::ostream* out) const {
- *out << "(id)";
-}
+void Id::print(std::ostream* out) const { *out << "(id)"; }
-String::String(const StringPool::Ref& ref) : value(ref) {
-}
+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;
+ 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;
- }
+ // 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;
+ 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;
+ 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 << "\"";
+ *out << "(string) \"" << *value << "\"";
}
-StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {
-}
+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;
- });
- }
+ 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;
+ });
+ }
+ return false;
}
bool StyledString::flatten(android::Res_value* outValue) const {
- if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
- return false;
- }
+ 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;
+ outValue->dataType = android::Res_value::TYPE_STRING;
+ outValue->data =
+ util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
+ return true;
}
StyledString* StyledString::clone(StringPool* newPool) const {
- StyledString* str = new StyledString(newPool->makeRef(value));
- str->mComment = mComment;
- str->mSource = mSource;
- return str;
+ StyledString* str = new StyledString(newPool->makeRef(value));
+ str->mComment = mComment;
+ str->mSource = mSource;
+ 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.firstChar << "," << span.lastChar;
- }
+ *out << "(styled string) \"" << *value->str << "\"";
+ for (const StringPool::Span& span : value->spans) {
+ *out << " " << *span.name << ":" << span.firstChar << "," << span.lastChar;
+ }
}
-FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {
-}
+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;
+ const FileReference* other = valueCast<FileReference>(value);
+ if (!other) {
+ return false;
+ }
+ return *path == *other->path;
}
bool FileReference::flatten(android::Res_value* outValue) const {
- if (path.getIndex() > std::numeric_limits<uint32_t>::max()) {
- return false;
- }
+ 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;
+ outValue->dataType = android::Res_value::TYPE_STRING;
+ outValue->data = util::hostToDevice32(static_cast<uint32_t>(path.getIndex()));
+ return true;
}
FileReference* FileReference::clone(StringPool* newPool) const {
- FileReference* fr = new FileReference(newPool->makeRef(*path));
- fr->file = file;
- fr->mComment = mComment;
- fr->mSource = mSource;
- return fr;
+ FileReference* fr = new FileReference(newPool->makeRef(*path));
+ fr->file = file;
+ fr->mComment = mComment;
+ fr->mSource = mSource;
+ return fr;
}
void FileReference::print(std::ostream* out) const {
- *out << "(file) " << *path;
+ *out << "(file) " << *path;
}
-BinaryPrimitive::BinaryPrimitive(const android::Res_value& val) : value(val) {
-}
+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;
+ 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;
+ outValue->dataType = value.dataType;
+ outValue->data = util::hostToDevice32(value.data);
+ return true;
}
BinaryPrimitive* BinaryPrimitive::clone(StringPool* /*newPool*/) const {
- return new BinaryPrimitive(*this);
+ 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;
- }
+ 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)
+ : typeMask(t),
+ minInt(std::numeric_limits<int32_t>::min()),
+ maxInt(std::numeric_limits<int32_t>::max()) {
+ mWeak = 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;
- }
+ 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 (typeMask != other->typeMask || minInt != other->minInt ||
+ maxInt != other->maxInt) {
+ 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*> 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*> 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*> 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;
+ });
- 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(sortedA.begin(), sortedA.end(), sortedB.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);
+ return new Attribute(*this);
}
void Attribute::printMask(std::ostream* out) const {
- if (typeMask == android::ResTable_map::TYPE_ANY) {
- *out << "any";
- return;
- }
+ if (typeMask == 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";
+ 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_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_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_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_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_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_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_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_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";
+ 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);
+ *out << "(attr) ";
+ printMask(out);
- if (!symbols.empty()) {
- *out << " [" << util::joiner(symbols, ", ") << "]";
- }
+ if (!symbols.empty()) {
+ *out << " [" << util::joiner(symbols, ", ") << "]";
+ }
- if (minInt != std::numeric_limits<int32_t>::min()) {
- *out << " min=" << minInt;
- }
+ if (minInt != std::numeric_limits<int32_t>::min()) {
+ *out << " min=" << minInt;
+ }
- if (maxInt != std::numeric_limits<int32_t>::max()) {
- *out << " max=" << maxInt;
- }
+ if (maxInt != std::numeric_limits<int32_t>::max()) {
+ *out << " max=" << maxInt;
+ }
- if (isWeak()) {
- *out << " [weak]";
- }
+ if (isWeak()) {
+ *out << " [weak]";
+ }
}
-static void buildAttributeMismatchMessage(DiagMessage* msg, const Attribute* attr,
+static void buildAttributeMismatchMessage(DiagMessage* msg,
+ const Attribute* attr,
const Item* value) {
- *msg << "expected";
- if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
- *msg << " boolean";
- }
+ *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_COLOR) {
+ *msg << " color";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
- *msg << " dimension";
- }
+ 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_ENUM) {
+ *msg << " enum";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
- *msg << " flags";
- }
+ 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_FLOAT) {
+ *msg << " float";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
- *msg << " fraction";
- }
+ 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_INTEGER) {
+ *msg << " integer";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
- *msg << " reference";
- }
+ if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
+ *msg << " reference";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
- *msg << " string";
- }
+ if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
+ *msg << " string";
+ }
- *msg << " but got " << *value;
+ *msg << " but got " << *value;
}
bool Attribute::matches(const Item* item, DiagMessage* outMsg) const {
- android::Res_value val = {};
- item->flatten(&val);
+ 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;
- }
+ // 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 true;
+ 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;
- }
+ 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;
- }
+ 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*> 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;
- });
+ 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());
- });
+ 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;
+ 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 << "(style) ";
+ if (parent && parent.value().name) {
+ if (parent.value().privateReference) {
+ *out << "*";
}
- *out << " ["
- << util::joiner(entries, ", ")
- << "]";
+ *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();
- } else {
- out << "???";
- }
- out << " = ";
- value.value->print(&out);
- return out;
+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 Array::equals(const Value* value) const {
- const Array* other = valueCast<Array>(value);
- if (!other) {
- return false;
- }
+ const Array* other = valueCast<Array>(value);
+ if (!other) {
+ return false;
+ }
- if (items.size() != other->items.size()) {
- 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());
- });
+ 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* 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;
+ 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;
}
void Array::print(std::ostream* out) const {
- *out << "(array) ["
- << util::joiner(items, ", ")
- << "]";
+ *out << "(array) [" << util::joiner(items, ", ") << "]";
}
bool Plural::equals(const Value* value) const {
- const Plural* other = valueCast<Plural>(value);
- if (!other) {
- return false;
- }
+ const Plural* other = valueCast<Plural>(value);
+ if (!other) {
+ return false;
+ }
- if (values.size() != other->values.size()) {
- 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());
- });
+ 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* 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));
- }
+ 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;
+ }
+ return p;
}
void Plural::print(std::ostream* out) const {
- *out << "(plural)";
- if (values[Zero]) {
- *out << " zero=" << *values[Zero];
- }
+ *out << "(plural)";
+ if (values[Zero]) {
+ *out << " zero=" << *values[Zero];
+ }
- if (values[One]) {
- *out << " one=" << *values[One];
- }
+ if (values[One]) {
+ *out << " one=" << *values[One];
+ }
- if (values[Two]) {
- *out << " two=" << *values[Two];
- }
+ if (values[Two]) {
+ *out << " two=" << *values[Two];
+ }
- if (values[Few]) {
- *out << " few=" << *values[Few];
- }
+ if (values[Few]) {
+ *out << " few=" << *values[Few];
+ }
- if (values[Many]) {
- *out << " many=" << *values[Many];
- }
+ if (values[Many]) {
+ *out << " many=" << *values[Many];
+ }
}
-static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Item>& item) {
- return out << *item;
+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;
- }
+ const Styleable* other = valueCast<Styleable>(value);
+ if (!other) {
+ return false;
+ }
- if (entries.size() != other->entries.size()) {
- 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);
- });
+ 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* /*newPool*/) const {
- return new Styleable(*this);
+ return new Styleable(*this);
}
void Styleable::print(std::ostream* out) const {
- *out << "(styleable) " << " ["
- << util::joiner(entries, ", ")
- << "]";
+ *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.valueOrDefault({}).compare(b.name.valueOrDefault({}));
+ 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());
+ // 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..a28ffe5 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -23,8 +23,8 @@
#include "io/File.h"
#include "util/Maybe.h"
-#include <array>
#include <androidfw/ResourceTypes.h>
+#include <array>
#include <ostream>
#include <vector>
@@ -40,84 +40,64 @@
* 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 mWeak; }
- void setWeak(bool val) {
- mWeak = val;
- }
+ void setWeak(bool val) { mWeak = 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) { mTranslateable = val; }
- // Default true.
- bool isTranslateable() const {
- return mTranslateable;
- }
+ // Default true.
+ bool isTranslateable() const { return mTranslateable; }
- /**
- * 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 mSource; }
- void setSource(const Source& source) {
- mSource = source;
- }
+ void setSource(const Source& source) { mSource = source; }
- void setSource(Source&& source) {
- mSource = std::move(source);
- }
+ void setSource(Source&& source) { mSource = 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 mComment; }
- void setComment(const StringPiece& str) {
- mComment = str.toString();
- }
+ void setComment(const StringPiece& str) { mComment = str.toString(); }
- void setComment(std::string&& str) {
- mComment = std::move(str);
- }
+ void setComment(std::string&& str) { mComment = 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.
+ */
+ virtual Value* clone(StringPool* newPool) 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 mSource;
+ std::string mComment;
+ bool mWeak = false;
+ bool mTranslateable = true;
};
/**
@@ -125,23 +105,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* newPool) 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* outValue) const = 0;
};
/**
@@ -149,35 +130,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 referenceType;
+ bool privateReference = 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);
+ explicit 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* outValue) const override;
+ Reference* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
};
bool operator<(const Reference&, const Reference&);
@@ -187,11 +170,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() { 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;
};
/**
@@ -200,164 +183,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* outValue) const override;
+ RawString* clone(StringPool* newPool) 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* outValue) const override;
+ String* clone(StringPool* newPool) 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* outValue) const override;
+ StyledString* clone(StringPool* newPool) 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* outValue) const override;
+ FileReference* clone(StringPool* newPool) 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* outValue) const override;
+ BinaryPrimitive* clone(StringPool* newPool) 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 typeMask;
+ int32_t minInt;
+ int32_t maxInt;
+ 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* newPool) const override;
+ void printMask(std::ostream* out) const;
+ void print(std::ostream* out) const override;
+ bool matches(const Item* item, DiagMessage* outMsg) 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 parentInferred = 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* newPool) 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* newPool) 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* newPool) 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..4b6b122 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -20,96 +20,96 @@
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..75375da 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 lessEntryId(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;
+ 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;
}
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;
- }
+ 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);
+ 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..bd17fe4 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -24,33 +24,33 @@
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();
-} // 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..3b70acb 100644
--- a/tools/aapt2/SdkConstants_test.cpp
+++ b/tools/aapt2/SdkConstants_test.cpp
@@ -21,18 +21,18 @@
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..422b361 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -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..a167a6a 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -19,395 +19,400 @@
#include "util/StringPiece.h"
#include "util/Util.h"
-#include <algorithm>
#include <androidfw/ResourceTypes.h>
+#include <algorithm>
#include <memory>
#include <string>
namespace aapt {
-StringPool::Ref::Ref() : mEntry(nullptr) {
-}
+StringPool::Ref::Ref() : mEntry(nullptr) {}
StringPool::Ref::Ref(const StringPool::Ref& rhs) : mEntry(rhs.mEntry) {
- if (mEntry != nullptr) {
- mEntry->ref++;
- }
+ if (mEntry != nullptr) {
+ mEntry->ref++;
+ }
}
StringPool::Ref::Ref(StringPool::Entry* entry) : mEntry(entry) {
- if (mEntry != nullptr) {
- mEntry->ref++;
- }
+ if (mEntry != nullptr) {
+ mEntry->ref++;
+ }
}
StringPool::Ref::~Ref() {
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
+ if (mEntry != nullptr) {
+ mEntry->ref--;
+ }
}
StringPool::Ref& StringPool::Ref::operator=(const StringPool::Ref& rhs) {
- if (rhs.mEntry != nullptr) {
- rhs.mEntry->ref++;
- }
+ if (rhs.mEntry != nullptr) {
+ rhs.mEntry->ref++;
+ }
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
- mEntry = rhs.mEntry;
- return *this;
+ if (mEntry != nullptr) {
+ mEntry->ref--;
+ }
+ mEntry = rhs.mEntry;
+ return *this;
}
const std::string* StringPool::Ref::operator->() const {
- return &mEntry->value;
+ return &mEntry->value;
}
-const std::string& StringPool::Ref::operator*() const {
- return mEntry->value;
-}
+const std::string& StringPool::Ref::operator*() const { return mEntry->value; }
-size_t StringPool::Ref::getIndex() const {
- return mEntry->index;
-}
+size_t StringPool::Ref::getIndex() const { return mEntry->index; }
const StringPool::Context& StringPool::Ref::getContext() const {
- return mEntry->context;
+ return mEntry->context;
}
-StringPool::StyleRef::StyleRef() : mEntry(nullptr) {
-}
+StringPool::StyleRef::StyleRef() : mEntry(nullptr) {}
-StringPool::StyleRef::StyleRef(const StringPool::StyleRef& rhs) : mEntry(rhs.mEntry) {
- if (mEntry != nullptr) {
- mEntry->ref++;
- }
+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++;
- }
+ if (mEntry != nullptr) {
+ mEntry->ref++;
+ }
}
StringPool::StyleRef::~StyleRef() {
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
+ if (mEntry != nullptr) {
+ mEntry->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.mEntry != nullptr) {
+ rhs.mEntry->ref++;
+ }
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
- mEntry = rhs.mEntry;
- return *this;
+ if (mEntry != nullptr) {
+ mEntry->ref--;
+ }
+ mEntry = rhs.mEntry;
+ return *this;
}
const StringPool::StyleEntry* StringPool::StyleRef::operator->() const {
- return mEntry;
+ return mEntry;
}
const StringPool::StyleEntry& StringPool::StyleRef::operator*() const {
- return *mEntry;
+ return *mEntry;
}
-size_t StringPool::StyleRef::getIndex() const {
- return mEntry->str.getIndex();
-}
+size_t StringPool::StyleRef::getIndex() const { return mEntry->str.getIndex(); }
const StringPool::Context& StringPool::StyleRef::getContext() const {
- return mEntry->str.getContext();
+ return mEntry->str.getContext();
}
StringPool::Ref StringPool::makeRef(const StringPiece& str) {
- return makeRefImpl(str, Context{}, true);
+ 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 = mIndexedStrings.find(str);
+ if (iter != std::end(mIndexedStrings)) {
+ 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 = mStrings.size();
+ entry->ref = 0;
+ mStrings.emplace_back(entry);
+ mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
+ return Ref(entry);
}
StringPool::StyleRef StringPool::makeRef(const StyleString& str) {
- return makeRef(str, Context{});
+ 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 = mStrings.size();
+ entry->ref = 0;
+ mStrings.emplace_back(entry);
+ mIndexedStrings.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->ref = 0;
- mStyles.emplace_back(styleEntry);
- return StyleRef(styleEntry);
+ 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->ref = 0;
+ mStyles.emplace_back(styleEntry);
+ return StyleRef(styleEntry);
}
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));
+ 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));
- 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);
+ 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);
}
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();
+ 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;
- }
+ // 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);
+ 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;
- }
+ 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 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;
- }
- );
+ 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));
+ // 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;
- }
+ // 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);
- }
- );
+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;
- }
+ // 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(std::begin(mStyles), std::end(mStyles),
[](const std::unique_ptr<StyleEntry>& lhs,
const std::unique_ptr<StyleEntry>& rhs) -> bool {
- return lhs->str.getIndex() < rhs->str.getIndex();
- }
- );
+ return lhs->str.getIndex() < rhs->str.getIndex();
+ });
}
template <typename T>
static T* encodeLength(T* data, size_t length) {
- static_assert(std::is_integral<T>::value, "wat.");
+ 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_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 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();
+ 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();
+ if (utf8) {
+ header->flags |= android::ResStringPool_header::UTF8_FLAG;
+ }
+
+ 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) {
- header->flags |= android::ResStringPool_header::UTF8_FLAG;
+ 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;
}
- 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.
- }
- }
-
+ // 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();
-
- 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;
+ }
+ header->header.size = out->size() - startIndex;
+ return true;
}
bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
- return flatten(out, pool, true);
+ return flatten(out, pool, true);
}
bool StringPool::flattenUtf16(BigBuffer* out, const StringPool& pool) {
- return flatten(out, pool, false);
+ return flatten(out, pool, false);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 13545be..05c26e7 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -17,207 +17,205 @@
#ifndef AAPT_STRING_POOL_H
#define AAPT_STRING_POOL_H
-#include "util/BigBuffer.h"
#include "ConfigDescription.h"
+#include "util/BigBuffer.h"
#include "util/StringPiece.h"
#include <functional>
-#include <unordered_map>
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
namespace aapt {
struct Span {
- std::string name;
- uint32_t firstChar;
- uint32_t lastChar;
+ std::string name;
+ uint32_t firstChar;
+ uint32_t lastChar;
};
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:
+ struct Context {
+ uint32_t priority;
+ ConfigDescription config;
+ };
- class Entry;
+ class Entry;
- class Ref {
- public:
- Ref();
- Ref(const Ref&);
- ~Ref();
+ class Ref {
+ public:
+ Ref();
+ Ref(const Ref&);
+ ~Ref();
- Ref& operator=(const Ref& rhs);
- const std::string* operator->() const;
- const std::string& operator*() const;
+ Ref& operator=(const Ref& rhs);
+ const std::string* operator->() const;
+ const std::string& operator*() const;
- size_t getIndex() const;
- const Context& getContext() const;
+ size_t getIndex() const;
+ const Context& getContext() const;
- private:
- friend class StringPool;
+ private:
+ friend class StringPool;
- explicit Ref(Entry* entry);
+ explicit Ref(Entry* entry);
- Entry* mEntry;
- };
+ Entry* mEntry;
+ };
- class StyleEntry;
+ class StyleEntry;
- class StyleRef {
- public:
- StyleRef();
- StyleRef(const StyleRef&);
- ~StyleRef();
+ class StyleRef {
+ public:
+ StyleRef();
+ StyleRef(const StyleRef&);
+ ~StyleRef();
- StyleRef& operator=(const StyleRef& rhs);
- const StyleEntry* operator->() const;
- const StyleEntry& operator*() const;
+ StyleRef& operator=(const StyleRef& rhs);
+ const StyleEntry* operator->() const;
+ const StyleEntry& operator*() const;
- size_t getIndex() const;
- const Context& getContext() const;
+ size_t getIndex() const;
+ const Context& getContext() const;
- private:
- friend class StringPool;
+ private:
+ friend class StringPool;
- explicit StyleRef(StyleEntry* entry);
+ explicit StyleRef(StyleEntry* entry);
- StyleEntry* mEntry;
- };
+ StyleEntry* mEntry;
+ };
- class Entry {
- public:
- std::string value;
- Context context;
- size_t index;
+ class Entry {
+ public:
+ std::string value;
+ Context context;
+ size_t index;
- private:
- friend class StringPool;
- friend class Ref;
+ private:
+ friend class StringPool;
+ friend class Ref;
- int ref;
- };
+ int ref;
+ };
- struct Span {
- Ref name;
- uint32_t firstChar;
- uint32_t lastChar;
- };
+ struct Span {
+ Ref name;
+ uint32_t firstChar;
+ uint32_t lastChar;
+ };
- class StyleEntry {
- public:
- Ref str;
- std::vector<Span> spans;
+ class StyleEntry {
+ public:
+ Ref str;
+ std::vector<Span> spans;
- private:
- friend class StringPool;
- friend class StyleRef;
+ private:
+ friend class StringPool;
+ friend class StyleRef;
- int ref;
- };
+ int ref;
+ };
- using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator;
+ using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator;
- static bool flattenUtf8(BigBuffer* out, const StringPool& pool);
- static bool flattenUtf16(BigBuffer* out, const StringPool& pool);
+ static bool flattenUtf8(BigBuffer* out, const StringPool& pool);
+ static bool flattenUtf16(BigBuffer* out, const StringPool& pool);
- StringPool() = default;
- StringPool(const StringPool&) = delete;
+ StringPool() = default;
+ StringPool(const StringPool&) = delete;
- /**
- * 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 string to the pool, unless it already exists. Returns
+ * a reference to the string in the pool.
+ */
+ Ref makeRef(const StringPiece& 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 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 and returns a reference to it.
- */
- StyleRef makeRef(const StyleString& str);
+ /**
+ * Adds a style to the string pool and returns a reference to it.
+ */
+ StyleRef makeRef(const StyleString& str);
- /**
- * 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 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 from another string pool. Returns a reference to the
- * style in the string pool.
- */
- StyleRef makeRef(const StyleRef& ref);
+ /**
+ * Adds a style from another string pool. Returns a reference to the
+ * style in the string pool.
+ */
+ StyleRef makeRef(const StyleRef& ref);
- /**
- * Moves pool into this one without coalescing strings. When this
- * function returns, pool will be empty.
- */
- void merge(StringPool&& pool);
+ /**
+ * Moves pool into this one without coalescing strings. When this
+ * function returns, pool will be empty.
+ */
+ void merge(StringPool&& pool);
- /**
- * Retuns the number of strings in the table.
- */
- inline size_t size() const;
+ /**
+ * Retuns the number of strings in the table.
+ */
+ inline size_t size() const;
- /**
- * Reserves space for strings and styles as an optimization.
- */
- void hintWillAdd(size_t stringCount, size_t styleCount);
+ /**
+ * Reserves space for strings and styles as an optimization.
+ */
+ void hintWillAdd(size_t stringCount, size_t styleCount);
- /**
- * Sorts the strings according to some comparison function.
- */
- void sort(const std::function<bool(const Entry&, const Entry&)>& cmp);
+ /**
+ * Sorts the strings according to some comparison function.
+ */
+ void sort(const std::function<bool(const Entry&, const Entry&)>& cmp);
- /**
- * Removes any strings that have no references.
- */
- void prune();
+ /**
+ * Removes any strings that have no references.
+ */
+ void prune();
-private:
- friend const_iterator begin(const StringPool& pool);
- friend const_iterator end(const StringPool& pool);
+ private:
+ friend const_iterator begin(const StringPool& pool);
+ friend const_iterator end(const StringPool& pool);
- static bool flatten(BigBuffer* out, const StringPool& pool, bool utf8);
+ static bool flatten(BigBuffer* out, const StringPool& pool, bool utf8);
- Ref makeRefImpl(const StringPiece& str, const Context& context, bool unique);
+ Ref makeRefImpl(const StringPiece& str, const Context& context, bool unique);
- std::vector<std::unique_ptr<Entry>> mStrings;
- std::vector<std::unique_ptr<StyleEntry>> mStyles;
- std::unordered_multimap<StringPiece, Entry*> mIndexedStrings;
+ std::vector<std::unique_ptr<Entry>> mStrings;
+ std::vector<std::unique_ptr<StyleEntry>> mStyles;
+ std::unordered_multimap<StringPiece, Entry*> mIndexedStrings;
};
//
// Inline implementation
//
-inline size_t StringPool::size() const {
- return mStrings.size();
-}
+inline size_t StringPool::size() const { return mStrings.size(); }
inline StringPool::const_iterator begin(const StringPool& pool) {
- return pool.mStrings.begin();
+ return pool.mStrings.begin();
}
inline StringPool::const_iterator end(const StringPool& pool) {
- return pool.mStrings.end();
+ return pool.mStrings.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..2a7e1dd 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -23,246 +23,245 @@
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.getIndex());
+ EXPECT_EQ(1u, ref2.getIndex());
+ EXPECT_EQ(2u, ref3.getIndex());
}
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.getIndex());
- EXPECT_EQ(*(ref2->str), "a");
- EXPECT_EQ(1u, ref2.getIndex());
+ EXPECT_EQ(*(ref2->str), "a");
+ EXPECT_EQ(1u, ref2.getIndex());
- EXPECT_EQ(*ref3, "m");
- EXPECT_EQ(2u, ref3.getIndex());
+ EXPECT_EQ(*ref3, "m");
+ EXPECT_EQ(2u, ref3.getIndex());
- 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.getIndex());
- EXPECT_EQ(*ref, "z");
- EXPECT_EQ(2u, ref.getIndex());
+ EXPECT_EQ(*(ref2->str), "a");
+ EXPECT_EQ(0u, ref2.getIndex());
- 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.getIndex());
}
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.getIndex(), ref.getIndex());
+ EXPECT_EQ(ref5.getIndex(), ref2.getIndex());
+ EXPECT_EQ(ref6.getIndex(), ref3.getIndex());
}
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.getIndex());
+ 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.firstChar);
+ EXPECT_EQ(6u, span.lastChar);
}
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.getIndex(), styleRef.getIndex());
}
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.getIndex());
+ EXPECT_EQ(1u, ref2.getIndex());
+ EXPECT_EQ(2u, ref3.getIndex());
+ EXPECT_EQ(3u, ref4.getIndex());
+ EXPECT_EQ(4u, ref5.getIndex());
- 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..121c337 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -17,91 +17,94 @@
#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
+ * 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) { /* 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 +112,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 +122,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));
+ return valueCast<T>(const_cast<Value*>(value));
}
/**
@@ -139,30 +138,32 @@
*/
template <typename T>
T* valueCast(Value* value) {
- if (!value) {
- return nullptr;
- }
- DynCastVisitor<T> visitor;
- value->accept(&visitor);
- return visitor.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& configValue : entry->values) {
+ configValue->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..ed9c7f6 100644
--- a/tools/aapt2/ValueVisitor_test.cpp
+++ b/tools/aapt2/ValueVisitor_test.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "ResourceValues.h"
#include "ValueVisitor.h"
+#include "ResourceValues.h"
#include "test/Test.h"
#include "util/Util.h"
@@ -24,63 +24,63 @@
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*> visitedRefs;
+ Style* visitedStyle = nullptr;
- void visit(Reference* ref) override {
- visitedRefs.push_back(ref);
- }
+ void visit(Reference* ref) override { visitedRefs.push_back(ref); }
- void visit(Style* style) override {
- visitedStyle = style;
- ValueVisitor::visit(style);
- }
+ void visit(Style* style) override {
+ visitedStyle = 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.visitedStyle);
- // 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.visitedRefs.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..d001228 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -33,8 +33,8 @@
#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 <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <android-base/errors.h>
#include <android-base/file.h>
@@ -48,16 +48,18 @@
namespace aapt {
struct ResourcePathData {
- Source source;
- std::string resourceDir;
- std::string name;
- std::string extension;
+ Source source;
+ std::string resourceDir;
+ 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 configStr;
+ ConfigDescription config;
};
/**
@@ -66,696 +68,735 @@
*/
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 {};
+ std::vector<std::string> parts = util::split(path, file::sDirSep);
+ if (parts.size() < 2) {
+ if (outError) *outError = "bad resource path";
+ return {};
+ }
+
+ std::string& dir = parts[parts.size() - 2];
+ StringPiece dirStr = dir;
+
+ 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& dir = parts[parts.size() - 2];
- StringPiece dirStr = dir;
+ 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);
+ }
- 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), dirStr.toString(),
+ name.toString(), extension.toString(),
+ configStr.toString(), config};
}
struct CompileOptions {
- std::string outputPath;
- Maybe<std::string> resDir;
- bool pseudolocalize = false;
- bool legacyMode = false;
- bool verbose = false;
+ std::string outputPath;
+ Maybe<std::string> resDir;
+ bool pseudolocalize = false;
+ bool legacyMode = 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();
+ 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 bool isHidden(const StringPiece& filename) {
- return util::stringStartsWith(filename, ".");
+ return util::stringStartsWith(filename, ".");
}
/**
* Walks the res directory structure, looking for resource files.
*/
-static bool loadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
+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));
+ 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));
+ return false;
+ }
+
+ 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()));
}
-
- 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 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();
+ ResourceTable table;
+ {
+ std::ifstream fin(pathData.source.path, std::ifstream::binary);
+ if (!fin) {
+ context->getDiagnostics()->error(DiagMessage(pathData.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 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;
}
- // 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 pseudolocaleGenerator;
+ if (!pseudolocaleGenerator.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(outputPath, 0)) {
+ context->getDiagnostics()->error(DiagMessage(outputPath)
+ << "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 copyingAdaptor(writer);
+
+ std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(&table);
+ if (!pbTable->SerializeToZeroCopyStream(©ingAdaptor)) {
+ context->getDiagnostics()->error(DiagMessage(outputPath)
+ << "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(outputPath)
+ << "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& outputPath,
+ 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(outputPath, 0)) {
+ diag->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);
+
+ // 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;
}
+ }
- // 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(outputPath) << "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& outputPath,
+ 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(outputPath, 0)) {
+ diag->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);
+
+ // 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;
}
+ }
- // 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(outputPath) << "failed to finish writing data");
+ return false;
+ }
+ return true;
}
-static bool flattenXmlToOutStream(IAaptContext* context, const StringPiece& outputPath,
+static bool flattenXmlToOutStream(IAaptContext* context,
+ const StringPiece& outputPath,
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 xmlFlattenerOptions;
+ xmlFlattenerOptions.keepRawValues = true;
+ XmlFlattener flattener(&buffer, xmlFlattenerOptions);
+ 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> pbCompiledFile =
+ serializeCompiledFileToPb(xmlRes->file);
+ out->WriteCompiledFile(pbCompiledFile.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(outputPath)
+ << "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");
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "compiling XML");
+ }
+
+ 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;
}
- 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(), pathData.source);
- xmlRes = xml::inflate(&fin, context->getDiagnostics(), pathData.source);
+ fin.close();
+ }
- fin.close();
+ if (!xmlRes) {
+ 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;
}
- if (!xmlRes) {
+ for (auto& inlineXmlDoc : inlineDocuments) {
+ if (!flattenXmlToOutStream(context, outputPath, inlineXmlDoc.get(),
+ &outputStream)) {
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(outputPath)
+ << "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) : mBuffer(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 = mBuffer->nextBlock(&count);
+ *len = static_cast<int>(count);
+ return true;
+ }
- void BackUp(int count) override {
- mBuffer->backUp(count);
- }
+ void BackUp(int count) override { mBuffer->backUp(count); }
- int64_t ByteCount() const override {
- return mBuffer->size();
- }
+ int64_t ByteCount() const override { return mBuffer->size(); }
- bool HadError() const override {
- return false;
- }
+ bool HadError() const override { return false; }
-private:
- BigBuffer* mBuffer;
+ private:
+ BigBuffer* mBuffer;
- 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");
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "compiling PNG");
+ }
+
+ BigBuffer buffer(4096);
+ ResourceFile resFile;
+ resFile.name =
+ ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
+ resFile.config = pathData.config;
+ resFile.source = pathData.source;
+
+ {
+ std::string content;
+ if (!android::base::ReadFileToString(pathData.source.path, &content)) {
+ context->getDiagnostics()->error(
+ DiagMessage(pathData.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 crunchedPngBuffer(4096);
+ BigBufferOutputStream crunchedPngBufferOut(&crunchedPngBuffer);
- {
- 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 pngChunkFilter(content);
+ std::unique_ptr<Image> image = readPng(context, &pngChunkFilter);
+ if (!image) {
+ return false;
}
- if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
- context->getDiagnostics())) {
+ 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);
+ }
}
- return true;
+
+ // 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());
+ }
+ }
+
+ if (!writeHeaderAndBufferToWriter(outputPath, resFile, 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");
- }
+ const ResourcePathData& pathData,
+ IArchiveWriter* writer, const std::string& outputPath) {
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.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 resFile;
+ resFile.name =
+ ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
+ resFile.config = pathData.config;
+ resFile.source = pathData.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 errorStr;
+ Maybe<android::FileMap> f = file::mmapPath(pathData.source.path, &errorStr);
+ if (!f) {
+ context->getDiagnostics()->error(DiagMessage(pathData.source) << errorStr);
+ return false;
+ }
- if (!writeHeaderAndMmapToWriter(outputPath, resFile, f.value(), writer,
- context->getDiagnostics())) {
- return false;
- }
- return true;
+ if (!writeHeaderAndMmapToWriter(outputPath, resFile, 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) { mVerbose = val; }
- bool verbose() override {
- return mVerbose;
- }
+ bool verbose() override { return mVerbose; }
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
- 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 mDiagnostics;
+ bool mVerbose = 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;
+ 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.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;
+ }
+
+ context.setVerbose(verbose);
+
+ std::unique_ptr<IArchiveWriter> archiveWriter;
+
+ 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;
}
- context.setVerbose(verbose);
+ if (!loadInputFilesFromDir(&context, options, &inputData)) {
+ return 1;
+ }
- std::unique_ptr<IArchiveWriter> archiveWriter;
+ archiveWriter = createZipFileArchiveWriter(context.getDiagnostics(),
+ options.outputPath);
- 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 {
+ inputData.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 errorStr;
+ if (Maybe<ResourcePathData> pathData =
+ extractResourcePathData(arg, &errorStr)) {
+ inputData.push_back(std::move(pathData.value()));
+ } else {
+ context.getDiagnostics()->error(DiagMessage() << errorStr << " (" << arg
+ << ")");
+ return 1;
+ }
+ }
- archiveWriter = createZipFileArchiveWriter(context.getDiagnostics(), options.outputPath);
+ 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 {
- 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 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;
}
- }
-
- 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 (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 {
- 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, pathData, archiveWriter.get(),
+ outputFilename)) {
+ error = true;
+ }
}
+ } else {
+ context.getDiagnostics()->error(
+ DiagMessage() << "invalid file path '" << pathData.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..73eb066 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "ResourceTable.h"
#include "compile/IdAssigner.h"
+#include "ResourceTable.h"
#include "process/IResourceTableConsumer.h"
#include "util/Util.h"
@@ -25,178 +25,192 @@
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.packageId()) {
+ if (!type->id || type->id.value() == id.typeId()) {
+ type->id = id.typeId();
- if (!entry->id || entry->id.value() == id.entryId()) {
- entry->id = id.entryId();
- return true;
- }
- }
+ if (!entry->id || entry->id.value() == id.entryId()) {
+ entry->id = id.entryId();
+ 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 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;
}
bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
- std::map<ResourceId, ResourceName> assignedIds;
+ std::map<ResourceId, ResourceName> assignedIds;
- for (auto& package : table->packages) {
- assert(package->id && "packages must have manually assigned IDs");
+ for (auto& package : table->packages) {
+ assert(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 (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 (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 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;
+ }
}
+ }
}
+ }
- // Assign any resources without IDs the next available ID. Gaps will be filled if possible,
- // unless those IDs have been reserved.
+ 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;
+ }
+ }
+ }
- 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 assignedIdsIterEnd = assignedIds.end();
+ for (auto& package : table->packages) {
+ assert(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 resourceId(package->id.value(), 0, 0);
+ uint8_t nextExpectedTypeId = 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 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;
+ }
- // Set our expectation to be the next type ID after the reserved one we
- // just saw.
- nextExpectedTypeId = typeId + 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;
+ }
- // Move to the next reserved ID.
- ++nextTypeIter;
- }
+ // Set our expectation to be the next type ID after the reserved one
+ // we
+ // just saw.
+ nextExpectedTypeId = typeId + 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.
+ ++nextTypeIter;
}
+
+ 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++;
+ }
+ }
+ }
}
- return true;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.h b/tools/aapt2/compile/IdAssigner.h
index 06cd5e3..d399064 100644
--- a/tools/aapt2/compile/IdAssigner.h
+++ b/tools/aapt2/compile/IdAssigner.h
@@ -26,22 +26,22 @@
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)
+ : mAssignedIdMap(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>* mAssignedIdMap = 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..ff7bf5c 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -22,154 +22,163 @@
::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> maybeResult;
- // 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);
+ maybeResult = table->findResource(test::parseNameOrDie("android:dimen/two"));
+ AAPT_ASSERT_TRUE(maybeResult);
+ EXPECT_EQ(make_value<uint8_t>(2), maybeResult.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);
+ maybeResult =
+ table->findResource(test::parseNameOrDie("android:integer/three"));
+ AAPT_ASSERT_TRUE(maybeResult);
+ EXPECT_EQ(make_value<uint8_t>(3), maybeResult.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);
+ maybeResult =
+ table->findResource(test::parseNameOrDie("android:string/five"));
+ AAPT_ASSERT_TRUE(maybeResult);
+ EXPECT_EQ(make_value<uint8_t>(5), maybeResult.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);
+ maybeResult = table->findResource(test::parseNameOrDie("android:attr/bar"));
+ AAPT_ASSERT_TRUE(maybeResult);
+ EXPECT_EQ(make_value<uint16_t>(1), maybeResult.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);
+ maybeResult = table->findResource(test::parseNameOrDie("android:attr/baz"));
+ AAPT_ASSERT_TRUE(maybeResult);
+ EXPECT_EQ(make_value<uint16_t>(2), maybeResult.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> 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);
- 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& 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);
}
::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;
- }
+ std::set<uint8_t> packageIds;
+ 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 (!packageIds.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> 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;
+ }
+ }
+ }
+ }
+ 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..4cf2ea7 100644
--- a/tools/aapt2/compile/Image.h
+++ b/tools/aapt2/compile/Image.h
@@ -29,173 +29,180 @@
* 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* errOut);
- /**
- * 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 layoutBounds;
- /**
- * 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 outlineRadius = 0.0f;
- /**
- * The largest alpha value within the outline.
- */
- uint32_t outlineAlpha = 0x000000ffu;
+ /**
+ * The largest alpha value within the outline.
+ */
+ uint32_t outlineAlpha = 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> horizontalStretchRegions;
- /**
- * 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> verticalStretchRegions;
- /**
- * 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> regionColors;
- /**
- * 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* outLen) 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* outLen) 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* outLen) 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);
-} // 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..56f72b5 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include "compile/InlineXmlFormatParser.h"
#include "Debug.h"
#include "ResourceUtils.h"
-#include "compile/InlineXmlFormatParser.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
#include "xml/XmlUtil.h"
@@ -33,158 +33,172 @@
* 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 attrNamespaceUri;
+ std::string attrName;
+ };
- explicit Visitor(IAaptContext* context, xml::XmlResource* xmlResource) :
- mContext(context), mXmlResource(xmlResource) {
+ explicit Visitor(IAaptContext* context, xml::XmlResource* xmlResource)
+ : mContext(context), mXmlResource(xmlResource) {}
+
+ void visit(xml::Element* el) override {
+ if (el->namespaceUri != 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 = mXmlResource->file.source.withLine(el->lineNumber);
- 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) {
+ mContext->getDiagnostics()->error(DiagMessage(src)
+ << "missing 'name' attribute");
+ mError = true;
+ return;
}
- const std::vector<InlineDeclaration>& getInlineDeclarations() const {
- return mInlineDeclarations;
+ Maybe<Reference> ref = ResourceUtils::parseXmlAttributeName(attr->value);
+ if (!ref) {
+ mContext->getDiagnostics()->error(
+ DiagMessage(src) << "invalid XML attribute '" << attr->value << "'");
+ mError = 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> maybePkg =
+ transformPackageAlias(name.package, {});
+ if (!maybePkg) {
+ mContext->getDiagnostics()->error(DiagMessage(src)
+ << "invalid namespace prefix '"
+ << name.package << "'");
+ mError = true;
+ return;
}
-private:
- DISALLOW_COPY_AND_ASSIGN(Visitor);
+ const xml::ExtractedPackage& pkg = maybePkg.value();
+ const bool privateNamespace =
+ pkg.privateNamespace || ref.value().privateReference;
- IAaptContext* mContext;
- xml::XmlResource* mXmlResource;
- std::vector<InlineDeclaration> mInlineDeclarations;
- bool mError = false;
+ 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));
+ }
+
+ const std::vector<InlineDeclaration>& getInlineDeclarations() const {
+ return mInlineDeclarations;
+ }
+
+ bool hasError() const { return mError; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Visitor);
+
+ IAaptContext* mContext;
+ xml::XmlResource* mXmlResource;
+ std::vector<InlineDeclaration> mInlineDeclarations;
+ bool mError = 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 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;
+ }
}
- 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* parentEl = nullptr;
+ while (node->parent &&
+ (parentEl = xml::nodeCast<xml::Element>(node->parent)) == nullptr) {
+ node = node->parent;
}
- return true;
+
+ 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++;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.h b/tools/aapt2/compile/InlineXmlFormatParser.h
index 69065fd..cd8794b 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.h
+++ b/tools/aapt2/compile/InlineXmlFormatParser.h
@@ -41,25 +41,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 mQueue;
+ }
-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>> mQueue;
};
-} // 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..4adb21c 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
@@ -20,22 +20,22 @@
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 +45,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 nameRef;
+ ASSERT_TRUE(ResourceUtils::parseReference(attr->value, &nameRef));
- xml::XmlResource* extractedDoc = parser.getExtractedInlineXmlDocuments()[0].get();
- ASSERT_NE(nullptr, extractedDoc);
+ xml::XmlResource* extractedDoc =
+ parser.getExtractedInlineXmlDocuments()[0].get();
+ ASSERT_NE(nullptr, extractedDoc);
- // 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(nameRef.package, extractedDoc->file.name.package);
+ EXPECT_EQ(nameRef.type, extractedDoc->file.name.type);
+ EXPECT_EQ(nameRef.entry, extractedDoc->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(extractedDoc);
+ 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 +100,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* attrText = el->findAttribute(xml::kSchemaAndroid, "text");
+ ASSERT_NE(nullptr, attrText);
- xml::Attribute* attrDrawable = el->findAttribute(xml::kSchemaAndroid, "drawable");
- ASSERT_NE(nullptr, attrDrawable);
+ xml::Attribute* attrDrawable =
+ el->findAttribute(xml::kSchemaAndroid, "drawable");
+ ASSERT_NE(nullptr, attrDrawable);
- // The two extracted resources should have different names.
- EXPECT_NE(attrText->value, attrDrawable->value);
+ // The two extracted resources should have different names.
+ EXPECT_NE(attrText->value, attrDrawable->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* extractedDocText =
+ parser.getExtractedInlineXmlDocuments()[0].get();
+ ASSERT_NE(nullptr, extractedDocText);
+ el = xml::findRootElement(extractedDocText);
+ 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* extractedDocDrawable =
+ parser.getExtractedInlineXmlDocuments()[1].get();
+ ASSERT_NE(nullptr, extractedDocDrawable);
+ el = xml::findRootElement(extractedDocDrawable);
+ 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..8842eb7 100644
--- a/tools/aapt2/compile/NinePatch.cpp
+++ b/tools/aapt2/compile/NinePatch.cpp
@@ -28,7 +28,7 @@
// 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;
@@ -46,35 +46,37 @@
* 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 {
@@ -87,590 +89,604 @@
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();
+ std::vector<Range>* secondaryRanges, std::string* err) {
+ const int32_t length = imageLine->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 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;
}
- return true;
+
+ 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;
+ }
+ }
+ 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)
+ : mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {}
- inline int32_t getLength() const {
- return mLength;
- }
+ inline int32_t getLength() const { return mLength; }
- 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(mRows[mYOffset] + (idx + mXOffset) * 4);
+ }
-private:
- uint8_t** mRows;
- int32_t mXOffset, mYOffset, mLength;
+ private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mLength;
- 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)
+ : mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {}
- inline int32_t getLength() const {
- return mLength;
- }
+ inline int32_t getLength() const { return mLength; }
- 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(mRows[mYOffset + idx] + (mXOffset * 4));
+ }
-private:
- uint8_t** mRows;
- int32_t mXOffset, mYOffset, mLength;
+ private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mLength;
- 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)
+ : mRows(rows),
+ mXOffset(xOffset),
+ mYOffset(yOffset),
+ mXStep(xStep),
+ mYStep(yStep),
+ mLength(length) {}
- inline int32_t getLength() const {
- return mLength;
- }
+ inline int32_t getLength() const { return mLength; }
- 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(mRows[mYOffset + (idx * mYStep)] +
+ ((idx + mXOffset) * mXStep) * 4);
+ }
-private:
- uint8_t** mRows;
- int32_t mXOffset, mYOffset, mXStep, mYStep, mLength;
+ private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mXStep, mYStep, mLength;
- 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 getAlpha(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;
+ 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,
+ 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) {
+ if (padding.size() > 1) {
+ std::stringstream errStream;
+ errStream << "too many padding sections on " << edgeName << " border";
+ *err = errStream.str();
+ return false;
+ }
+
+ *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 << "too many padding sections on " << edgeName << " border";
+ errStream << "layout bounds on " << edgeName
+ << " border must start at edge";
*err = errStream.str();
return false;
+ }
+ *layoutEnd = 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>& stretchRegions,
+ int32_t length) {
+ if (stretchRegions.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 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;
}
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;
- }
+ // 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;
+ }
}
+ }
- if (getAlpha(expectedColor) == 0) {
- return android::Res_png_9patch::TRANSPARENT_COLOR;
- }
- return expectedColor;
+ if (getAlpha(expectedColor) == 0) {
+ return android::Res_png_9patch::TRANSPARENT_COLOR;
+ }
+ return expectedColor;
}
-// 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
+// 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.
//
-// 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>& 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));
+ }
+ }
}
-// 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* imageLine, int32_t* outStart,
+ int32_t* outEnd) {
+ *outStart = 0;
+ *outEnd = 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 = 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);
+ }
+ }
+ 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;
- }
+ 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;
}
- return maxAlpha;
+ }
+ return maxAlpha;
}
// 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];
+ 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,
+ 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 {};
- }
+ if (width < 3 || height < 3) {
+ *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> horizontalPadding;
+ std::vector<Range> horizontalOpticalBounds;
+ std::vector<Range> verticalPadding;
+ std::vector<Range> verticalOpticalBounds;
+ std::vector<Range> unexpectedRanges;
+ std::unique_ptr<ColorValidator> colorValidator;
- 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) {
+ 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 {};
+ }
- // Private constructor, can't use make_unique.
- auto ninePatch = std::unique_ptr<NinePatch>(new NinePatch());
+ // Private constructor, can't use make_unique.
+ auto ninePatch = std::unique_ptr<NinePatch>(new NinePatch());
- HorizontalImageLine topRow(rows, 0, 0, width);
- if (!fillRanges(&topRow, colorValidator.get(), &ninePatch->horizontalStretchRegions,
- &unexpectedRanges, err)) {
- return {};
- }
+ HorizontalImageLine topRow(rows, 0, 0, width);
+ if (!fillRanges(&topRow, colorValidator.get(),
+ &ninePatch->horizontalStretchRegions, &unexpectedRanges,
+ 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 (!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 {};
+ }
- VerticalImageLine leftCol(rows, 0, 0, height);
- if (!fillRanges(&leftCol, colorValidator.get(), &ninePatch->verticalStretchRegions,
- &unexpectedRanges, err)) {
- return {};
- }
+ VerticalImageLine leftCol(rows, 0, 0, height);
+ if (!fillRanges(&leftCol, colorValidator.get(),
+ &ninePatch->verticalStretchRegions, &unexpectedRanges, 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 (!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 {};
+ }
- HorizontalImageLine bottomRow(rows, 0, height - 1, width);
- if (!fillRanges(&bottomRow, colorValidator.get(), &horizontalPadding,
- &horizontalOpticalBounds, err)) {
- return {};
- }
+ HorizontalImageLine bottomRow(rows, 0, height - 1, width);
+ if (!fillRanges(&bottomRow, colorValidator.get(), &horizontalPadding,
+ &horizontalOpticalBounds, 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(horizontalPadding, horizontalOpticalBounds,
+ ninePatch->horizontalStretchRegions, width - 2,
+ &ninePatch->padding.left, &ninePatch->padding.right,
+ &ninePatch->layoutBounds.left,
+ &ninePatch->layoutBounds.right, "bottom", err)) {
+ return {};
+ }
- VerticalImageLine rightCol(rows, width - 1, 0, height);
- if (!fillRanges(&rightCol, colorValidator.get(), &verticalPadding,
- &verticalOpticalBounds, err)) {
- return {};
- }
+ VerticalImageLine rightCol(rows, width - 1, 0, height);
+ if (!fillRanges(&rightCol, colorValidator.get(), &verticalPadding,
+ &verticalOpticalBounds, 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(verticalPadding, verticalOpticalBounds,
+ ninePatch->verticalStretchRegions, height - 2,
+ &ninePatch->padding.top, &ninePatch->padding.bottom,
+ &ninePatch->layoutBounds.top,
+ &ninePatch->layoutBounds.bottom, "right", 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 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 {};
+ }
- ninePatch->regionColors.reserve(numRows * numCols);
- calculateRegionColors(rows, ninePatch->horizontalStretchRegions,
- ninePatch->verticalStretchRegions,
- width - 2, height - 2,
- &ninePatch->regionColors);
+ ninePatch->regionColors.reserve(numRows * numCols);
+ calculateRegionColors(rows, ninePatch->horizontalStretchRegions,
+ ninePatch->verticalStretchRegions, width - 2,
+ height - 2, &ninePatch->regionColors);
- // 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 midRow(rows, 1, height / 2, width - 2);
+ findOutlineInsets(&midRow, &ninePatch->outline.left,
+ &ninePatch->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 midCol(rows, width / 2, 1, height - 2);
+ findOutlineInsets(&midCol, &ninePatch->outline.top,
+ &ninePatch->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 outlineWidth =
+ (width - 2) - ninePatch->outline.left - ninePatch->outline.right;
+ const int32_t outlineHeight =
+ (height - 2) - ninePatch->outline.top - ninePatch->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 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));
- // 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 + ninePatch->outline.left,
+ 1 + ninePatch->outline.top, 1, 1,
+ std::min(outlineWidth, outlineHeight));
+ int32_t topLeft, bottomRight;
+ findOutlineInsets(&diagonal, &topLeft, &bottomRight);
- /* 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
+ */
+ ninePatch->outlineRadius = 3.4142f * topLeft;
+ return ninePatch;
}
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;
+ 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;
- 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*)horizontalStretchRegions.data(),
+ (const int32_t*)verticalStretchRegions.data(), regionColors.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* 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();
- memcpy(cursor, &layoutBounds.left, sizeof(layoutBounds.left));
- cursor += sizeof(layoutBounds.left);
+ memcpy(cursor, &layoutBounds.left, sizeof(layoutBounds.left));
+ cursor += sizeof(layoutBounds.left);
- memcpy(cursor, &layoutBounds.top, sizeof(layoutBounds.top));
- cursor += sizeof(layoutBounds.top);
+ memcpy(cursor, &layoutBounds.top, sizeof(layoutBounds.top));
+ cursor += sizeof(layoutBounds.top);
- memcpy(cursor, &layoutBounds.right, sizeof(layoutBounds.right));
- cursor += sizeof(layoutBounds.right);
+ memcpy(cursor, &layoutBounds.right, sizeof(layoutBounds.right));
+ cursor += sizeof(layoutBounds.right);
- memcpy(cursor, &layoutBounds.bottom, sizeof(layoutBounds.bottom));
- cursor += sizeof(layoutBounds.bottom);
+ memcpy(cursor, &layoutBounds.bottom, sizeof(layoutBounds.bottom));
+ cursor += sizeof(layoutBounds.bottom);
- *outLen = chunkLen;
- return buffer;
+ *outLen = chunkLen;
+ 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* 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();
- 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) = outlineRadius;
+ cursor += sizeof(outlineRadius);
- *((uint32_t*) cursor) = outlineAlpha;
+ *((uint32_t*)cursor) = outlineAlpha;
- *outLen = chunkLen;
- return buffer;
+ *outLen = chunkLen;
+ 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;
+ 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;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/NinePatch_test.cpp b/tools/aapt2/compile/NinePatch_test.cpp
index 3106ff8..b8eda09 100644
--- a/tools/aapt2/compile/NinePatch_test.cpp
+++ b/tools/aapt2/compile/NinePatch_test.cpp
@@ -21,8 +21,8 @@
// 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 +32,346 @@
#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> ninePatch =
+ NinePatch::create(kSingleStretch7x6, 7, 6, &err);
+ ASSERT_NE(nullptr, ninePatch);
- ASSERT_EQ(1u, ninePatch->horizontalStretchRegions.size());
- ASSERT_EQ(1u, ninePatch->verticalStretchRegions.size());
+ ASSERT_EQ(1u, ninePatch->horizontalStretchRegions.size());
+ ASSERT_EQ(1u, ninePatch->verticalStretchRegions.size());
- EXPECT_EQ(Range(1, 4), ninePatch->horizontalStretchRegions.front());
- EXPECT_EQ(Range(1, 3), ninePatch->verticalStretchRegions.front());
+ EXPECT_EQ(Range(1, 4), ninePatch->horizontalStretchRegions.front());
+ EXPECT_EQ(Range(1, 3), ninePatch->verticalStretchRegions.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> ninePatch =
+ NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
+ ASSERT_NE(nullptr, ninePatch);
- ASSERT_EQ(3u, ninePatch->horizontalStretchRegions.size());
- ASSERT_EQ(2u, ninePatch->verticalStretchRegions.size());
+ ASSERT_EQ(3u, ninePatch->horizontalStretchRegions.size());
+ ASSERT_EQ(2u, ninePatch->verticalStretchRegions.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), ninePatch->horizontalStretchRegions[0]);
+ EXPECT_EQ(Range(3, 5), ninePatch->horizontalStretchRegions[1]);
+ EXPECT_EQ(Range(6, 7), ninePatch->horizontalStretchRegions[2]);
- EXPECT_EQ(Range(0, 2), ninePatch->verticalStretchRegions[0]);
- EXPECT_EQ(Range(3, 5), ninePatch->verticalStretchRegions[1]);
+ EXPECT_EQ(Range(0, 2), ninePatch->verticalStretchRegions[0]);
+ EXPECT_EQ(Range(3, 5), ninePatch->verticalStretchRegions[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> ninePatch =
+ NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 0, 1, 0), ninePatch->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> ninePatch =
+ NinePatch::create(kPadding6x5, 6, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->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> ninePatch =
+ NinePatch::create(kLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
- ninePatch = NinePatch::create(kAsymmetricLayoutBounds5x5, 5, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(1, 1, 0, 0), ninePatch->layoutBounds);
+ ninePatch = NinePatch::create(kAsymmetricLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 0, 0), ninePatch->layoutBounds);
}
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> 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);
}
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> ninePatch =
+ NinePatch::create(kColorfulImage5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
- 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> 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);
}
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> 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);
}
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> 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);
}
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> ninePatch =
+ NinePatch::create(kOutlineOffsetTranslucent12x10, 12, 10, &err);
+ ASSERT_NE(nullptr, ninePatch);
- // 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 behaviour I'm breaking, I will leave it at the
+ // incorrect:
+ EXPECT_EQ(Bounds(4, 3, 3, 3), ninePatch->outline);
- EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
- EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+ EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
+ EXPECT_EQ(0.0f, ninePatch->outlineRadius);
}
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> 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);
}
::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";
+ 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> ninePatch =
+ NinePatch::create(kStretchAndPadding5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
- 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 = ninePatch->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..9b5fa7e09 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);
- }
- 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_color rgbPalette[256];
+ png_byte alphaPalette[256];
+ bool hasTransparency;
+ int paletteEntries;
- png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
+ analyze_image(diag, *info, grayScaleTolerance, rgbPalette, alphaPalette,
+ &paletteEntries, &hasTransparency, &colorType, outRows);
- if (kDebug) {
+ // 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()
- << "writing image: w = " << info->width
- << ", h = " << info->height);
+ << "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_color rgbPalette[256];
- png_byte alphaPalette[256];
- bool hasTransparency;
- int paletteEntries;
+ png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
- 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 (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) {
- 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;
- }
+ 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_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);
+ for (int i = 0; i < chunkCount; i++) {
+ unknowns[i].location = PNG_HAVE_PLTE;
}
-
- 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::stringEndsWith(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.grayScaleTolerance)) {
+ 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..f9038af 100644
--- a/tools/aapt2/compile/Png.h
+++ b/tools/aapt2/compile/Png.h
@@ -31,51 +31,48 @@
namespace aapt {
struct PngOptions {
- int grayScaleTolerance = 0;
+ int grayScaleTolerance = 0;
};
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>(mWindowStart);
+ }
- bool HadError() const override {
- return mError;
- }
+ bool HadError() const override { return mError; }
-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 mData;
+ size_t mWindowStart = 0;
+ size_t mWindowEnd = 0;
+ bool mError = false;
- DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
+ DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
};
/**
@@ -84,11 +81,13 @@
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* ninePatch, 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..fb7fe92 100644
--- a/tools/aapt2/compile/PngChunkFilter.cpp
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -25,149 +25,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;
+ 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) {
+ 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;
- }
+ if (util::stringStartsWith(mData, kPngSignature)) {
+ mWindowStart = 0;
+ mWindowEnd = strlen(kPngSignature);
+ } else {
+ mError = 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;
+ 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::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 (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;
+ }
+ return false;
}
void PngChunkFilter::BackUp(int count) {
- if (mError) {
- return;
- }
- mWindowStart -= count;
+ if (mError) {
+ return;
+ }
+ mWindowStart -= count;
}
bool PngChunkFilter::Skip(int count) {
- if (mError) {
- return false;
- }
+ if (mError) {
+ 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..4a74f7af7 100644
--- a/tools/aapt2/compile/PngCrunch.cpp
+++ b/tools/aapt2/compile/PngCrunch.cpp
@@ -16,13 +16,13 @@
#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>
namespace aapt {
@@ -33,250 +33,261 @@
* 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:
+ explicit PngReadStructDeleter(png_structp readPtr, png_infop infoPtr)
+ : mReadPtr(readPtr), mInfoPtr(infoPtr) {}
- ~PngReadStructDeleter() {
- png_destroy_read_struct(&mReadPtr, &mInfoPtr, nullptr);
- }
+ ~PngReadStructDeleter() {
+ png_destroy_read_struct(&mReadPtr, &mInfoPtr, nullptr);
+ }
-private:
- png_structp mReadPtr;
- png_infop mInfoPtr;
+ private:
+ png_structp mReadPtr;
+ png_infop mInfoPtr;
- 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:
+ explicit PngWriteStructDeleter(png_structp writePtr, png_infop infoPtr)
+ : mWritePtr(writePtr), mInfoPtr(infoPtr) {}
- ~PngWriteStructDeleter() {
- png_destroy_write_struct(&mWritePtr, &mInfoPtr);
- }
+ ~PngWriteStructDeleter() { png_destroy_write_struct(&mWritePtr, &mInfoPtr); }
-private:
- png_structp mWritePtr;
- png_infop mInfoPtr;
+ private:
+ png_structp mWritePtr;
+ png_infop mInfoPtr;
- 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);
+ IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(pngPtr);
+ diag->warn(DiagMessage() << warningMsg);
}
// 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);
+ IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(pngPtr);
+ diag->error(DiagMessage() << errorMsg);
}
-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 pngPtr, png_bytep buffer,
+ png_size_t len) {
+ io::InputStream* in = (io::InputStream*)png_get_io_ptr(pngPtr);
- 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* inBuffer;
+ int inLen;
+ if (!in->Next(&inBuffer, &inLen)) {
+ if (in->HadError()) {
+ std::string err = in->GetError();
+ png_error(pngPtr, 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 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));
+ }
}
-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 pngPtr, png_bytep buffer,
+ png_size_t len) {
+ io::OutputStream* out = (io::OutputStream*)png_get_io_ptr(pngPtr);
- 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* 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;
}
- // If the entire output buffer wasn't used, backup.
- if (outLen > 0) {
- out->BackUp(outLen);
- }
+ 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);
+ }
+
+ // If the entire output buffer wasn't used, backup.
+ if (outLen > 0) {
+ out->BackUp(outLen);
+ }
}
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 {};
- }
+ // 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 {};
+ }
- 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>(bufferSize) < 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(bufferSize - 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 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 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 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 {};
+ }
- // Automatically release PNG resources at end of scope.
- PngReadStructDeleter pngReadDeleter(readPtr, infoPtr);
+ // Automatically release PNG resources at end of scope.
+ PngReadStructDeleter pngReadDeleter(readPtr, infoPtr);
- // 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(readPtr))) {
+ 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(readPtr, (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(readPtr, (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(readPtr, kPngSignatureSize);
- // Read the chunk headers.
- png_read_info(readPtr, infoPtr);
+ // Read the chunk headers.
+ png_read_info(readPtr, infoPtr);
- // 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 bitDepth, colorType, interlaceMethod, compressionMethod, filterMethod;
+ png_get_IHDR(readPtr, infoPtr, &width, &height, &bitDepth, &colorType,
+ &interlaceMethod, &compressionMethod, &filterMethod);
- // 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 (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);
+ }
- if (interlaceMethod != PNG_INTERLACE_NONE) {
- png_set_interlace_handling(readPtr);
- }
+ if (interlaceMethod != PNG_INTERLACE_NONE) {
+ png_set_interlace_handling(readPtr);
+ }
- // 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(readPtr, infoPtr);
- // 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> outputImage = util::make_unique<Image>();
+ outputImage->width = static_cast<int32_t>(width);
+ outputImage->height = static_cast<int32_t>(height);
- const size_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
- assert(rowBytes == 4 * width); // RGBA
+ const size_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
+ assert(rowBytes == 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.
+ outputImage->data =
+ std::unique_ptr<uint8_t[]>(new uint8_t[height * rowBytes]);
- // 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.
+ 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);
+ }
- // Actually read the image pixels.
- png_read_image(readPtr, outputImage->rows.get());
+ // Actually read the image pixels.
+ png_read_image(readPtr, outputImage->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(readPtr, infoPtr);
- return outputImage;
+ return outputImage;
}
/**
- * 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 +309,450 @@
// - Grayscale + cheap alpha
// - Grayscale + alpha
//
-static int pickColorType(int32_t width, int32_t height,
- bool grayScale, bool convertibleToGrayScale, bool hasNinePatch,
+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;
+ 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;
- 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 (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 (convertibleToGrayScale) {
- if (alphaPaletteSize == 0) {
- return PNG_COLOR_TYPE_GRAY;
- } else {
- return PNG_COLOR_TYPE_GRAY_ALPHA;
- }
- }
-
+ if (grayScale) {
if (alphaPaletteSize == 0) {
- return PNG_COLOR_TYPE_RGB;
+ // 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_RGBA;
+ return PNG_COLOR_TYPE_GRAY_ALPHA;
+ }
+
+ 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 (convertibleToGrayScale) {
+ if (alphaPaletteSize == 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;
}
-// 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.
+// 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);
+ std::unordered_map<uint32_t, int>* colorPalette,
+ std::unordered_set<uint32_t>* alphaPalette) {
+ assert(colorPalette->size() <= 256);
+ assert(alphaPalette->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 : *alphaPalette) {
+ (*colorPalette)[color] = index++;
+ }
+
+ // Assign the rest of the entries.
+ for (auto& entry : *colorPalette) {
+ 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 colorPaletteBytes =
+ std::unique_ptr<png_color[]>(new png_color[colorPalette->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;
}
+ }
- // 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 colorPaletteBytes at
+ // the end of function
+ // scope.
+ png_set_PLTE(writePtr, writeInfoPtr, colorPaletteBytes.get(),
+ colorPalette->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 (alphaPaletteBytes) {
+ png_set_tRNS(writePtr, writeInfoPtr, alphaPaletteBytes.get(),
+ alphaPalette->size(), nullptr);
+ }
}
// Write the 9-patch custom PNG chunks to writeInfoPtr. 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.
+ // 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 unknownChunks[3];
+ memset(unknownChunks, 0, sizeof(unknownChunks));
- size_t index = 0;
- size_t chunkLen = 0;
+ size_t index = 0;
+ size_t chunkLen = 0;
- std::unique_ptr<uint8_t[]> serializedOutline =
- ninePatch->serializeRoundedRectOutline(&chunkLen);
- strcpy((char*) unknownChunks[index].name, "npOl");
+ 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;
+ 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) serializedOutline.get();
+ unknownChunks[index].data = (png_bytep)serializedLayoutBounds.get();
unknownChunks[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[]> 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++;
- 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(writePtr, 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(writePtr, writeInfoPtr, unknownChunks, 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* 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;
+ }
+
+ // 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;
+ }
+
+ // 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;
+ }
+
+ // Handle warnings with our IDiagnostics.
+ png_set_error_fn(writePtr, (png_voidp)context->getDiagnostics(), logError,
+ logWarning);
+
+ // Set up the write functions which write to our custom data sources.
+ png_set_write_fn(writePtr, (png_voidp)out, writeDataToStream, nullptr);
+
+ // We want small files and can take the performance hit to achieve this goal.
+ png_set_compression_level(writePtr, 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> colorPalette;
+ std::unordered_set<uint32_t> alphaPalette;
+ bool needsToZeroRGBChannelsOfTransparentPixels = false;
+ bool grayScale = true;
+ int maxGrayDeviation = 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.
+ 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);
}
+ }
- // 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->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 ";
}
-
- // 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 (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);
+ }
- // Handle warnings with our IDiagnostics.
- png_set_error_fn(writePtr, (png_voidp) context->getDiagnostics(), logError, logWarning);
+ png_set_IHDR(writePtr, writeInfoPtr, image->width, image->height, 8,
+ newColorType, 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 (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);
+ }
- // We want small files and can take the performance hit to achieve this goal.
- png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
+ if (ninePatch) {
+ writeNinePatch(writePtr, writeInfoPtr, ninePatch);
+ }
- // 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(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++) {
- 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 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]);
- 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 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;
}
- 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.
+ outRow[x * bpp] = inRow[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
+ outRow[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.
+ 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());
+ }
+ } 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());
+ }
+ } else {
+ assert(false && "unreachable");
+ }
+
+ png_write_end(writePtr, writeInfoPtr);
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 732101f..d8ed0bb 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -14,235 +14,242 @@
* limitations under the License.
*/
+#include "compile/PseudolocaleGenerator.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "compile/PseudolocaleGenerator.h"
#include "compile/Pseudolocalizer.h"
#include <algorithm>
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 originalText = *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.firstChar, span.lastChar});
+ }
+
+ // 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* updateStart;
+ uint32_t* updateEnd;
+ };
+
+ 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});
}
- // 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.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});
+ }
+ }
- // 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 = originalText.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].updateStart) {
+ *ranges[i].updateStart = localized.str.size();
}
- localized.str += localizer.end();
+ if (ranges[i].updateEnd) {
+ *ranges[i].updateEnd = 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(originalText.substr(start, len));
+ }
+
+ localized.str += localizer.end();
+
+ std::unique_ptr<StyledString> localizedString =
+ util::make_unique<StyledString>(pool->makeRef(localized));
+ localizedString->setSource(string->getSource());
+ return localizedString;
}
namespace {
struct Visitor : public RawValueVisitor {
- StringPool* mPool;
- Pseudolocalizer::Method mMethod;
- Pseudolocalizer mLocalizer;
+ StringPool* mPool;
+ Pseudolocalizer::Method mMethod;
+ Pseudolocalizer mLocalizer;
- // Either value or item will be populated upon visiting the value.
- std::unique_ptr<Value> mValue;
- std::unique_ptr<Item> mItem;
+ // 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) :
- mPool(pool), mMethod(method), mLocalizer(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 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));
}
- localized->setSource(plural->getSource());
- localized->setWeak(true);
- mValue = std::move(localized);
+ }
}
+ localized->setSource(plural->getSource());
+ localized->setWeak(true);
+ mValue = 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 =
+ 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(StyledString* string) override {
- mItem = pseudolocalizeStyledString(string, mMethod, mPool);
- mItem->setWeak(true);
- }
+ void visit(StyledString* string) override {
+ mItem = pseudolocalizeStyledString(string, mMethod, mPool);
+ mItem->setWeak(true);
+ }
};
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);
+ StringPool* pool, ResourceEntry* entry) {
+ Visitor visitor(pool, method);
+ originalValue->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> localizedValue;
+ if (visitor.mValue) {
+ localizedValue = std::move(visitor.mValue);
+ } else if (visitor.mItem) {
+ localizedValue = std::move(visitor.mItem);
+ }
- if (!localizedValue) {
- return;
- }
+ if (!localizedValue) {
+ return;
+ }
- ConfigDescription configWithAccent = modifyConfigForPseudoLocale(
- originalValue->config, method);
+ ConfigDescription configWithAccent =
+ modifyConfigForPseudoLocale(originalValue->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* newConfigValue =
+ entry->findOrCreateValue(configWithAccent, originalValue->product);
+ if (!newConfigValue->value) {
+ // Only use auto-generated pseudo-localization if none is defined.
+ newConfigValue->value = std::move(localizedValue);
+ }
}
/**
- * 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();
+ const int diff = configValue->config.diff(ConfigDescription::defaultConfig());
+ if (diff & ConfigDescription::CONFIG_LOCALE) {
+ return false;
+ }
+ return configValue->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->stringPool, entry.get());
+ pseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
+ &table->stringPool, 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..4e97cb9 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..64a3e97 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
@@ -23,99 +23,110 @@
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 originalStyle;
+ originalStyle.str = "Hello world!";
+ originalStyle.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> newString = pseudolocalizeStyledString(
+ util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(),
+ Pseudolocalizer::Method::kNone, &pool);
- EXPECT_EQ(originalStyle.str, *newString->value->str);
- ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size());
+ EXPECT_EQ(originalStyle.str, *newString->value->str);
+ ASSERT_EQ(originalStyle.spans.size(), newString->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(), 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("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(), 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("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(), 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);
- originalStyle.spans.push_back(Span{ "em", 0, 11u });
+ originalStyle.spans.push_back(Span{"em", 0, 11u});
- newString = pseudolocalizeStyledString(
- util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(),
- Pseudolocalizer::Method::kAccent, &pool);
+ newString = pseudolocalizeStyledString(
+ util::make_unique<StyledString>(pool.makeRef(originalStyle)).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]"), *newString->value->str);
+ ASSERT_EQ(originalStyle.spans.size(), newString->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(), newString->value->spans[0].firstChar);
+ EXPECT_EQ(std::string("[Ĥéļ").size(), newString->value->spans[0].lastChar);
- 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(),
+ newString->value->spans[1].firstChar);
+ EXPECT_EQ(std::string("[Ĥéļļö ŵ").size(),
+ newString->value->spans[1].lastChar);
- 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(), newString->value->spans[2].firstChar);
+ EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļ").size(),
+ newString->value->spans[2].lastChar);
- 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(), newString->value->spans[3].firstChar);
+ EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð").size(),
+ newString->value->spans[3].lastChar);
}
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..c3aec98 100644
--- a/tools/aapt2/compile/Pseudolocalizer.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer.cpp
@@ -20,9 +20,10 @@
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 k_expansion_string =
+ "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";
@@ -37,229 +38,310 @@
static const char k_arg_end = '}';
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() : 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;
};
Pseudolocalizer::Pseudolocalizer(Method method) : mLastDepth(0) {
- setMethod(method);
+ setMethod(method);
}
void Pseudolocalizer::setMethod(Method method) {
- switch (method) {
+ switch (method) {
case Method::kNone:
- mImpl = util::make_unique<PseudoMethodNone>();
- break;
+ mImpl = util::make_unique<PseudoMethodNone>();
+ break;
case Method::kAccent:
- mImpl = util::make_unique<PseudoMethodAccent>();
- break;
+ mImpl = util::make_unique<PseudoMethodAccent>();
+ break;
case Method::kBidi:
- mImpl = util::make_unique<PseudoMethodBidi>();
- break;
- }
+ mImpl = util::make_unique<PseudoMethodBidi>();
+ break;
+ }
}
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;
- }
+ 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;
}
- return out;
+ 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;
}
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;
- }
+ 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;
- }
+ 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);
+ 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;
+ }
}
- return result;
+ 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 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 result;
+ if (mLength) {
+ result += " ";
+ result += pseudoGenerateExpansion(mWordCount > 3 ? mLength : mLength / 2);
+ }
+ mWordCount = mLength = 0;
+ mDepth--;
+ if (mDepth == 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 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) {
- // Word start
- result += k_rlm + k_rlo;
- } else if (!lastspace && space) {
- // Word end
- result += k_pdf + k_rlm;
+ mWordCount++;
}
lastspace = space;
result.append(&c, 1);
+ }
+ // Count only pseudolocalizable chars and delimiters
+ mLength++;
}
- if (!lastspace) {
- // End of last word
- result += k_pdf + k_rlm;
+ }
+ 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);
+ if (lastspace && !space) {
+ // Word start
+ result += k_rlm + k_rlo;
+ } else if (!lastspace && space) {
+ // Word end
+ result += k_pdf + k_rlm;
}
- return result;
+ lastspace = space;
+ result.append(&c, 1);
+ }
+ if (!lastspace) {
+ // End of last word
+ result += k_pdf + k_rlm;
+ }
+ 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;
+ // Surround a placeholder with directionality change sequence
+ return k_rlm + k_rlo + source.toString() + k_pdf + k_rlm;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/Pseudolocalizer.h b/tools/aapt2/compile/Pseudolocalizer.h
index 91d17d174..a526877 100644
--- a/tools/aapt2/compile/Pseudolocalizer.h
+++ b/tools/aapt2/compile/Pseudolocalizer.h
@@ -27,32 +27,33 @@
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 mImpl->start(); }
+ std::string end() { return mImpl->end(); }
+ std::string text(const StringPiece& text);
+
+ private:
+ std::unique_ptr<PseudoMethodImpl> mImpl;
+ size_t mLastDepth;
};
-} // 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..a152ed6 100644
--- a/tools/aapt2/compile/Pseudolocalizer_test.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer_test.cpp
@@ -25,199 +25,207 @@
// 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..aa8b1df 100644
--- a/tools/aapt2/compile/XmlIdCollector.cpp
+++ b/tools/aapt2/compile/XmlIdCollector.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include "compile/XmlIdCollector.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
-#include "compile/XmlIdCollector.h"
#include "xml/XmlDom.h"
#include <algorithm>
@@ -27,44 +27,44 @@
namespace {
static bool cmpName(const SourcedResourceName& a, const ResourceNameRef& b) {
- return a.name < b;
+ return a.name < b;
}
struct IdCollector : public xml::Visitor {
- using xml::Visitor::visit;
+ using xml::Visitor::visit;
- std::vector<SourcedResourceName>* mOutSymbols;
+ std::vector<SourcedResourceName>* mOutSymbols;
- explicit IdCollector(std::vector<SourcedResourceName>* outSymbols) : mOutSymbols(outSymbols) {
- }
+ 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(mOutSymbols->begin(), mOutSymbols->end(),
+ name, cmpName);
+ if (iter == mOutSymbols->end() || iter->name != name) {
+ mOutSymbols->insert(iter, SourcedResourceName{name.toResourceName(),
+ element->lineNumber});
+ }
}
-
- xml::Visitor::visit(element);
+ }
}
+
+ xml::Visitor::visit(element);
+ }
};
-} // 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;
+ xmlRes->file.exportedSymbols.clear();
+ IdCollector collector(&xmlRes->file.exportedSymbols);
+ 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..8423f48 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* xmlRes) 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..08ca7b1 100644
--- a/tools/aapt2/compile/XmlIdCollector_test.cpp
+++ b/tools/aapt2/compile/XmlIdCollector_test.cpp
@@ -18,15 +18,15 @@
#include "test/Builders.h"
#include "test/Context.h"
-#include <algorithm>
#include <gtest/gtest.h>
+#include <algorithm>
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 +34,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.exportedSymbols.begin(),
+ doc->file.exportedSymbols.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.exportedSymbols.begin(),
+ doc->file.exportedSymbols.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.exportedSymbols.begin(),
+ doc->file.exportedSymbols.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.exportedSymbols.empty());
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/diff/Diff.cpp b/tools/aapt2/diff/Diff.cpp
index 9b1f057..01f4539 100644
--- a/tools/aapt2/diff/Diff.cpp
+++ b/tools/aapt2/diff/Diff.cpp
@@ -27,412 +27,401 @@
namespace aapt {
class DiffContext : public IAaptContext {
-public:
- const std::string& getCompilationPackage() override {
- return mEmpty;
- }
+ public:
+ const std::string& getCompilationPackage() override { return mEmpty; }
- uint8_t getPackageId() override {
- return 0x0;
- }
+ uint8_t getPackageId() override { return 0x0; }
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
- NameMangler* getNameMangler() override {
- return &mNameMangler;
- }
+ NameMangler* getNameMangler() override { return &mNameMangler; }
- SymbolTable* getExternalSymbols() override {
- return &mSymbolTable;
- }
+ SymbolTable* getExternalSymbols() override { return &mSymbolTable; }
- bool verbose() override {
- return false;
- }
+ bool verbose() 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 mEmpty;
+ StdErrDiagnostics mDiagnostics;
+ NameMangler mNameMangler = NameMangler(NameManglerPolicy{});
+ SymbolTable mSymbolTable;
};
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)
+ : mSource(source), mApk(std::move(apk)), mTable(std::move(table)) {}
- io::IFileCollection* getFileCollection() {
- return mApk.get();
- }
+ io::IFileCollection* getFileCollection() { return mApk.get(); }
- ResourceTable* getResourceTable() {
- return mTable.get();
- }
+ ResourceTable* getResourceTable() { return mTable.get(); }
- const Source& getSource() {
- return mSource;
- }
+ const Source& getSource() { return mSource; }
-private:
- Source mSource;
- std::unique_ptr<io::IFileCollection> mApk;
- std::unique_ptr<ResourceTable> mTable;
+ private:
+ Source mSource;
+ std::unique_ptr<io::IFileCollection> mApk;
+ std::unique_ptr<ResourceTable> mTable;
- 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";
+ std::cerr << source << ": " << message << "\n";
}
-static bool isSymbolVisibilityDifferent(const Symbol& symbolA, const Symbol& symbolB) {
- return symbolA.state != symbolB.state;
+static bool isSymbolVisibilityDifferent(const Symbol& symbolA,
+ const Symbol& symbolB) {
+ return symbolA.state != symbolB.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;
+ if (symbolA.state == SymbolState::kPublic ||
+ symbolB.state == SymbolState::kPublic) {
+ return idA != idB;
+ }
+ 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* 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 emitResourceEntryDiff(IAaptContext* context,
- LoadedApk* apkA,
+static bool emitResourceEntryDiff(IAaptContext* context, LoadedApk* apkA,
ResourceTablePackage* pkgA,
ResourceTableType* typeA,
- ResourceEntry* entryA,
- LoadedApk* apkB,
+ 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;
- } else {
- diff |= emitResourceConfigValueDiff(context, apkA, pkgA, typeA, entryA,
- configValueA.get(), apkB, pkgB, typeB, entryB,
- configValueB);
- }
+ 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;
+ } else {
+ diff |= emitResourceConfigValueDiff(context, apkA, pkgA, typeA, entryA,
+ configValueA.get(), apkB, pkgB, typeB,
+ entryB, configValueB);
}
+ }
- // 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 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;
}
- return false;
+ }
+ return false;
}
-static bool emitResourceTypeDiff(IAaptContext* context,
- LoadedApk* apkA,
+static bool emitResourceTypeDiff(IAaptContext* context, LoadedApk* apkA,
ResourceTablePackage* pkgA,
- ResourceTableType* typeA,
- LoadedApk* apkB,
+ 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;
+ 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;
+ } 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 {
- 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);
+ 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);
}
+ }
- // 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 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;
}
- 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;
+ 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;
+ } 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 {
- 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);
+ 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);
}
+ }
- // 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;
- }
+ // 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;
+ }
+ return diff;
}
-static bool emitResourceTableDiff(IAaptContext* context, LoadedApk* apkA, LoadedApk* apkB) {
- ResourceTable* tableA = apkA->getResourceTable();
- ResourceTable* tableB = apkB->getResourceTable();
+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;
+ 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;
+ } 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 {
- 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);
+ 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);
}
+ }
- // 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>& 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;
}
- 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().packageId() == 0x7f) {
+ ref->id = {};
+ }
}
+ }
};
static void zeroOutAppReferences(ResourceTable* table) {
- ZeroingReferenceVisitor visitor;
- visitAllValuesInTable(table, &visitor);
+ ZeroingReferenceVisitor visitor;
+ visitAllValuesInTable(table, &visitor);
}
int diff(const std::vector<StringPiece>& args) {
- DiffContext context;
+ 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> apkA =
+ loadApkFromPath(&context, flags.getArgs()[0]);
+ std::unique_ptr<LoadedApk> apkB =
+ loadApkFromPath(&context, flags.getArgs()[1]);
+ if (!apkA || !apkB) {
+ return 1;
+ }
- // Zero out Application IDs in references.
- zeroOutAppReferences(apkA->getResourceTable());
- zeroOutAppReferences(apkB->getResourceTable());
+ // Zero out Application IDs in references.
+ zeroOutAppReferences(apkA->getResourceTable());
+ zeroOutAppReferences(apkB->getResourceTable());
- if (emitResourceTableDiff(&context, apkA.get(), apkB.get())) {
- // We emitted a diff, so return 1 (failure).
- return 1;
- }
- return 0;
+ if (emitResourceTableDiff(&context, apkA.get(), apkB.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..3556cd88 100644
--- a/tools/aapt2/dump/Dump.cpp
+++ b/tools/aapt2/dump/Dump.cpp
@@ -28,183 +28,181 @@
namespace aapt {
-//struct DumpOptions {
+// struct DumpOptions {
//
//};
-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;
- }
+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;
+ 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(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;
+ }
- pb::ResourceTable pbTable;
- if (!pbTable.ParseFromArray(data->data(), data->size())) {
- context->getDiagnostics()->error(DiagMessage(filePath)
- << "invalid resources.arsc.flat");
- return;
- }
+ pb::ResourceTable pbTable;
+ if (!pbTable.ParseFromArray(data->data(), data->size())) {
+ context->getDiagnostics()->error(DiagMessage(filePath)
+ << "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(pbTable, Source(filePath),
+ 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(filePath)
+ << "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(filePath),
+ 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(filePath, &err);
+ if (!file) {
+ context->getDiagnostics()->error(DiagMessage(filePath) << err);
+ return;
}
- if (table) {
- DebugPrintTableOptions debugPrintTableOptions;
- debugPrintTableOptions.showSources = true;
- Debug::printTable(table.get(), debugPrintTableOptions);
+ 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());
}
+
+ 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) {
+ DebugPrintTableOptions debugPrintTableOptions;
+ debugPrintTableOptions.showSources = true;
+ Debug::printTable(table.get(), debugPrintTableOptions);
+ }
}
class DumpContext : public IAaptContext {
-public:
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ public:
+ IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
- 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 verbose() override { return mVerbose; }
- void setVerbose(bool val) {
- mVerbose = val;
- }
+ void setVerbose(bool val) { mVerbose = val; }
- int getMinSdkVersion() override {
- return 0;
- }
+ int getMinSdkVersion() override { return 0; }
-private:
- StdErrDiagnostics mDiagnostics;
- bool mVerbose = false;
+ private:
+ StdErrDiagnostics mDiagnostics;
+ bool mVerbose = 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;
- }
+ 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..5af996c 100644
--- a/tools/aapt2/filter/ConfigFilter.cpp
+++ b/tools/aapt2/filter/ConfigFilter.cpp
@@ -14,64 +14,68 @@
* limitations under the License.
*/
-#include "ConfigDescription.h"
#include "filter/ConfigFilter.h"
+#include "ConfigDescription.h"
#include <androidfw/ResourceTypes.h>
namespace aapt {
void AxisConfigFilter::addConfig(ConfigDescription config) {
- uint32_t diffMask = ConfigDescription::defaultConfig().diff(config);
+ uint32_t diffMask = ConfigDescription::defaultConfig().diff(config);
- // Ignore the version
- diffMask &= ~android::ResTable_config::CONFIG_VERSION;
+ // Ignore the version
+ diffMask &= ~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 ((diffMask & android::ResTable_config::CONFIG_DENSITY) != 0) {
+ config.density = 0;
+ diffMask &= ~android::ResTable_config::CONFIG_DENSITY;
+ }
- mConfigs.insert(std::make_pair(config, diffMask));
- mConfigMask |= diffMask;
+ mConfigs.insert(std::make_pair(config, diffMask));
+ mConfigMask |= diffMask;
}
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;
- }
+ const uint32_t mask = ConfigDescription::defaultConfig().diff(config);
+ if ((mConfigMask & 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 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;
+ }
}
- return matchedAxis == (mConfigMask & mask);
+ }
+ return matchedAxis == (mConfigMask & mask);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/filter/ConfigFilter.h b/tools/aapt2/filter/ConfigFilter.h
index 36e9c44..c50160b 100644
--- a/tools/aapt2/filter/ConfigFilter.h
+++ b/tools/aapt2/filter/ConfigFilter.h
@@ -28,34 +28,37 @@
* 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>> mConfigs;
+ uint32_t mConfigMask = 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..edb40a8 100644
--- a/tools/aapt2/filter/ConfigFilter_test.cpp
+++ b/tools/aapt2/filter/ConfigFilter_test.cpp
@@ -22,91 +22,92 @@
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..ae08f65 100644
--- a/tools/aapt2/flatten/Archive.cpp
+++ b/tools/aapt2/flatten/Archive.cpp
@@ -18,167 +18,169 @@
#include "util/Files.h"
#include "util/StringPiece.h"
+#include <ziparchive/zip_writer.h>
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
-#include <ziparchive/zip_writer.h>
namespace aapt {
namespace {
struct DirectoryWriter : public IArchiveWriter {
- std::string mOutDir;
- std::unique_ptr<FILE, decltype(fclose)*> mFile = { nullptr, fclose };
+ std::string mOutDir;
+ std::unique_ptr<FILE, decltype(fclose)*> mFile = {nullptr, fclose};
- 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& 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 startEntry(const StringPiece& path, uint32_t flags) override {
+ if (mFile) {
+ return false;
}
- bool startEntry(const StringPiece& path, uint32_t flags) override {
- if (mFile) {
- return false;
- }
+ std::string fullPath = mOutDir;
+ file::appendPath(&fullPath, path);
+ file::mkdirs(file::getStem(fullPath));
- std::string fullPath = mOutDir;
- file::appendPath(&fullPath, path);
- file::mkdirs(file::getStem(fullPath));
+ mFile = {fopen(fullPath.data(), "wb"), fclose};
+ if (!mFile) {
+ return false;
+ }
+ return true;
+ }
- mFile = { fopen(fullPath.data(), "wb"), fclose };
- if (!mFile) {
- return false;
- }
- return true;
+ bool writeEntry(const BigBuffer& buffer) override {
+ if (!mFile) {
+ 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;
- }
-
- 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 finishEntry() 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 true;
+ 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 finishEntry() override {
+ if (!mFile) {
+ return false;
+ }
+ mFile.reset(nullptr);
+ return true;
+ }
};
struct ZipFileWriter : public IArchiveWriter {
- std::unique_ptr<FILE, decltype(fclose)*> mFile = { nullptr, fclose };
- std::unique_ptr<ZipWriter> mWriter;
+ std::unique_ptr<FILE, decltype(fclose)*> mFile = {nullptr, fclose};
+ std::unique_ptr<ZipWriter> mWriter;
- 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) {
+ 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 startEntry(const StringPiece& path, uint32_t flags) override {
+ if (!mWriter) {
+ 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 zipFlags = 0;
+ if (flags & ArchiveEntry::kCompress) {
+ zipFlags |= 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) {
+ zipFlags |= 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 = mWriter->StartEntry(path.data(), zipFlags);
+ 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 = mWriter->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 = mWriter->WriteBytes(b.buffer.get(), b.size);
+ if (result != 0) {
+ return false;
+ }
}
+ return true;
+ }
+
+ bool finishEntry() override {
+ int32_t result = mWriter->FinishEntry();
+ if (result != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ virtual ~ZipFileWriter() {
+ if (mWriter) {
+ mWriter->Finish();
+ }
+ }
};
-} // 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..46cd0ac 100644
--- a/tools/aapt2/flatten/Archive.h
+++ b/tools/aapt2/flatten/Archive.h
@@ -31,37 +31,37 @@
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 uncompressedSize;
};
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..852afd4 100644
--- a/tools/aapt2/flatten/ChunkWriter.h
+++ b/tools/aapt2/flatten/ChunkWriter.h
@@ -25,63 +25,56 @@
namespace aapt {
class ChunkWriter {
-private:
- BigBuffer* mBuffer;
- size_t mStartSize = 0;
- android::ResChunk_header* mHeader = nullptr;
+ private:
+ BigBuffer* mBuffer;
+ size_t mStartSize = 0;
+ android::ResChunk_header* mHeader = nullptr;
-public:
- explicit inline ChunkWriter(BigBuffer* buffer) : mBuffer(buffer) {
- }
+ public:
+ explicit inline ChunkWriter(BigBuffer* buffer) : mBuffer(buffer) {}
- ChunkWriter(const ChunkWriter&) = delete;
- ChunkWriter& operator=(const ChunkWriter&) = delete;
- ChunkWriter(ChunkWriter&&) = default;
- ChunkWriter& operator=(ChunkWriter&&) = default;
+ ChunkWriter(const ChunkWriter&) = delete;
+ ChunkWriter& operator=(const ChunkWriter&) = delete;
+ ChunkWriter(ChunkWriter&&) = default;
+ ChunkWriter& operator=(ChunkWriter&&) = default;
- 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;
- }
+ 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;
+ }
- template <typename T>
- inline T* nextBlock(size_t count = 1) {
- return mBuffer->nextBlock<T>(count);
- }
+ template <typename T>
+ inline T* nextBlock(size_t count = 1) {
+ return mBuffer->nextBlock<T>(count);
+ }
- inline BigBuffer* getBuffer() {
- return mBuffer;
- }
+ inline BigBuffer* getBuffer() { return mBuffer; }
- inline android::ResChunk_header* getChunkHeader() {
- return mHeader;
- }
+ inline android::ResChunk_header* getChunkHeader() { return mHeader; }
- inline size_t size() {
- return mBuffer->size() - mStartSize;
- }
+ inline size_t size() { return mBuffer->size() - mStartSize; }
- inline android::ResChunk_header* finish() {
- mBuffer->align4();
- mHeader->size = util::hostToDevice32(mBuffer->size() - mStartSize);
- return mHeader;
- }
+ inline android::ResChunk_header* finish() {
+ mBuffer->align4();
+ mHeader->size = util::hostToDevice32(mBuffer->size() - mStartSize);
+ return mHeader;
+ }
};
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;
+ mStartSize = mBuffer->size();
+ mHeader = mBuffer->nextBlock<android::ResChunk_header>();
+ mHeader->type = util::hostToDevice16(type);
+ mHeader->headerSize = util::hostToDevice16(sizeof(android::ResChunk_header));
+ return mHeader;
}
-} // 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..0b19240 100644
--- a/tools/aapt2/flatten/ResourceTypeExtensions.h
+++ b/tools/aapt2/flatten/ResourceTypeExtensions.h
@@ -22,15 +22,16 @@
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..d4ea6c0 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -25,9 +25,9 @@
#include <android-base/macros.h>
#include <algorithm>
+#include <numeric>
#include <sstream>
#include <type_traits>
-#include <numeric>
using namespace android;
@@ -37,438 +37,454 @@
template <typename T>
static bool cmpIds(const T* a, const T* b) {
- return a->id.value() < b->id.value();
+ 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* srcData = src.data();
+ for (i = 0; i < len - 1 && i < src.size(); i++) {
+ dst[i] = util::hostToDevice16((uint16_t)srcData[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;
+ 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 entryKey;
};
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* outEntry, BigBuffer* buffer)
+ : mOutEntry(outEntry), mBuffer(buffer) {}
+
+ 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);
}
- 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->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);
}
- 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->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);
}
- 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& parentRef = style->parent.value();
+ assert(parentRef.id && "parent has no ID");
+ mOutEntry->parent.ident = util::hostToDevice32(parentRef.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(), cmpStyleEntries);
+
+ 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& attrRef : styleable->entries) {
+ BinaryPrimitive val(Res_value{});
+ flattenEntry(&attrRef, &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* outEntry = mBuffer->nextBlock<ResTable_map>();
+ flattenValue(item.get(), outEntry);
+ outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
+ mEntryCount++;
}
+ }
-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:
+ assert(false);
+ 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() { mOutEntry->count = util::hostToDevice32(mEntryCount); }
- 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:
+ void flattenKey(Reference* key, ResTable_map* outEntry) {
+ assert(key->id && "key has no ID");
+ outEntry->name.ident = util::hostToDevice32(key->id.value().id);
+ }
- ResTable_entry_ext* mOutEntry;
- BigBuffer* mBuffer;
- size_t mEntryCount = 0;
+ void flattenValue(Item* value, ResTable_map* outEntry) {
+ bool result = value->flatten(&outEntry->value);
+ assert(result && "flatten failed");
+ }
+
+ 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++;
+ }
+
+ ResTable_entry_ext* mOutEntry;
+ BigBuffer* mBuffer;
+ size_t mEntryCount = 0;
};
class PackageFlattener {
-public:
- PackageFlattener(IDiagnostics* diag, ResourceTablePackage* package) :
- mDiag(diag), mPackage(package) {
+ public:
+ PackageFlattener(IDiagnostics* diag, ResourceTablePackage* package)
+ : mDiag(diag), mPackage(package) {}
+
+ 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());
+
+ if (mPackage->name.size() >= arraysize(pkgHeader->name)) {
+ mDiag->error(DiagMessage() << "package name '" << mPackage->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(pkgHeader->name, arraysize(pkgHeader->name),
+ util::utf8ToUtf16(mPackage->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 typeBuffer(1024);
+ flattenTypes(&typeBuffer);
- // Copy the package name in device endianness.
- strcpy16_htod(pkgHeader->name, arraysize(pkgHeader->name),
- util::utf8ToUtf16(mPackage->name));
+ pkgHeader->typeStrings = util::hostToDevice32(pkgWriter.size());
+ StringPool::flattenUtf16(pkgWriter.getBuffer(), mTypePool);
- // 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);
+ pkgHeader->keyStrings = util::hostToDevice32(pkgWriter.size());
+ StringPool::flattenUtf8(pkgWriter.getBuffer(), mKeyPool);
- pkgHeader->typeStrings = util::hostToDevice32(pkgWriter.size());
- StringPool::flattenUtf16(pkgWriter.getBuffer(), mTypePool);
+ // Append the types.
+ buffer->appendBuffer(std::move(typeBuffer));
- pkgHeader->keyStrings = util::hostToDevice32(pkgWriter.size());
- StringPool::flattenUtf8(pkgWriter.getBuffer(), mKeyPool);
+ pkgWriter.finish();
+ return true;
+ }
- // Append the types.
- buffer->appendBuffer(std::move(typeBuffer));
+ private:
+ IDiagnostics* mDiag;
+ ResourceTablePackage* mPackage;
+ StringPool mTypePool;
+ StringPool mKeyPool;
- 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* outEntry = (ResTable_entry*)(result);
+ if (entry->entry->symbolStatus.state == SymbolState::kPublic) {
+ outEntry->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()) {
+ outEntry->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) {
+ outEntry->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;
- }
+ outEntry->flags = util::hostToDevice16(outEntry->flags);
+ outEntry->key.index = util::hostToDevice32(entry->entryKey);
+ outEntry->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>();
+ 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;
+ }
- 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 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;
+ }
+
+ 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;
+ }
+
+ assert(type->id && "type must have an ID set");
+
+ sortedTypes.push_back(type.get());
+ }
+ std::sort(sortedTypes.begin(), sortedTypes.end(),
+ cmpIds<ResourceTableType>);
+ return sortedTypes;
+ }
+
+ 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());
+ }
+ std::sort(sortedEntries.begin(), sortedEntries.end(),
+ cmpIds<ResourceEntry>);
+ return sortedEntries;
+ }
+
+ 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();
+
+ if (sortedEntries->empty()) {
+ typeSpecWriter.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 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));
}
- std::sort(sortedEntries.begin(), sortedEntries.end(), cmpIds<ResourceEntry>);
- return sortedEntries;
+ }
}
+ typeSpecWriter.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*> sortedTypes = collectAndSortTypes();
- if (sortedEntries->empty()) {
- typeSpecWriter.finish();
- return true;
+ 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});
}
+ }
- // 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 : configToEntryListMap) {
+ if (!flattenConfig(type, entry.first, &entry.second, buffer)) {
+ return false;
}
- typeSpecWriter.finish();
- 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;
- }
+ return true;
+ }
};
-} // 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 {
+ // 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 {
int diff = a.context.priority - b.context.priority;
if (diff < 0) return true;
if (diff > 0) return false;
@@ -476,31 +492,32 @@
if (diff < 0) return true;
if (diff > 0) return false;
return a.value < b.value;
- });
- table->stringPool.prune();
+ });
+ table->stringPool.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 tableWriter(mBuffer);
+ ResTable_header* tableHeader =
+ tableWriter.startChunk<ResTable_header>(RES_TABLE_TYPE);
+ tableHeader->packageCount = util::hostToDevice32(table->packages.size());
- // Flatten the values string pool.
- StringPool::flattenUtf8(tableWriter.getBuffer(), table->stringPool);
+ // Flatten the values string pool.
+ StringPool::flattenUtf8(tableWriter.getBuffer(), table->stringPool);
- BigBuffer packageBuffer(1024);
+ BigBuffer packageBuffer(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(&packageBuffer)) {
+ 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.
+ tableWriter.getBuffer()->appendBuffer(std::move(packageBuffer));
+ tableWriter.finish();
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/flatten/TableFlattener.h
index b416f20..91f9654 100644
--- a/tools/aapt2/flatten/TableFlattener.h
+++ b/tools/aapt2/flatten/TableFlattener.h
@@ -25,16 +25,15 @@
class ResourceTable;
class TableFlattener : public IResourceTableConsumer {
-public:
- explicit TableFlattener(BigBuffer* buffer) : mBuffer(buffer) {
- }
+ public:
+ explicit TableFlattener(BigBuffer* buffer) : mBuffer(buffer) {}
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ bool consume(IAaptContext* context, ResourceTable* table) override;
-private:
- BigBuffer* mBuffer;
+ private:
+ BigBuffer* mBuffer;
};
-} // 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..a7706bd 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/flatten/TableFlattener_test.cpp
@@ -14,8 +14,8 @@
* 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 +25,207 @@
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 {
+ mContext = test::ContextBuilder()
+ .setCompilationPackage("com.app.test")
+ .setPackageId(0x7f)
+ .build();
+ }
+
+ ::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";
}
- ::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 (outTable->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* outTable) {
+ BigBuffer buffer(1024);
+ TableFlattener flattener(&buffer);
+ if (!flattener.consume(mContext.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(mContext.get(), outTable, {}, 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& 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";
}
- ::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 (expectedDataType != val.dataType) {
+ return ::testing::AssertionFailure()
+ << "expected data type " << std::hex << (int)expectedDataType
+ << " but got data type " << (int)val.dataType << std::dec
+ << " instead";
}
-private:
- std::unique_ptr<IAaptContext> mContext;
+ 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();
+ }
+
+ private:
+ std::unique_ptr<IAaptContext> mContext;
};
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 resTable;
+ ASSERT_TRUE(flatten(table.get(), &resTable));
- EXPECT_TRUE(exists(&resTable, "com.app.test:id/one", ResourceId(0x7f020000), {},
- Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(exists(&resTable, "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(&resTable, "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(&resTable, "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(&resTable, "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(&resTable, "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 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 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 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));
}
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 resTable;
+ ASSERT_TRUE(flatten(table.get(), &resTable));
- 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(&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));
}
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.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();
- 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.typeMask, actualAttr->typeMask);
+ EXPECT_EQ(attr.minInt, actualAttr->minInt);
+ EXPECT_EQ(attr.maxInt, actualAttr->maxInt);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index ed5b60f..c296dde 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -14,16 +14,16 @@
* limitations under the License.
*/
+#include "flatten/XmlFlattener.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 <utils/misc.h>
#include <algorithm>
#include <map>
-#include <utils/misc.h>
#include <vector>
using namespace android;
@@ -35,300 +35,316 @@
constexpr uint32_t kLowPriority = 0xffffffffu;
struct XmlFlattenerVisitor : public xml::Visitor {
- using xml::Visitor::visit;
+ using xml::Visitor::visit;
- BigBuffer* mBuffer;
- XmlFlattenerOptions mOptions;
- StringPool mPool;
- std::map<uint8_t, StringPool> mPackagePools;
+ BigBuffer* mBuffer;
+ XmlFlattenerOptions mOptions;
+ StringPool mPool;
+ std::map<uint8_t, StringPool> mPackagePools;
- struct StringFlattenDest {
- StringPool::Ref ref;
- ResStringPool_ref* dest;
- };
- std::vector<StringFlattenDest> mStringRefs;
+ struct StringFlattenDest {
+ StringPool::Ref ref;
+ ResStringPool_ref* dest;
+ };
+ std::vector<StringFlattenDest> mStringRefs;
- // Scratch vector to filter attributes. We avoid allocations
- // making this a member.
- std::vector<xml::Attribute*> mFilteredAttrs;
+ // Scratch vector to filter attributes. We avoid allocations
+ // making this a member.
+ std::vector<xml::Attribute*> mFilteredAttrs;
+ XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options)
+ : mBuffer(buffer), mOptions(options) {}
- XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options) :
- mBuffer(buffer), mOptions(options) {
+ 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});
+ }
+ }
+
+ 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;
}
- 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(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();
}
- 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 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);
- *resIdMapWriter.nextBlock<uint32_t>() = id.id;
+ 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;
}
- resIdMapWriter.finish();
+ }
+ if (attr.namespaceUri == xml::kSchemaTools) {
+ continue;
+ }
+ mFilteredAttrs.push_back(&attr);
}
- // Move the nodeBuffer and append it to the out buffer.
- mBuffer->appendBuffer(std::move(nodeBuffer));
+ if (mFilteredAttrs.empty()) {
+ return;
+ }
- // Finish the xml header.
- xmlHeaderWriter.finish();
- return true;
+ 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);
+
+ {
+ // 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;
+ }
+
+ *resIdMapWriter.nextBlock<uint32_t>() = id.id;
+ }
+ resIdMapWriter.finish();
+ }
+
+ // 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;
- }
- return flatten(context, resource->root.get());
+ if (!resource->root) {
+ return false;
+ }
+ return flatten(context, resource->root.get());
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/XmlFlattener.h b/tools/aapt2/flatten/XmlFlattener.h
index a688ac9..d8d592b 100644
--- a/tools/aapt2/flatten/XmlFlattener.h
+++ b/tools/aapt2/flatten/XmlFlattener.h
@@ -24,32 +24,31 @@
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 keepRawValues = 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> maxSdkLevel;
};
class XmlFlattener : public IXmlResourceConsumer {
-public:
- XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options) :
- mBuffer(buffer), mOptions(options) {
- }
+ public:
+ XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options)
+ : mBuffer(buffer), mOptions(options) {}
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ bool consume(IAaptContext* context, xml::XmlResource* resource) override;
-private:
- BigBuffer* mBuffer;
- XmlFlattenerOptions mOptions;
+ private:
+ BigBuffer* mBuffer;
+ XmlFlattenerOptions mOptions;
- bool flatten(IAaptContext* context, xml::Node* node);
+ bool flatten(IAaptContext* context, xml::Node* node);
};
-} // 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..e0159cb 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -25,230 +25,240 @@
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 {
+ 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();
+ }
+
+ ::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";
}
- ::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 (outTree->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> mContext;
};
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* namespacePrefix = tree.getNamespacePrefix(&len);
+ EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
- size_t len;
- const char16_t* namespacePrefix = tree.getNamespacePrefix(&len);
- EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
+ const char16_t* namespaceUri = tree.getNamespaceUri(&len);
+ ASSERT_EQ(StringPiece16(namespaceUri, 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* tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, 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* attrName = tree.getAttributeName(0, &len);
+ EXPECT_EQ(StringPiece16(attrName, 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);
+ tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, 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* attrNamespace = tree.getAttributeNamespace(0, &len);
+ EXPECT_EQ(StringPiece16(attrNamespace, 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");
+ attrName = tree.getAttributeName(0, &len);
+ EXPECT_EQ(StringPiece16(attrName, 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);
+ tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, 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);
+ 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);
- 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);
+ tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, 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);
+ namespacePrefix = tree.getNamespacePrefix(&len);
+ EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
- ASSERT_EQ(tree.next(), android::ResXMLTree::END_NAMESPACE);
- namespacePrefix = tree.getNamespacePrefix(&len);
- EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
+ namespaceUri = tree.getNamespaceUri(&len);
+ ASSERT_EQ(StringPiece16(namespaceUri, 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(mContext.get(), doc.get()));
+ ASSERT_TRUE(linker.getSdkLevels().count(17) == 1);
+ ASSERT_TRUE(linker.getSdkLevels().count(21) == 1);
- android::ResXMLTree tree;
- XmlFlattenerOptions options;
- options.maxSdkLevel = 17;
- ASSERT_TRUE(flatten(doc.get(), &tree, options));
+ android::ResXMLTree tree;
+ XmlFlattenerOptions options;
+ options.maxSdkLevel = 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* namespacePrefix = tree.getNamespacePrefix(&len);
+ EXPECT_EQ(StringPiece16(namespacePrefix, len), u"foo");
- const char16_t* namespaceUri = tree.getNamespaceUri(&len);
- ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://schemas.android.com/foo");
+ const char16_t* namespaceUri = tree.getNamespaceUri(&len);
+ ASSERT_EQ(StringPiece16(namespaceUri, 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..0479228b 100644
--- a/tools/aapt2/io/Data.h
+++ b/tools/aapt2/io/Data.h
@@ -18,105 +18,95 @@
#define AAPT_IO_DATA_H
#include <android-base/macros.h>
-#include <memory>
#include <utils/FileMap.h>
+#include <memory>
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)
+ : mData(std::move(data)), mOffset(offset), mLen(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*>(mData->data()) + mOffset;
+ }
- size_t size() const override {
- return mLen;
- }
+ size_t size() const override { return mLen; }
-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> mData;
+ size_t mOffset;
+ size_t mLen;
};
/**
- * 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)
+ : mMap(std::forward<android::FileMap>(map)) {}
- const void* data() const override {
- return mMap.getDataPtr();
- }
+ const void* data() const override { return mMap.getDataPtr(); }
- size_t size() const override {
- return mMap.getDataLength();
- }
+ size_t size() const override { return mMap.getDataLength(); }
-private:
- android::FileMap mMap;
+ private:
+ android::FileMap mMap;
};
/**
- * 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)
+ : mData(std::move(data)), mSize(size) {}
- const void* data() const override {
- return mData.get();
- }
+ const void* data() const override { return mData.get(); }
- size_t size() const override {
- return mSize;
- }
+ size_t size() const override { return mSize; }
-private:
- std::unique_ptr<const uint8_t[]> mData;
- size_t mSize;
+ private:
+ std::unique_ptr<const uint8_t[]> mData;
+ size_t mSize;
};
/**
- * 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.h b/tools/aapt2/io/File.h
index 807981e..012f446 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -30,83 +30,90 @@
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>> mSegments;
};
/**
- * 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)
+ : mFile(file), mOffset(offset), mLen(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 mFile->getSource(); }
-private:
- DISALLOW_COPY_AND_ASSIGN(FileSegment);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FileSegment);
- IFile* mFile;
- size_t mOffset;
- size_t mLen;
+ IFile* mFile;
+ size_t mOffset;
+ size_t mLen;
};
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.h b/tools/aapt2/io/FileSystem.h
index 72a932a..8584d48 100644
--- a/tools/aapt2/io/FileSystem.h
+++ b/tools/aapt2/io/FileSystem.h
@@ -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 mSource;
};
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 mCurrent, mEnd;
};
/**
* 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>> mFiles;
};
-} // 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.h b/tools/aapt2/io/Io.h
index e1e9107..49b9fc3 100644
--- a/tools/aapt2/io/Io.h
+++ b/tools/aapt2/io/Io.h
@@ -30,12 +30,10 @@
* The code style here matches the protobuf style.
*/
class InputStream : public google::protobuf::io::ZeroCopyInputStream {
-public:
- virtual std::string GetError() const {
- return {};
- }
+ public:
+ virtual std::string GetError() const { return {}; }
- virtual bool HadError() const = 0;
+ virtual bool HadError() const = 0;
};
/**
@@ -45,12 +43,10 @@
* The code style here matches the protobuf style.
*/
class OutputStream : public google::protobuf::io::ZeroCopyOutputStream {
-public:
- virtual std::string GetError() const {
- return {};
- }
+ public:
+ virtual std::string GetError() const { return {}; }
- virtual bool HadError() const = 0;
+ virtual bool HadError() const = 0;
};
/**
@@ -60,7 +56,7 @@
*/
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.h b/tools/aapt2/io/ZipArchive.h
index 565588e..e04525f 100644
--- a/tools/aapt2/io/ZipArchive.h
+++ b/tools/aapt2/io/ZipArchive.h
@@ -20,64 +20,66 @@
#include "io/File.h"
#include "util/StringPiece.h"
-#include <map>
#include <ziparchive/zip_archive.h>
+#include <map>
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 mZipHandle;
+ ZipEntry mZipEntry;
+ Source mSource;
};
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 mCurrent, mEnd;
};
/**
* 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 mHandle;
+ std::map<std::string, std::unique_ptr<IFile>> mFiles;
};
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
#endif /* AAPT_IO_ZIPARCHIVE_H */
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index cfc32f3..5419608 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -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 mComment;
+ std::stringstream mAnnotations;
+ bool mHasComments = false;
+ uint32_t mAnnotationBitMask = 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/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index e84c274..bd7e7b2 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -33,46 +33,43 @@
constexpr static const char* kIndent = " ";
class ClassMember {
-public:
- virtual ~ClassMember() = default;
+ public:
+ virtual ~ClassMember() = default;
- AnnotationProcessor* getCommentBuilder() {
- return &mProcessor;
- }
+ AnnotationProcessor* getCommentBuilder() { return &mProcessor; }
- 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 {
+ mProcessor.writeToStream(out, prefix);
+ }
-private:
- AnnotationProcessor mProcessor;
+ private:
+ AnnotationProcessor mProcessor;
};
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)
+ : mName(name.toString()), mVal(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 "
+ << mName << "=" << mVal << ";";
+ }
-private:
- std::string mName;
- T mVal;
+ private:
+ std::string mName;
+ T mVal;
- DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
};
/**
@@ -80,27 +77,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)
+ : mName(name.toString()), mVal(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 "
+ << mName << "=\"" << mVal << "\";";
+ }
-private:
- std::string mName;
- std::string mVal;
+ private:
+ std::string mName;
+ std::string mVal;
- DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
};
using IntMember = PrimitiveMember<uint32_t>;
@@ -109,80 +104,75 @@
template <typename T>
class PrimitiveArrayMember : public ClassMember {
-public:
- explicit PrimitiveArrayMember(const StringPiece& name) :
- mName(name.toString()) {
+ public:
+ explicit PrimitiveArrayMember(const StringPiece& name)
+ : mName(name.toString()) {}
+
+ void addElement(const T& val) { mElements.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[] " << 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 << "};";
+ }
- void addElement(const T& val) {
- mElements.push_back(val);
- }
+ private:
+ std::string mName;
+ std::vector<T> mElements;
- 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)
+ : mName(name.toString()),
+ mQualifier(qualifier),
+ mCreateIfEmpty(createIfEmpty) {}
- void addMember(std::unique_ptr<ClassMember> member) {
- mMembers.push_back(std::move(member));
- }
+ void addMember(std::unique_ptr<ClassMember> member) {
+ mMembers.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 mName;
+ ClassQualifier mQualifier;
+ bool mCreateIfEmpty;
+ std::vector<std::unique_ptr<ClassMember>> mMembers;
- 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.h b/tools/aapt2/java/JavaClassGenerator.h
index 901a86e..2fdf268 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -31,72 +31,74 @@
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 useFinal = 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> javadocAnnotations;
};
/*
* 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* mContext;
+ ResourceTable* mTable;
+ JavaClassGeneratorOptions mOptions;
+ std::string mError;
};
inline const std::string& JavaClassGenerator::getError() const {
- return mError;
+ return mError;
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_JAVA_CLASS_GENERATOR_H
+#endif // AAPT_JAVA_CLASS_GENERATOR_H
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
index f565289..1817648 100644
--- a/tools/aapt2/java/ManifestClassGenerator.h
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -26,8 +26,9 @@
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/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index c2d2bd9..7578ec2 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -30,29 +30,31 @@
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& className) {
+ mKeepSet[className].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& methodName) {
+ mKeepMethodSet[methodName].insert(source);
+ }
-private:
- friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+ private:
+ friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
- std::map<std::string, std::set<Source>> mKeepSet;
- std::map<std::string, std::set<Source>> mKeepMethodSet;
+ std::map<std::string, std::set<Source>> mKeepSet;
+ std::map<std::string, std::set<Source>> mKeepMethodSet;
};
-bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keepSet,
+bool collectProguardRulesForManifest(const Source& source,
+ xml::XmlResource* res, KeepSet* keepSet,
bool mainDexOnly = false);
-bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet);
+bool collectProguardRules(const Source& source, xml::XmlResource* res,
+ KeepSet* keepSet);
bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
-} // namespace proguard
-} // namespace aapt
+} // namespace proguard
+} // namespace aapt
-#endif // AAPT_PROGUARD_RULES_H
+#endif // AAPT_PROGUARD_RULES_H
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index 8ed27c3..5ba9819 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -25,117 +25,128 @@
namespace aapt {
-bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
+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);
+ // We assume the caller is trying to generate a version greater than the
+ // current configuration.
+ assert(sdkVersionToGenerate > config.sdkVersion);
- const auto endIter = entry->values.end();
- auto iter = entry->values.begin();
- for (; iter != endIter; ++iter) {
- if ((*iter)->config == config) {
- break;
- }
+ const auto endIter = entry->values.end();
+ auto iter = entry->values.begin();
+ for (; iter != endIter; ++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.
+ assert(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 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;
}
+ }
- // 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;
- }
+ 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* 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;
+ }
- if (Style* style = valueCast<Style>(configValue->value.get())) {
- Maybe<size_t> minSdkStripped;
- std::vector<Style::Entry> stripped;
+ if (Style* style = valueCast<Style>(configValue->value.get())) {
+ Maybe<size_t> minSdkStripped;
+ 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()) {
+ assert(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 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));
- // 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 (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);
+ }
+ }
+ }
}
+ }
}
- 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..04bf9cd 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -21,102 +21,114 @@
namespace aapt {
TEST(AutoVersionerTest, GenerateVersionedResources) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription landConfig = test::parseConfigOrDie("land");
- const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land");
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ const ConfigDescription sw600dpLandConfig =
+ 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>(defaultConfig, ""));
+ entry.values.push_back(
+ util::make_unique<ResourceConfigValue>(landConfig, ""));
+ entry.values.push_back(
+ util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
- EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
- EXPECT_TRUE(shouldGenerateVersionedResource(&entry, landConfig, 17));
+ EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
+ EXPECT_TRUE(shouldGenerateVersionedResource(&entry, landConfig, 17));
}
TEST(AutoVersionerTest, GenerateVersionedResourceWhenHigherVersionExists) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription sw600dpV13Config = test::parseConfigOrDie("sw600dp-v13");
- const ConfigDescription v21Config = test::parseConfigOrDie("v21");
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription sw600dpV13Config =
+ test::parseConfigOrDie("sw600dp-v13");
+ const ConfigDescription v21Config = 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>(defaultConfig, ""));
+ entry.values.push_back(
+ util::make_unique<ResourceConfigValue>(sw600dpV13Config, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(v21Config, ""));
- EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
- EXPECT_FALSE(shouldGenerateVersionedResource(&entry, defaultConfig, 22));
+ EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
+ EXPECT_FALSE(shouldGenerateVersionedResource(&entry, 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..a42d868 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -31,9 +31,9 @@
#include "java/ManifestClassGenerator.h"
#include "java/ProguardRules.h"
#include "link/Linkers.h"
+#include "link/ManifestFixer.h"
#include "link/ProductFilter.h"
#include "link/ReferenceLinker.h"
-#include "link/ManifestFixer.h"
#include "link/TableMerger.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
@@ -47,9 +47,9 @@
#include <android-base/file.h>
#include <google/protobuf/io/coded_stream.h>
+#include <sys/stat.h>
#include <fstream>
#include <queue>
-#include <sys/stat.h>
#include <unordered_map>
#include <vector>
@@ -58,1908 +58,2043 @@
namespace aapt {
struct LinkOptions {
- std::string outputPath;
- std::string manifestPath;
- std::vector<std::string> includePaths;
- std::vector<std::string> overlayFiles;
+ std::string outputPath;
+ std::string manifestPath;
+ std::vector<std::string> includePaths;
+ std::vector<std::string> overlayFiles;
- // Java/Proguard options.
- Maybe<std::string> generateJavaClassPath;
- Maybe<std::string> customJavaPackage;
- std::set<std::string> extraJavaPackages;
- Maybe<std::string> generateProguardRulesPath;
- Maybe<std::string> generateMainDexProguardRulesPath;
+ // Java/Proguard options.
+ Maybe<std::string> generateJavaClassPath;
+ Maybe<std::string> customJavaPackage;
+ std::set<std::string> extraJavaPackages;
+ Maybe<std::string> generateProguardRulesPath;
+ Maybe<std::string> generateMainDexProguardRulesPath;
- bool noAutoVersion = false;
- bool noVersionVectors = false;
- bool 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;
+ 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;
- // Split APK options.
- TableSplitterOptions tableSplitterOptions;
- std::vector<SplitConstraints> splitConstraints;
- std::vector<std::string> splitPaths;
+ // Split APK options.
+ TableSplitterOptions tableSplitterOptions;
+ std::vector<SplitConstraints> splitConstraints;
+ std::vector<std::string> splitPaths;
- // Stable ID options.
- std::unordered_map<ResourceName, ResourceId> stableIdMap;
- Maybe<std::string> resourceIdMapPath;
+ // Stable ID options.
+ std::unordered_map<ResourceName, ResourceId> stableIdMap;
+ Maybe<std::string> resourceIdMapPath;
};
class LinkContext : public IAaptContext {
-public:
- LinkContext() : mNameMangler({}) {
- }
+ public:
+ LinkContext() : mNameMangler({}) {}
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
- NameMangler* getNameMangler() override {
- return &mNameMangler;
- }
+ NameMangler* getNameMangler() override { return &mNameMangler; }
- void setNameManglerPolicy(const NameManglerPolicy& policy) {
- mNameMangler = NameMangler(policy);
- }
+ void setNameManglerPolicy(const NameManglerPolicy& policy) {
+ mNameMangler = NameMangler(policy);
+ }
- const std::string& getCompilationPackage() override {
- return mCompilationPackage;
- }
+ const std::string& getCompilationPackage() override {
+ return mCompilationPackage;
+ }
- void setCompilationPackage(const StringPiece& packageName) {
- mCompilationPackage = packageName.toString();
- }
+ void setCompilationPackage(const StringPiece& packageName) {
+ mCompilationPackage = packageName.toString();
+ }
- uint8_t getPackageId() override {
- return mPackageId;
- }
+ uint8_t getPackageId() override { return mPackageId; }
- void setPackageId(uint8_t id) {
- mPackageId = id;
- }
+ void setPackageId(uint8_t id) { mPackageId = id; }
- SymbolTable* getExternalSymbols() override {
- return &mSymbols;
- }
+ SymbolTable* getExternalSymbols() override { return &mSymbols; }
- bool verbose() override {
- return mVerbose;
- }
+ bool verbose() override { return mVerbose; }
- void setVerbose(bool val) {
- mVerbose = val;
- }
+ void setVerbose(bool val) { mVerbose = val; }
- int getMinSdkVersion() override {
- return mMinSdkVersion;
- }
+ int getMinSdkVersion() override { return mMinSdkVersion; }
- void setMinSdkVersion(int minSdk) {
- mMinSdkVersion = minSdk;
- }
+ void setMinSdkVersion(int minSdk) { mMinSdkVersion = minSdk; }
-private:
- StdErrDiagnostics mDiagnostics;
- NameMangler mNameMangler;
- std::string mCompilationPackage;
- uint8_t mPackageId = 0x0;
- SymbolTable mSymbols;
- bool mVerbose = false;
- int mMinSdkVersion = 0;
+ private:
+ StdErrDiagnostics mDiagnostics;
+ NameMangler mNameMangler;
+ std::string mCompilationPackage;
+ uint8_t mPackageId = 0x0;
+ SymbolTable mSymbols;
+ bool mVerbose = false;
+ int mMinSdkVersion = 0;
};
static bool copyFileToArchive(io::IFile* file, const std::string& outPath,
- uint32_t compressionFlags,
- 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);
+ uint32_t compressionFlags, 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);
+ 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* 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");
+ return false;
}
static std::unique_ptr<ResourceTable> loadTableFromPb(const Source& source,
- const void* data, size_t len,
+ 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 pbTable;
+ if (!pbTable.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(pbTable, 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 noAutoVersion = false;
+ bool noVersionVectors = false;
+ bool noXmlNamespaces = false;
+ bool keepRawValues = false;
+ bool doNotCompressAnything = false;
+ bool updateProguardSpec = false;
+ std::unordered_set<std::string> extensionsToNotCompress;
};
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* keepSet)
+ : mOptions(options), mContext(context), mKeepSet(keepSet) {}
- bool flatten(ResourceTable* table, IArchiveWriter* archiveWriter);
+ bool flatten(ResourceTable* table, IArchiveWriter* archiveWriter);
-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* fileToCopy;
- // The XML to process and flatten.
- std::unique_ptr<xml::XmlResource> xmlToFlatten;
+ // The XML to process and flatten.
+ std::unique_ptr<xml::XmlResource> xmlToFlatten;
- // The destination to write this file to.
- std::string dstPath;
- bool skipVersion = false;
- };
+ // The destination to write this file to.
+ std::string dstPath;
+ bool skipVersion = 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* fileOp,
+ std::queue<FileOperation>* outFileOpQueue);
- ResourceFileFlattenerOptions mOptions;
- IAaptContext* mContext;
- proguard::KeepSet* mKeepSet;
+ ResourceFileFlattenerOptions mOptions;
+ IAaptContext* mContext;
+ proguard::KeepSet* mKeepSet;
};
uint32_t ResourceFileFlattener::getCompressionFlags(const StringPiece& str) {
- if (mOptions.doNotCompressAnything) {
- return 0;
- }
+ if (mOptions.doNotCompressAnything) {
+ return 0;
+ }
- for (const std::string& extension : mOptions.extensionsToNotCompress) {
- if (util::stringEndsWith(str, extension)) {
- return 0;
- }
+ for (const std::string& extension : mOptions.extensionsToNotCompress) {
+ if (util::stringEndsWith(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* fileOp,
+ std::queue<FileOperation>* outFileOpQueue) {
+ xml::XmlResource* doc = fileOp->xmlToFlatten.get();
+ const Source& src = doc->file.source;
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage() << "linking " << src.path);
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(DiagMessage() << "linking " << src.path);
+ }
+
+ 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;
}
+ }
- 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 (!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;
}
+ }
}
- 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 = fileOp->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 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;
}
- const ConfigDescription& config = fileOp->config;
+ ResourceFile versionedFileDesc = doc->file;
+ versionedFileDesc.config.sdkVersion = (uint16_t)sdkLevel;
- // 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 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());
- 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 (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;
+ }
}
- 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* archiveWriter) {
+ bool error = false;
+ std::map<std::pair<ConfigDescription, StringPiece>, FileOperation>
+ configSortedFiles;
- 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.
+ configSortedFiles.clear();
+ std::queue<FileOperation> fileOperations;
- // 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& configValue : entry->values) {
+ FileReference* fileRef =
+ valueCast<FileReference>(configValue->value.get());
+ if (!fileRef) {
continue;
- }
+ }
- auto iter = std::find(line.begin(), line.end(), '=');
- if (iter == line.end()) {
- diag->error(DiagMessage(Source(path, lineNo)) << "missing '='");
+ 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;
}
- 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 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;
+ }
}
-
- 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 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()) {
+ continue;
+ }
+
+ auto iter = std::find(line.begin(), line.end(), '=');
+ if (iter == line.end()) {
+ diag->error(DiagMessage(Source(path, lineNo)) << "missing '='");
+ return false;
+ }
+
+ 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;
+ }
+
+ 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;
}
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;
+ std::string* outPath,
+ SplitConstraints* outSplit) {
+ std::vector<std::string> parts = util::split(arg, ':');
+ if (parts.size() != 2) {
+ diag->error(DiagMessage() << "invalid split parameter '" << arg << "'");
+ diag->note(
+ DiagMessage()
+ << "should be --split path/to/output.apk:<config>[,<config>...]");
+ return false;
+ }
+ *outPath = parts[0];
+ std::vector<ConfigDescription> configs;
+ for (const StringPiece& configStr : util::tokenize(parts[1], ',')) {
+ configs.push_back({});
+ if (!ConfigDescription::parse(configStr, &configs.back())) {
+ diag->error(DiagMessage() << "invalid config '" << configStr
+ << "' in split parameter '" << arg << "'");
+ return false;
}
- *outPath = parts[0];
- std::vector<ConfigDescription> configs;
- for (const StringPiece& configStr : util::tokenize(parts[1], ',')) {
- configs.push_back({});
- if (!ConfigDescription::parse(configStr, &configs.back())) {
- diag->error(DiagMessage() << "invalid config '" << configStr
- << "' in split parameter '" << arg << "'");
- return false;
- }
- }
- outSplit->configs.insert(configs.begin(), configs.end());
- return true;
+ }
+ outSplit->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)
+ : mOptions(options),
+ mContext(context),
+ mFinalTable(),
+ mFileCollection(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> assetSource =
+ util::make_unique<AssetManagerSymbolSource>();
+ for (const std::string& path : mOptions.includePaths) {
+ if (mContext->verbose()) {
+ mContext->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;
- }
- }
-
- 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;
- }
- }
- }
- }
- }
- }
-
- auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
- isExtPackageFunc);
- mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
- return !error;
- }
-
- /**
- * 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;
- }
-
- 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");
- 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;
- }
-
- // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->finishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
- // interface.
- CopyingOutputStreamAdaptor adaptor(writer);
-
- std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table);
- if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed to write");
- return false;
- }
- }
-
- if (!writer->finishEntry()) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed to finish entry");
- return false;
- }
- return true;
- }
-
- bool writeJavaFile(ResourceTable* table, const StringPiece& packageNameToGenerate,
- const StringPiece& outPackage, const JavaClassGeneratorOptions& javaOptions) {
- if (!mOptions.generateJavaClassPath) {
- return true;
- }
-
- 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;
- }
-
- 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)) {
- 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);
- }
-
- 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) {
- return false;
- }
-
- ResourceFileFlattenerOptions fileFlattenerOptions;
- fileFlattenerOptions.keepRawValues = keepRawValues;
- fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything;
- fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress;
- fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion;
- fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors;
- fileFlattenerOptions.noXmlNamespaces = mOptions.noXmlNamespaces;
- fileFlattenerOptions.updateProguardSpec =
- static_cast<bool>(mOptions.generateProguardRulesPath);
-
- ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, keepSet);
-
- if (!fileFlattener.flatten(table, writer)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources");
- return false;
- }
-
- if (mOptions.staticLib) {
- if (!flattenTableToPb(table, writer)) {
- mContext->getDiagnostics()->error(DiagMessage()
- << "failed to write resources.arsc.flat");
- return false;
- }
- } else {
- if (!flattenTable(table, writer)) {
- mContext->getDiagnostics()->error(DiagMessage()
- << "failed to write resources.arsc");
- return false;
- }
- }
- return true;
- }
-
- int run(const std::vector<std::string>& inputFiles) {
- // Load the AndroidManifest.xml
- std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
- mContext->getDiagnostics());
- if (!manifestXml) {
- return 1;
- }
-
- // 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;
- }
-
+ // 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) {
- PrivateAttributeMover mover;
- if (!mover.consume(mContext, &mFinalTable)) {
+ // 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;
+ }
+ }
+
+ 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() << "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 {
+ DiagMessage(configValue->value->getSource())
+ << "defined resource '" << resName
+ << "' for external package '" << package->name << "'");
error = true;
+ }
}
+ }
+ }
+ }
+ }
+
+ auto newEndIter =
+ std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
+ isExtPackageFunc);
+ mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
+ return !error;
+ }
+
+ /**
+ * 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;
}
- if (error) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed processing manifest");
- return 1;
+ 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");
+ 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;
+ }
+
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call
+ // writer->finishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the
+ // ZeroCopyOutputStream
+ // interface.
+ CopyingOutputStreamAdaptor adaptor(writer);
+
+ std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table);
+ if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed to write");
+ return false;
+ }
+ }
+
+ if (!writer->finishEntry()) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed to finish entry");
+ return false;
+ }
+ return true;
+ }
+
+ bool writeJavaFile(ResourceTable* table,
+ const StringPiece& packageNameToGenerate,
+ const StringPiece& outPackage,
+ const JavaClassGeneratorOptions& javaOptions) {
+ if (!mOptions.generateJavaClassPath) {
+ return true;
+ }
+
+ 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;
+ }
+
+ 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)) {
+ 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);
+ }
+
+ 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;
}
- if (!writeApk(archiveWriter.get(), &proguardKeepSet, manifestXml.get(), &mFinalTable)) {
- return 1;
+ 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) {
+ return false;
+ }
+
+ ResourceFileFlattenerOptions fileFlattenerOptions;
+ fileFlattenerOptions.keepRawValues = keepRawValues;
+ fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything;
+ fileFlattenerOptions.extensionsToNotCompress =
+ mOptions.extensionsToNotCompress;
+ fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion;
+ fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors;
+ fileFlattenerOptions.noXmlNamespaces = mOptions.noXmlNamespaces;
+ fileFlattenerOptions.updateProguardSpec =
+ static_cast<bool>(mOptions.generateProguardRulesPath);
+
+ ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext,
+ keepSet);
+
+ if (!fileFlattener.flatten(table, writer)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed linking file resources");
+ return false;
+ }
+
+ if (mOptions.staticLib) {
+ if (!flattenTableToPb(table, writer)) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to write resources.arsc.flat");
+ return false;
+ }
+ } else {
+ if (!flattenTable(table, writer)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed to write resources.arsc");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ int run(const std::vector<std::string>& inputFiles) {
+ // Load the AndroidManifest.xml
+ std::unique_ptr<xml::XmlResource> manifestXml =
+ loadXml(mOptions.manifestPath, mContext->getDiagnostics());
+ if (!manifestXml) {
+ return 1;
+ }
+
+ // 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) {
- 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 (!writeManifestJavaFile(manifestXml.get())) {
+ error = true;
+ }
}
- if (!writeProguardFile(mOptions.generateProguardRulesPath, proguardKeepSet)) {
- return 1;
+ 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;
+ }
}
-
- if (!writeProguardFile(mOptions.generateMainDexProguardRulesPath, proguardMainDexKeepSet)) {
- return 1;
- }
-
- if (mContext->verbose()) {
- DebugPrintTableOptions debugPrintTableOptions;
- debugPrintTableOptions.showSources = true;
- Debug::printTable(&mFinalTable, debugPrintTableOptions);
- }
- return 0;
+ } else {
+ error = true;
+ }
}
-private:
- LinkOptions mOptions;
- LinkContext* mContext;
- ResourceTable mFinalTable;
+ if (error) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed processing manifest");
+ return 1;
+ }
- std::unique_ptr<TableMerger> mTableMerger;
+ if (!writeApk(archiveWriter.get(), &proguardKeepSet, manifestXml.get(),
+ &mFinalTable)) {
+ return 1;
+ }
- // A pointer to the FileCollection representing the filesystem (not archives).
- std::unique_ptr<io::FileCollection> mFileCollection;
+ if (mOptions.generateJavaClassPath) {
+ JavaClassGeneratorOptions options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ options.javadocAnnotations = mOptions.javadocAnnotations;
- // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
- std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
+ if (mOptions.staticLib || mOptions.generateNonFinalIds) {
+ options.useFinal = 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;
+ 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;
+
+ std::unique_ptr<TableMerger> mTableMerger;
+
+ // A pointer to the FileCollection representing the filesystem (not archives).
+ std::unique_ptr<io::FileCollection> mFileCollection;
+
+ // A vector of IFileCollections. This is mainly here to keep ownership of the
+ // collections.
+ std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
+
+ // A vector of ResourceTables. This is here to retain ownership, so that the
+ // SymbolTable
+ // can use these.
+ std::vector<std::unique_ptr<ResourceTable>> mStaticTableIncludes;
};
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 "
+ 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 "
"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);
+ .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);
- 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> 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);
+ }
+ }
+
+ // 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);
+ }
+ }
+
+ if (verbose) {
+ context.setVerbose(verbose);
+ }
+
+ // 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());
+ }
+ }
+
+ if (productList) {
+ for (StringPiece product : util::tokenize(productList.value(), ',')) {
+ if (product != "" && product != "default") {
+ options.products.insert(product.toString());
+ }
+ }
+ }
+
+ 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);
+ }
}
- // 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.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;
}
- // 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.
+ 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 (verbose) {
- context.setVerbose(verbose);
+ if (!options.staticLib && stableIdFilePath) {
+ if (!loadStableIdMap(context.getDiagnostics(), stableIdFilePath.value(),
+ &options.stableIdMap)) {
+ 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.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;
}
+ }
- 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.staticLib) {
+ options.noAutoVersion = true;
+ options.noVersionVectors = 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(argList);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index ce455da..f40c0e8 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -30,106 +30,121 @@
struct ConfigDescription;
/**
- * Defines the location in which a value exists. This determines visibility of other
+ * 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
+ * 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,
+bool shouldGenerateVersionedResource(const ResourceEntry* entry,
+ const ConfigDescription& config,
const int sdkVersionToGenerate);
class AutoVersioner : public IResourceTableConsumer {
-public:
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ public:
+ bool consume(IAaptContext* context, ResourceTable* table) override;
};
class XmlAutoVersioner : public IXmlResourceConsumer {
-public:
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ public:
+ bool consume(IAaptContext* context, xml::XmlResource* resource) override;
};
class VersionCollapser : public IResourceTableConsumer {
-public:
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ public:
+ bool consume(IAaptContext* context, ResourceTable* table) override;
};
/**
* Removes duplicated key-value entries from dominated resources.
*/
class ResourceDeduper : public IResourceTableConsumer {
-public:
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ public:
+ bool consume(IAaptContext* context, ResourceTable* table) override;
};
/**
- * 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;
+ bool consume(IAaptContext* context, ResourceTable* table) override;
};
/**
* 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;
+ private:
+ bool mKeepUris;
-public:
- XmlNamespaceRemover(bool keepUris = false) : mKeepUris(keepUris) {
- };
+ public:
+ XmlNamespaceRemover(bool keepUris = false) : mKeepUris(keepUris){};
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ bool consume(IAaptContext* context, xml::XmlResource* resource) override;
};
/**
- * 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;
+ private:
+ std::set<int> mSdkLevelsFound;
-public:
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ public:
+ 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>& getSdkLevels() const { return mSdkLevelsFound; }
};
-} // 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..3c9298b 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "ResourceUtils.h"
#include "link/ManifestFixer.h"
+#include "ResourceUtils.h"
#include "util/Util.h"
#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"
@@ -25,295 +25,310 @@
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,
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> fullyQualifiedClassName =
+ util::getFullyQualifiedClassName("a", attr->value);
- StringPiece qualifiedClassName = fullyQualifiedClassName
- ? fullyQualifiedClassName.value() : attr->value;
+ StringPiece qualifiedClassName =
+ fullyQualifiedClassName ? fullyQualifiedClassName.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);
- }
+ if (!util::isJavaClassName(qualifiedClassName)) {
diag->error(DiagMessage(el->lineNumber)
- << "<" << el->name << "> is missing attribute 'android:name'");
+ << "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'");
+ return false;
}
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;
+ 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;
}
/**
- * The coreApp attribute in <manifest> is not a regular AAPT attribute, so type checking on it
+ * 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);
+ 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;
}
- return true;
+ attr->compiledValue = 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 (mOptions.renameManifestPackage) {
+ if (!util::isJavaPackageName(mOptions.renameManifestPackage.value())) {
+ diag->error(DiagMessage() << "invalid manifest package override '"
+ << mOptions.renameManifestPackage.value()
+ << "'");
+ return false;
+ }
+ }
+
+ if (mOptions.renameInstrumentationTargetPackage) {
+ if (!util::isJavaPackageName(
+ mOptions.renameInstrumentationTargetPackage.value())) {
+ diag->error(DiagMessage()
+ << "invalid instrumentation target package override '"
+ << mOptions.renameInstrumentationTargetPackage.value()
+ << "'");
+ return false;
+ }
+ }
+
+ // 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.renameInstrumentationTargetPackage) {
- if (!util::isJavaPackageName(mOptions.renameInstrumentationTargetPackage.value())) {
- diag->error(DiagMessage() << "invalid instrumentation target package override '"
- << mOptions.renameInstrumentationTargetPackage.value() << "'");
- return false;
- }
+ if (mOptions.versionCodeDefault) {
+ if (el->findAttribute(xml::kSchemaAndroid, "versionCode") == nullptr) {
+ el->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "versionCode",
+ mOptions.versionCodeDefault.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.
+ 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;
}
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)
+ : mPackage(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.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());
}
-
- // 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 mPackage;
+ std::unordered_set<StringPiece> mClassAttributes = {"name"};
};
-static bool renameManifestPackage(const StringPiece& packageOverride, xml::Element* manifestEl) {
- xml::Attribute* attr = manifestEl->findAttribute({}, "package");
+static bool renameManifestPackage(const StringPiece& packageOverride,
+ xml::Element* manifestEl) {
+ xml::Attribute* attr = manifestEl->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.
+ assert(attr);
- std::string originalPackage = std::move(attr->value);
- attr->value = packageOverride.toString();
+ std::string originalPackage = std::move(attr->value);
+ attr->value = packageOverride.toString();
- FullyQualifiedClassNameVisitor visitor(originalPackage);
- manifestEl->accept(&visitor);
- return true;
+ FullyQualifiedClassNameVisitor visitor(originalPackage);
+ manifestEl->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;
- }
+ 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;
+ }
- 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 ((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));
+ }
- 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::Whitelist,
+ 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 (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;
}
- return true;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 2e81266..c3a114b 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -27,12 +27,12 @@
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> minSdkVersionDefault;
+ Maybe<std::string> targetSdkVersionDefault;
+ Maybe<std::string> renameManifestPackage;
+ Maybe<std::string> renameInstrumentationTargetPackage;
+ Maybe<std::string> versionNameDefault;
+ Maybe<std::string> versionCodeDefault;
};
/**
@@ -40,18 +40,18 @@
* where specified with ManifestFixerOptions.
*/
class ManifestFixer : public IXmlResourceConsumer {
-public:
- explicit ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) {
- }
+ public:
+ explicit ManifestFixer(const ManifestFixerOptions& options)
+ : mOptions(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:
+ bool buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag);
- ManifestFixerOptions mOptions;
+ ManifestFixerOptions mOptions;
};
-} // 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..dc78d98 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -23,254 +23,274 @@
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.renameManifestPackage = 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.renameInstrumentationTargetPackage = 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* manifestEl = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
- xml::Element* instrumentationEl = manifestEl->findChild({}, "instrumentation");
- ASSERT_NE(nullptr, instrumentationEl);
+ xml::Element* instrumentationEl =
+ manifestEl->findChild({}, "instrumentation");
+ ASSERT_NE(nullptr, instrumentationEl);
- xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, "targetPackage");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("com.android"), attr->value);
+ xml::Attribute* attr =
+ instrumentationEl->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.versionNameDefault = std::string("Beta");
+ options.versionCodeDefault = 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* manifestEl = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
- xml::Attribute* attr = manifestEl->findAttribute(xml::kSchemaAndroid, "versionName");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("Beta"), attr->value);
+ xml::Attribute* attr =
+ manifestEl->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 = manifestEl->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->compiledValue);
+ EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(attr->compiledValue.get()));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/PrivateAttributeMover.cpp b/tools/aapt2/link/PrivateAttributeMover.cpp
index 3c8af4f..174b41f 100644
--- a/tools/aapt2/link/PrivateAttributeMover.cpp
+++ b/tools/aapt2/link/PrivateAttributeMover.cpp
@@ -25,56 +25,61 @@
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);
+ const auto last = inputContainer.end();
+ auto newEnd =
+ std::find_if(inputContainer.begin(), inputContainer.end(), pred);
+ if (newEnd == 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(*newEnd);
- if (type->symbolStatus.state != SymbolState::kPublic) {
- // No public attributes, so we can safely leave these private attributes where they are.
- return true;
- }
+ auto first = newEnd;
+ ++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.
+ *newEnd = std::move(*first);
+ ++newEnd;
}
- return true;
+ }
+
+ inputContainer.erase(newEnd, 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->symbolStatus.state != SymbolState::kPublic) {
+ // No public attributes, so we can safely leave these private attributes
+ // where they are.
+ return true;
+ }
+
+ 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;
+ }
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/PrivateAttributeMover_test.cpp b/tools/aapt2/link/PrivateAttributeMover_test.cpp
index c9d1a08..a7a1013 100644
--- a/tools/aapt2/link/PrivateAttributeMover_test.cpp
+++ b/tools/aapt2/link/PrivateAttributeMover_test.cpp
@@ -20,56 +20,60 @@
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..d59b682 100644
--- a/tools/aapt2/link/ProductFilter.cpp
+++ b/tools/aapt2/link/ProductFilter.cpp
@@ -18,101 +18,103 @@
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 defaultProductIter = end;
+ ResourceConfigValueIter selectedProductIter = 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* 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");
- 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* previouslySelectedConfigValue =
+ selectedProductIter->get();
+ diag->note(
+ DiagMessage(previouslySelectedConfigValue->value->getSource())
+ << "product '" << previouslySelectedConfigValue->product
+ << "' is also a candidate");
return end;
+ }
+
+ // Select this product.
+ selectedProductIter = iter;
}
- if (selectedProductIter == end) {
- selectedProductIter = defaultProductIter;
+ 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;
}
- return selectedProductIter;
+ }
+
+ if (defaultProductIter == end) {
+ diag->error(DiagMessage() << "no default product defined for resource "
+ << name);
+ return end;
+ }
+
+ if (selectedProductIter == end) {
+ selectedProductIter = defaultProductIter;
+ }
+ return selectedProductIter;
}
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 error = false;
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ for (auto& entry : type->entries) {
+ std::vector<std::unique_ptr<ResourceConfigValue>> newValues;
- 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 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);
+ }
}
- return !error;
+ }
+ return !error;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter.h b/tools/aapt2/link/ProductFilter.h
index 7724e14..cc8b8c2 100644
--- a/tools/aapt2/link/ProductFilter.h
+++ b/tools/aapt2/link/ProductFilter.h
@@ -26,24 +26,25 @@
namespace aapt {
class ProductFilter {
-public:
- using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
+ public:
+ using ResourceConfigValueIter =
+ std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
- explicit ProductFilter(std::unordered_set<std::string> products) : mProducts(products) { }
+ explicit ProductFilter(std::unordered_set<std::string> products)
+ : mProducts(products) {}
- ResourceConfigValueIter selectProductToKeep(const ResourceNameRef& name,
- const ResourceConfigValueIter begin,
- const ResourceConfigValueIter end,
- IDiagnostics* diag);
+ ResourceConfigValueIter selectProductToKeep(
+ const ResourceNameRef& name, const ResourceConfigValueIter begin,
+ const ResourceConfigValueIter end, IDiagnostics* diag);
- bool consume(IAaptContext* context, ResourceTable* table);
+ bool consume(IAaptContext* context, ResourceTable* table);
-private:
- std::unordered_set<std::string> mProducts;
+ private:
+ std::unordered_set<std::string> mProducts;
- DISALLOW_COPY_AND_ASSIGN(ProductFilter);
+ DISALLOW_COPY_AND_ASSIGN(ProductFilter);
};
-} // namespace aapt
+} // 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..7f78f8b 100644
--- a/tools/aapt2/link/ProductFilter_test.cpp
+++ b/tools/aapt2/link/ProductFilter_test.cpp
@@ -20,114 +20,110 @@
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..7fe0956 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "Diagnostics.h"
#include "ReferenceLinker.h"
+#include "Diagnostics.h"
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
@@ -34,302 +34,335 @@
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* stringPool, xml::IPackageDeclStack* decl,
+ CallSite* callSite)
+ : mContext(context),
+ mSymbols(symbols),
+ mPackageDecls(decl),
+ mStringPool(stringPool),
+ mCallSite(callSite) {}
+
+ void visit(Reference* ref) override {
+ if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls,
+ mCallSite)) {
+ mError = 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 errStr;
- /**
- * 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 transformedReference = entry.key;
+ transformReferenceFromNamespace(mPackageDecls,
+ mContext->getCompilationPackage(),
+ &transformedReference);
+
+ // 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;
+
+ // 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);
+ mContext->getDiagnostics()->error(msg);
+ mError = true;
}
- for (Style::Entry& entry : style->entries) {
- std::string errStr;
+ } else {
+ DiagMessage msg(entry.key.getSource());
+ msg << "style attribute '";
+ ReferenceLinker::writeResourceName(&msg, entry.key,
+ transformedReference);
+ msg << "' " << errStr;
+ mContext->getDiagnostics()->error(msg);
+ mError = 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 mError; }
- // 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:
+ IAaptContext* mContext;
+ SymbolTable* mSymbols;
+ xml::IPackageDeclStack* mPackageDecls;
+ StringPool* mStringPool;
+ CallSite* mCallSite;
+ bool mError = false;
- // 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* rawString = valueCast<RawString>(value.get())) {
+ std::unique_ptr<Item> transformed =
+ ResourceUtils::tryParseItemForAttribute(*rawString->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->typeMask & android::ResTable_map::TYPE_STRING)) {
+ util::StringBuilder stringBuilder;
+ stringBuilder.append(*rawString->value);
+ if (stringBuilder) {
+ transformed = util::make_unique<String>(
+ mStringPool->makeRef(stringBuilder.str()));
}
- }
+ }
- bool hasError() {
- return mError;
- }
-
-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;
- }
+ if (transformed) {
+ return transformed;
+ }
+ };
+ return value;
+ }
};
-} // namespace
+} // 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,
+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;
- }
+ 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;
+ }
+ 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());
- } else {
- return nullptr;
- }
+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::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;
- }
+ 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;
+ if (!isSymbolVisible(*symbol, reference, *callSite)) {
+ if (outError) *outError = "is private";
+ return nullptr;
+ }
+ return symbol;
}
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 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;
+ }
- if (!symbol->attribute) {
- if (outError) *outError = "is not an attribute";
- return nullptr;
- }
- return symbol;
+ if (!symbol->attribute) {
+ if (outError) *outError = "is not an attribute";
+ 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 {};
- }
+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 {};
+ }
- if (!symbol->attribute) {
- if (outError) *outError = "is not an attribute";
- return {};
- }
- return xml::AaptAttribute{ symbol->id, *symbol->attribute };
+ if (!symbol->attribute) {
+ if (outError) *outError = "is not an attribute";
+ return {};
+ }
+ return xml::AaptAttribute{symbol->id, *symbol->attribute};
}
-void ReferenceLinker::writeResourceName(DiagMessage* outMsg, const Reference& orig,
+void ReferenceLinker::writeResourceName(DiagMessage* outMsg,
+ const Reference& orig,
const Reference& transformed) {
- assert(outMsg);
+ assert(outMsg);
- 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) {
+ *outMsg << orig.name.value();
+ if (transformed.name.value() != orig.name.value()) {
+ *outMsg << " (aka " << transformed.name.value() << ")";
}
+ } else {
+ *outMsg << orig.id.value();
+ }
}
bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
- SymbolTable* symbols, xml::IPackageDeclStack* decls,
+ SymbolTable* symbols,
+ xml::IPackageDeclStack* decls,
CallSite* callSite) {
- assert(reference);
- assert(reference->name || reference->id);
+ assert(reference);
+ assert(reference->name || reference->id);
- Reference transformedReference = *reference;
- transformReferenceFromNamespace(decls, context->getCompilationPackage(),
- &transformedReference);
+ Reference transformedReference = *reference;
+ transformReferenceFromNamespace(decls, context->getCompilationPackage(),
+ &transformedReference);
- 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 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;
+ }
- DiagMessage errorMsg(reference->getSource());
- errorMsg << "resource ";
- writeResourceName(&errorMsg, *reference, transformedReference);
- errorMsg << " " << errStr;
- context->getDiagnostics()->error(errorMsg);
- return false;
+ DiagMessage errorMsg(reference->getSource());
+ errorMsg << "resource ";
+ writeResourceName(&errorMsg, *reference, transformedReference);
+ errorMsg << " " << errStr;
+ context->getDiagnostics()->error(errorMsg);
+ 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 */ };
- }
- return {};
+ Maybe<xml::ExtractedPackage> transformPackageAlias(
+ const StringPiece& alias,
+ const StringPiece& localPackage) const override {
+ if (alias.empty()) {
+ return xml::ExtractedPackage{localPackage.toString(), true /* private */};
}
+ return {};
+ }
};
-} // namespace
+} // namespace
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;
- }
- }
+ 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;
+ }
+ }
}
- return !error;
+ }
+ return !error;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 7993aaf..8f6604f 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -30,77 +30,86 @@
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);
+ /**
+ * 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. Returns nullptr
- * if the symbol was not found.
- */
- static const SymbolTable::Symbol* resolveSymbol(const Reference& reference,
- NameMangler* mangler, SymbolTable* symbols);
+ /**
+ * 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);
- /**
- * 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. 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);
- /**
- * 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);
+ /**
+ * 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);
- /**
- * 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);
+ /**
+ * 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);
- /**
- * 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);
+ /**
+ * 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);
- /**
- * 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);
+ /**
+ * 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;
+ /**
+ * Links all references in the ResourceTable.
+ */
+ bool consume(IAaptContext* context, ResourceTable* table) override;
};
-} // 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..8aa3616 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -22,206 +22,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->stringPool.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..f565359 100644
--- a/tools/aapt2/link/ResourceDeduper.cpp
+++ b/tools/aapt2/link/ResourceDeduper.cpp
@@ -36,79 +36,81 @@
* 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)
+ : mContext(context), mEntry(entry) {}
+
+ 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;
}
- 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& 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 = {};
+ }
-private:
- IAaptContext* mContext;
- ResourceEntry* mEntry;
+ private:
+ IAaptContext* mContext;
+ ResourceEntry* mEntry;
};
static void dedupeEntry(IAaptContext* context, ResourceEntry* entry) {
- DominatorTree tree(entry->values);
- DominatedKeyValueRemover remover(context, entry);
- tree.accept(&remover);
+ 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());
- }
- }
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ dedupeEntry(context, entry.get());
+ }
}
- return true;
+ }
+ return true;
}
-} // aapt
+} // aapt
diff --git a/tools/aapt2/link/ResourceDeduper_test.cpp b/tools/aapt2/link/ResourceDeduper_test.cpp
index 47071a51..7e2d476 100644
--- a/tools/aapt2/link/ResourceDeduper_test.cpp
+++ b/tools/aapt2/link/ResourceDeduper_test.cpp
@@ -21,63 +21,63 @@
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 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<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{}, 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();
- 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", 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));
}
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 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<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{}, defaultConfig, "keep")
+ .addString("android:string/keep", ResourceId{}, enConfig, "keep")
+ .addString("android:string/keep", ResourceId{}, enV21Config, "keep2")
+ .addString("android:string/keep", ResourceId{}, landConfig, "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", 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));
}
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index eea4306..adf83a4 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -14,11 +14,11 @@
* limitations under the License.
*/
+#include "link/TableMerger.h"
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "link/TableMerger.h"
#include "util/Util.h"
#include <cassert>
@@ -26,348 +26,365 @@
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");
+ 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");
}
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,
io::IFileCollection* collection) {
- return mergeImpl(src, table, collection, true /* overlay */, mOptions.autoAddOverlay);
+ return mergeImpl(src, table, collection, true /* overlay */,
+ mOptions.autoAddOverlay);
}
/**
* 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();
+ io::IFileCollection* collection, bool overlay,
+ bool allowNew) {
+ const uint8_t desiredPackageId = mContext->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() != desiredPackageId) {
+ mContext->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() ||
+ 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);
+ }
+ }
+ 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& 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;
}
- return !error;
+
+ 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);
+ }
+ 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* 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;
}
- return true;
+ 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;
+ }
+ 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* 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;
}
- return true;
+ 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;
+ }
+ 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* existingStyleable = valueCast<Styleable>(existing)) {
+ if (Styleable* incomingStyleable = valueCast<Styleable>(incoming)) {
+ // Styleables get merged.
+ existingStyleable->mergeWith(incomingStyleable);
+ 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& resName, const bool overlay,
+ ResourceConfigValue* dstConfigValue, ResourceConfigValue* srcConfigValue) {
+ using CollisionResult = ResourceTable::CollisionResult;
- Value* dstValue = dstConfigValue->value.get();
- Value* srcValue = srcConfigValue->value.get();
+ Value* dstValue = dstConfigValue->value.get();
+ Value* srcValue = srcConfigValue->value.get();
- CollisionResult collisionResult;
+ CollisionResult collisionResult;
+ if (overlay) {
+ collisionResult = resolveMergeCollision(dstValue, srcValue);
+ } else {
+ collisionResult = ResourceTable::resolveValueCollision(dstValue, srcValue);
+ }
+
+ if (collisionResult == 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(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;
}
-bool TableMerger::doMerge(const Source& src,
- ResourceTable* srcTable,
+bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable,
ResourceTablePackage* srcPackage,
- const bool manglePackage,
- const bool overlay,
+ const bool manglePackage, const bool overlay,
const bool allowNewResources,
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& srcType : srcPackage->types) {
+ ResourceTableType* dstType =
+ mMasterPackage->findOrCreateType(srcType->type);
+ if (!mergeType(mContext, src, dstType, srcType.get())) {
+ error = true;
+ continue;
+ }
+
+ for (auto& srcEntry : srcType->entries) {
+ std::string entryName = srcEntry->name;
+ if (manglePackage) {
+ entryName = NameMangler::mangleEntry(srcPackage->name, srcEntry->name);
+ }
+
+ 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);
}
- 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>(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);
- 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 {
+ dstConfigValue->value = std::unique_ptr<Value>(
+ srcConfigValue->value->clone(&mMasterTable->stringPool));
}
+ }
}
- 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& 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));
}
-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& 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;
- 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(fileDesc.name.package, 0x0);
+ pkg->findOrCreateType(fileDesc.name.type)
+ ->findOrCreateEntry(fileDesc.name.entry)
+ ->findOrCreateValue(fileDesc.config, {})
+ ->value = std::move(fileRef);
- 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 */);
+ return mergeFileImpl(fileDesc, file, false /* overlay */);
}
-bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file) {
- return mergeFileImpl(fileDesc, file, true /* overlay */);
+bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc,
+ io::IFile* file) {
+ return mergeFileImpl(fileDesc, file, true /* overlay */);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index 3473a27..c2e7181 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -31,96 +31,111 @@
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 autoAddOverlay = 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 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);
- const std::set<std::string>& getMergedPackages() const {
- return mMergedPackages;
- }
+ const std::set<std::string>& getMergedPackages() const {
+ return mMergedPackages;
+ }
- /**
- * 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:
+ using FileMergeCallback = std::function<bool(const ResourceNameRef&,
+ const ConfigDescription& config,
+ FileReference*, FileReference*)>;
- IAaptContext* mContext;
- ResourceTable* mMasterTable;
- TableMergerOptions mOptions;
- ResourceTablePackage* mMasterPackage;
+ IAaptContext* mContext;
+ ResourceTable* mMasterTable;
+ TableMergerOptions mOptions;
+ ResourceTablePackage* mMasterPackage;
- std::set<std::string> mMergedPackages;
+ std::set<std::string> mMergedPackages;
- bool mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay);
+ bool mergeFileImpl(const ResourceFile& fileDesc, 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* srcTable,
+ io::IFileCollection* collection, bool overlay, bool allowNew);
- 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* srcTable,
+ ResourceTablePackage* srcPackage, const bool manglePackage,
+ const bool overlay, const bool allowNewResources,
+ 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..e0b2b66 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -14,9 +14,9 @@
* 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"
@@ -25,292 +25,327 @@
namespace aapt {
struct TableMergerTest : public ::testing::Test {
- std::unique_ptr<IAaptContext> mContext;
+ std::unique_ptr<IAaptContext> mContext;
- void SetUp() override {
- mContext = test::ContextBuilder()
- // We are compiling this package.
- .setCompilationPackage("com.app.a")
+ void SetUp() override {
+ mContext =
+ 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> 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> tableB = test::ResourceTableBuilder()
- .setPackageId("com.app.b", 0x7f)
- .addSimple("com.app.b:id/foo")
- .build();
+ std::unique_ptr<ResourceTable> tableB = 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 finalTable;
+ TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+ io::FileCollection collection;
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeAndMangle({}, "com.app.b", tableB.get(), &collection));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(
+ merger.mergeAndMangle({}, "com.app.b", tableB.get(), &collection));
- EXPECT_TRUE(merger.getMergedPackages().count("com.app.b") != 0);
+ EXPECT_TRUE(merger.getMergedPackages().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(
+ 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")));
- // 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(
+ finalTable.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(finalTable.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 finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, 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 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");
- ASSERT_TRUE(merger.mergeFile(fileDesc, &testFile));
+ ASSERT_TRUE(merger.mergeFile(fileDesc, &testFile));
- 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>(
+ &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);
}
TEST_F(TableMergerTest, MergeFileOverlay) {
- ResourceTable finalTable;
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+ ResourceTable finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
- 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 fileDesc;
+ fileDesc.name = test::parseNameOrDie("xml/foo");
+ test::TestFile fileA("path/to/fileA.xml.flat");
+ test::TestFile fileB("path/to/fileB.xml.flat");
- ASSERT_TRUE(merger.mergeFile(fileDesc, &fileA));
- ASSERT_TRUE(merger.mergeFileOverlay(fileDesc, &fileB));
+ ASSERT_TRUE(merger.mergeFile(fileDesc, &fileA));
+ ASSERT_TRUE(merger.mergeFileOverlay(fileDesc, &fileB));
}
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> 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();
- ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
- io::FileCollection collection;
- collection.insertFile("res/xml/file.xml");
+ ResourceTable finalTable;
+ TableMerger merger(mContext.get(), &finalTable, 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({}, tableA.get()));
+ ASSERT_TRUE(
+ merger.mergeAndMangle({}, "com.app.b", tableB.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>(&finalTable, "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>(&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);
}
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 finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
- 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>(&finalTable, "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 finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
- 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 finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
- 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 finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
- 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> 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();
- ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+ ResourceTable finalTable;
+ TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, tableB.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> tableA =
+ test::ResourceTableBuilder().setPackageId("", 0x7f).build();
+ std::unique_ptr<ResourceTable> tableB =
+ 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 finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = true;
+ TableMerger merger(mContext.get(), &finalTable, options);
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, tableB.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> tableA =
+ test::ResourceTableBuilder().setPackageId("", 0x7f).build();
+ std::unique_ptr<ResourceTable> tableB =
+ 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 finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, options);
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_FALSE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_FALSE(merger.mergeOverlay({}, tableB.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> 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> 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> 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();
- ResourceTable finalTable;
- TableMergerOptions options;
- options.autoAddOverlay = true;
- TableMerger merger(mContext.get(), &finalTable, options);
+ ResourceTable finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = true;
+ TableMerger merger(mContext.get(), &finalTable, options);
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
- Debug::printTable(&finalTable, {});
+ Debug::printTable(&finalTable, {});
- Styleable* styleable = test::getValue<Styleable>(&finalTable, "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> 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)),
- };
+ 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(expectedRefs, styleable->entries);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/VersionCollapser.cpp b/tools/aapt2/link/VersionCollapser.cpp
index 949d656..61a1f86 100644
--- a/tools/aapt2/link/VersionCollapser.cpp
+++ b/tools/aapt2/link/VersionCollapser.cpp
@@ -24,129 +24,140 @@
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())
+ : mCurrent(begin), mEnd(end), mPred(pred) {
+ advance();
+ }
- bool hasNext() {
- return mCurrent != mEnd;
- }
+ bool hasNext() { return mCurrent != mEnd; }
- Iterator nextIter() {
- Iterator iter = mCurrent;
- ++mCurrent;
- advance();
- return iter;
- }
+ Iterator nextIter() {
+ Iterator iter = mCurrent;
+ ++mCurrent;
+ 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 (; mCurrent != mEnd; ++mCurrent) {
+ if (mPred(*mCurrent)) {
+ return;
+ }
}
+ }
- Iterator mCurrent, mEnd;
- Pred mPred;
+ Iterator mCurrent, mEnd;
+ Pred mPred;
};
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> makeFilterIterator(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() = {};
- }
- }
+ // 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 <= 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.
- // 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 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;
}
- }
- 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.
+ 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() = {};
+ }
}
+ }
+
+ // 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>& 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;
+ }
+ }
+
+ 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());
- }
- }
+ const int minSdk = context->getMinSdkVersion();
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ collapseVersions(minSdk, 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..c0e0ddb 100644
--- a/tools/aapt2/link/VersionCollapser_test.cpp
+++ b/tools/aapt2/link/VersionCollapser_test.cpp
@@ -22,82 +22,96 @@
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 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();
}
TEST(VersionCollapserTest, CollapseVersions) {
- uptr<IAaptContext> context = test::ContextBuilder().setMinSdkVersion(7).build();
+ uptr<IAaptContext> context =
+ test::ContextBuilder().setMinSdkVersion(7).build();
- const StringPiece resName = "@android:string/foo";
+ const StringPiece resName = "@android:string/foo";
- uptr<ResourceTable> table =
- buildTableWithConfigs(resName,
- { "land-v4", "land-v5", "sw600dp", "land-v6",
- "land-v14", "land-v21" });
+ uptr<ResourceTable> table = buildTableWithConfigs(
+ resName,
+ {"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(), 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 remain.
- EXPECT_NE(nullptr,
- test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("sw600dp")));
+ // These should remain.
+ EXPECT_NE(nullptr,
+ test::getValueForConfig<Id>(table.get(), resName,
+ 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(), 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")));
}
TEST(VersionCollapserTest, CollapseVersionsWhenMinSdkIsHighest) {
- uptr<IAaptContext> context = test::ContextBuilder().setMinSdkVersion(21).build();
+ uptr<IAaptContext> context =
+ test::ContextBuilder().setMinSdkVersion(21).build();
- const StringPiece resName = "@android:string/foo";
+ const StringPiece resName = "@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()));
+ 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()));
- // 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(), 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 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(), resName,
+ 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(), resName, test::parseConfigOrDie("land")));
+ // land-v22 should remain as-is.
+ EXPECT_NE(nullptr,
+ test::getValueForConfig<Id>(table.get(), resName,
+ test::parseConfigOrDie("land-v22")));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlNamespaceRemover.cpp b/tools/aapt2/link/XmlNamespaceRemover.cpp
index 9f95177..6e8d80d 100644
--- a/tools/aapt2/link/XmlNamespaceRemover.cpp
+++ b/tools/aapt2/link/XmlNamespaceRemover.cpp
@@ -27,57 +27,59 @@
* Visits each xml Node, removing URI references and nested namespaces.
*/
class XmlVisitor : public xml::Visitor {
-public:
- XmlVisitor(bool keepUris) : mKeepUris(keepUris) {
- }
+ public:
+ XmlVisitor(bool keepUris) : mKeepUris(keepUris) {}
- void visit(xml::Element* el) override {
- // Strip namespaces
- for (auto& child : el->children) {
- while (child && xml::nodeCast<xml::Namespace>(child.get())) {
- if (child->children.empty()) {
- child = {};
- } else {
- child = std::move(child->children.front());
- child->parent = el;
- }
- }
+ 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 (!mKeepUris) {
+ for (xml::Attribute& attr : el->attributes) {
+ attr.namespaceUri = std::string();
+ }
+ el->namespaceUri = std::string();
+ }
+ xml::Visitor::visit(el);
+ }
+
+ private:
+ bool mKeepUris;
};
-} // 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(mKeepUris);
+ 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..d2daaee 100644
--- a/tools/aapt2/link/XmlNamespaceRemover_test.cpp
+++ b/tools/aapt2/link/XmlNamespaceRemover_test.cpp
@@ -20,90 +20,92 @@
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:
+ void visit(xml::Element* el) override {
+ for (const auto& attr : el->attributes) {
+ EXPECT_EQ(std::string(), attr.namespaceUri);
}
+ EXPECT_EQ(std::string(), el->namespaceUri);
+ xml::Visitor::visit(el);
+ }
- void visit(xml::Namespace* ns) override {
- EXPECT_EQ(std::string(), ns->namespaceUri);
- xml::Visitor::visit(ns);
- }
+ void visit(xml::Namespace* ns) override {
+ EXPECT_EQ(std::string(), ns->namespaceUri);
+ xml::Visitor::visit(ns);
+ }
};
class XmlNamespaceTestVisitor : public xml::Visitor {
-public:
- void visit(xml::Namespace* ns) override {
- ADD_FAILURE() << "Detected namespace: "
- << ns->namespacePrefix << "=\"" << ns->namespaceUri << "\"";
- xml::Visitor::visit(ns);
- }
+ public:
+ void visit(xml::Namespace* ns) override {
+ ADD_FAILURE() << "Detected namespace: " << ns->namespacePrefix << "=\""
+ << ns->namespaceUri << "\"";
+ xml::Visitor::visit(ns);
+ }
};
class XmlNamespaceRemoverTest : public ::testing::Test {
-public:
- void SetUp() override {
- mContext = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .build();
- }
+ public:
+ void SetUp() override {
+ mContext =
+ test::ContextBuilder().setCompilationPackage("com.app.test").build();
+ }
-protected:
- std::unique_ptr<IAaptContext> mContext;
+ protected:
+ std::unique_ptr<IAaptContext> mContext;
};
TEST_F(XmlNamespaceRemoverTest, RemoveUris) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:text="hello" />)EOF");
- XmlNamespaceRemover remover;
- ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
+ XmlNamespaceRemover remover;
+ ASSERT_TRUE(remover.consume(mContext.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(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:foo="http://schemas.android.com/apk/res/foo"
foo:bar="foobar"
android:text="hello" />)EOF");
- XmlNamespaceRemover remover;
- ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
+ XmlNamespaceRemover remover;
+ ASSERT_TRUE(remover.consume(mContext.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(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:text="hello">
<View xmlns:foo="http://schemas.example.com/foo"
android:text="foo"/>
</View>)EOF");
- XmlNamespaceRemover remover;
- ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
+ XmlNamespaceRemover remover;
+ ASSERT_TRUE(remover.consume(mContext.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..945f98a 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -29,146 +29,156 @@
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)
+ : mContext(context),
+ mSymbols(symbols),
+ mDecls(decls),
+ mCallSite(callSite),
+ mError(false) {}
+
+ void visit(Reference* ref) override {
+ if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mDecls,
+ mCallSite)) {
+ mError = true;
}
+ }
- void visit(Reference* ref) override {
- if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mDecls, mCallSite)) {
- mError = true;
- }
- }
+ bool hasError() const { return mError; }
- bool hasError() const {
- return mError;
- }
-
-private:
- IAaptContext* mContext;
- SymbolTable* mSymbols;
- xml::IPackageDeclStack* mDecls;
- CallSite* mCallSite;
- bool mError;
+ private:
+ IAaptContext* mContext;
+ SymbolTable* mSymbols;
+ xml::IPackageDeclStack* mDecls;
+ CallSite* mCallSite;
+ bool mError;
};
/**
* 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>* sdkLevelsFound, CallSite* callSite)
+ : mContext(context),
+ mSymbols(symbols),
+ mSource(source),
+ mSdkLevelsFound(sdkLevelsFound),
+ mCallSite(callSite),
+ mReferenceVisitor(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 = 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();
}
- // Call the super implementation.
- xml::PackageAwareVisitor::visit(el);
+ 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);
+ }
}
- 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 mError || mReferenceVisitor.hasError(); }
+
+ private:
+ IAaptContext* mContext;
+ SymbolTable* mSymbols;
+ Source mSource;
+ std::set<int>* mSdkLevelsFound;
+ CallSite* mCallSite;
+ ReferenceVisitor mReferenceVisitor;
+ bool mError = 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) {
+ 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;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 51eb62c..35d479f 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -20,237 +20,276 @@
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 {
+ 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())
- // 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> mContext;
};
TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.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(mContext.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* viewEl = xml::findRootElement(doc.get());
+ ASSERT_NE(viewEl, 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* 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);
- 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));
+ 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));
- 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.
+ 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.
- xmlAttr = viewEl->findAttribute("", "class");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_FALSE(xmlAttr->compiledAttribute);
- ASSERT_EQ(xmlAttr->compiledValue, nullptr);
+ xmlAttr = viewEl->findAttribute("", "class");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_FALSE(xmlAttr->compiledAttribute);
+ ASSERT_EQ(xmlAttr->compiledValue, 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(mContext.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(mContext.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(mContext.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(mContext.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(mContext.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(mContext.get(), doc.get()));
+ EXPECT_TRUE(linker.getSdkLevels().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(mContext.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(mContext.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* viewEl = xml::findRootElement(doc.get());
+ ASSERT_NE(viewEl, 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* 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);
}
TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.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(mContext.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* viewEl = xml::findRootElement(doc.get());
+ ASSERT_NE(viewEl, 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* 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));
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.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(mContext.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* viewEl = xml::findRootElement(doc.get());
+ ASSERT_NE(viewEl, 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* 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));
- ASSERT_FALSE(viewEl->getChildElements().empty());
- viewEl = viewEl->getChildElements().front();
- ASSERT_NE(viewEl, nullptr);
+ ASSERT_FALSE(viewEl->getChildElements().empty());
+ viewEl = viewEl->getChildElements().front();
+ ASSERT_NE(viewEl, 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).
+ 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));
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.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(mContext.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* viewEl = xml::findRootElement(doc.get());
+ ASSERT_NE(viewEl, 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* 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));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index a7e752a..a9bde39 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -33,21 +33,21 @@
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 verbose() = 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..00ffeb2 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -14,11 +14,11 @@
* limitations under the License.
*/
+#include "process/SymbolTable.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>
@@ -27,254 +27,273 @@
namespace aapt {
void SymbolTable::appendSource(std::unique_ptr<ISymbolSource> source) {
- mSources.push_back(std::move(source));
+ mSources.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));
+ mSources.insert(mSources.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.
+ mCache.clear();
}
const SymbolTable::Symbol* SymbolTable::findByName(const ResourceName& name) {
- if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
- return s.get();
- }
+ if (const std::shared_ptr<Symbol>& s = mCache.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 : 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);
- if (sharedSymbol->id) {
- // The symbol has an ID, so we can also cache this!
- mIdCache.put(sharedSymbol->id.value(), sharedSymbol);
- }
- return sharedSymbol.get();
- }
+ if (sharedSymbol->id) {
+ // The symbol has an ID, so we can also cache this!
+ mIdCache.put(sharedSymbol->id.value(), sharedSymbol);
+ }
+ return sharedSymbol.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();
- }
+ if (const std::shared_ptr<Symbol>& s = mIdCache.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 : 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();
}
- 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());
- }
+ // 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));
- }
+ 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));
+ }
+ 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 {};
+ }
}
-
- 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);
+ int32_t cookie = 0;
+ return mAssets.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->typeMask = 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& 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;
+ }
+
+ android::ResTable::resource_name entryName;
+ if (!table.getResourceName(mapEntry.name.ident, false, &entryName)) {
+ table.unlockBag(entry);
+ return nullptr;
+ }
+
+ Maybe<ResourceName> parsedName = ResourceUtils::toResourceName(entryName);
+ if (!parsedName) {
+ return nullptr;
+ }
+
+ 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));
}
-
- 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;
- }
-
- android::ResTable::resource_name entryName;
- if (!table.getResourceName(mapEntry.name.ident, false, &entryName)) {
- table.unlockBag(entry);
- return nullptr;
- }
-
- Maybe<ResourceName> parsedName = ResourceUtils::toResourceName(entryName);
- if (!parsedName) {
- return nullptr;
- }
-
- 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);
- return s;
+ }
+ table.unlockBag(entry);
+ return s;
}
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByName(
- const ResourceName& name) {
- const android::ResTable& table = mAssets.getResources(false);
+ 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);
+ 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;
- }
+ 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;
+ }
+ return {};
}
-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);
+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 = 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;
+ }
+ 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 {};
+ 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..89d87de 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -34,99 +34,100 @@
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> 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;
}
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), isPublic(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 isPublic = false;
+ };
- SymbolTable() : mCache(200), mIdCache(200) {
- }
+ SymbolTable() : mCache(200), mIdCache(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
+ * are typically 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>> mSources;
- // 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>> mCache;
+ android::LruCache<ResourceId, std::shared_ptr<Symbol>> mIdCache;
- 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 +136,40 @@
* Lookups by ID are ignored.
*/
class ResourceTableSymbolSource : public ISymbolSource {
-public:
- explicit ResourceTableSymbolSource(ResourceTable* table) : mTable(table) {
- }
+ public:
+ explicit ResourceTableSymbolSource(ResourceTable* table) : mTable(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* mTable;
- 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 mAssets;
- 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..00474f7 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -20,34 +20,38 @@
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 symbolSource(table.get());
+ EXPECT_NE(nullptr,
+ symbolSource.findByName(test::parseNameOrDie("android:id/foo")));
+ EXPECT_NE(nullptr,
+ symbolSource.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 =
+ symbolSource.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 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);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/proto/ProtoHelpers.h b/tools/aapt2/proto/ProtoHelpers.h
index 02e67f1..7271e8b 100644
--- a/tools/aapt2/proto/ProtoHelpers.h
+++ b/tools/aapt2/proto/ProtoHelpers.h
@@ -30,14 +30,18 @@
void serializeStringPoolToPb(const StringPool& pool, pb::StringPool* outPbPool);
-void serializeSourceToPb(const Source& source, StringPool* srcPool, pb::Source* outPbSource);
-void deserializeSourceFromPb(const pb::Source& pbSource, const android::ResStringPool& srcPool,
+void serializeSourceToPb(const Source& source, StringPool* srcPool,
+ pb::Source* outPbSource);
+void deserializeSourceFromPb(const pb::Source& pbSource,
+ const android::ResStringPool& srcPool,
Source* outSource);
pb::SymbolStatus_Visibility serializeVisibilityToPb(SymbolState state);
-SymbolState deserializeVisibilityFromPb(pb::SymbolStatus_Visibility pbVisibility);
+SymbolState deserializeVisibilityFromPb(
+ pb::SymbolStatus_Visibility pbVisibility);
-void serializeConfig(const ConfigDescription& config, pb::ConfigDescription* outPbConfig);
+void serializeConfig(const ConfigDescription& config,
+ pb::ConfigDescription* outPbConfig);
bool deserializeConfigDescriptionFromPb(const pb::ConfigDescription& pbConfig,
ConfigDescription* outConfig);
@@ -47,6 +51,6 @@
pb::Plural_Arity serializePluralEnumToPb(size_t pluralIdx);
size_t deserializePluralEnumFromPb(pb::Plural_Arity arity);
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_PROTO_PROTOHELPERS_H */
diff --git a/tools/aapt2/proto/ProtoSerialize.h b/tools/aapt2/proto/ProtoSerialize.h
index cc7c251..378dafd 100644
--- a/tools/aapt2/proto/ProtoSerialize.h
+++ b/tools/aapt2/proto/ProtoSerialize.h
@@ -29,49 +29,49 @@
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 mOut;
};
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 mIn;
};
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<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/split/TableSplitter.h b/tools/aapt2/split/TableSplitter.h
index 2fa5c47..d7ecc82 100644
--- a/tools/aapt2/split/TableSplitter.h
+++ b/tools/aapt2/split/TableSplitter.h
@@ -29,50 +29,49 @@
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> preferredDensity;
- /**
- * 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* configFilter = 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)
+ : mSplitConstraints(splits),
+ mPreferredDensity(options.preferredDensity),
+ mConfigFilter(options.configFilter) {
+ for (size_t i = 0; i < mSplitConstraints.size(); i++) {
+ mSplits.push_back(util::make_unique<ResourceTable>());
}
+ }
- bool verifySplitConstraints(IAaptContext* context);
+ bool verifySplitConstraints(IAaptContext* context);
- void splitTable(ResourceTable* originalTable);
+ void splitTable(ResourceTable* originalTable);
- std::vector<std::unique_ptr<ResourceTable>>& getSplits() {
- return mSplits;
- }
+ std::vector<std::unique_ptr<ResourceTable>>& getSplits() { return mSplits; }
-private:
- std::vector<SplitConstraints> mSplitConstraints;
- std::vector<std::unique_ptr<ResourceTable>> mSplits;
- Maybe<uint16_t> mPreferredDensity;
- IConfigFilter* mConfigFilter;
+ private:
+ std::vector<SplitConstraints> mSplitConstraints;
+ std::vector<std::unique_ptr<ResourceTable>> mSplits;
+ Maybe<uint16_t> mPreferredDensity;
+ IConfigFilter* mConfigFilter;
- DISALLOW_COPY_AND_ASSIGN(TableSplitter);
+ DISALLOW_COPY_AND_ASSIGN(TableSplitter);
};
-
}
#endif /* AAPT_SPLIT_TABLESPLITTER_H */
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 637e991..c647159 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -29,232 +29,242 @@
namespace test {
class ResourceTableBuilder {
-private:
- DummyDiagnosticsImpl mDiagnostics;
- std::unique_ptr<ResourceTable> mTable = util::make_unique<ResourceTable>();
+ private:
+ DummyDiagnosticsImpl mDiagnostics;
+ std::unique_ptr<ResourceTable> mTable = util::make_unique<ResourceTable>();
-public:
- ResourceTableBuilder() = default;
+ public:
+ ResourceTableBuilder() = default;
- StringPool* getStringPool() {
- return &mTable->stringPool;
- }
+ StringPool* getStringPool() { return &mTable->stringPool; }
- ResourceTableBuilder& setPackageId(const StringPiece& packageName, uint8_t id) {
- ResourceTablePackage* package = mTable->createPackage(packageName, id);
- assert(package);
- 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>(mTable->stringPool.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>(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>(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>(mTable->stringPool.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>(mTable->stringPool.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 resName = parseNameOrDie(name);
- bool result = mTable->addResourceAllowMangled(resName, id, config, {},
- std::move(value), &mDiagnostics);
- assert(result);
- 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 resName = parseNameOrDie(name);
- Symbol symbol;
- symbol.state = state;
- bool result = mTable->setSymbolStateAllowMangled(resName, id, symbol, &mDiagnostics);
- assert(result);
- 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(mTable);
- }
+ std::unique_ptr<ResourceTable> build() { return std::move(mTable); }
};
-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;
+ private:
+ std::unique_ptr<Value> mValue;
-public:
- template <typename... Args>
- explicit ValueBuilder(Args&&... args) : mValue(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) {
- mValue->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) {
- mValue->setComment(str);
- return *this;
- }
+ ValueBuilder& setComment(const StringPiece& str) {
+ mValue->setComment(str);
+ return *this;
+ }
- std::unique_ptr<Value> build() {
- return std::move(mValue);
- }
+ std::unique_ptr<Value> build() { return std::move(mValue); }
};
class AttributeBuilder {
-private:
- std::unique_ptr<Attribute> mAttr;
+ private:
+ std::unique_ptr<Attribute> mAttr;
-public:
- explicit AttributeBuilder(bool weak = false) : mAttr(util::make_unique<Attribute>(weak)) {
- mAttr->typeMask = 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) {
- mAttr->typeMask = typeMask;
- return *this;
- }
+ AttributeBuilder& setTypeMask(uint32_t typeMask) {
+ mAttr->typeMask = typeMask;
+ return *this;
+ }
- AttributeBuilder& addItem(const StringPiece& name, uint32_t value) {
- mAttr->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(mAttr);
- }
+ std::unique_ptr<Attribute> build() { return std::move(mAttr); }
};
class StyleBuilder {
-private:
- std::unique_ptr<Style> mStyle = util::make_unique<Style>();
+ private:
+ std::unique_ptr<Style> mStyle = util::make_unique<Style>();
-public:
- StyleBuilder& setParent(const StringPiece& str) {
- mStyle->parent = Reference(parseNameOrDie(str));
- return *this;
- }
+ public:
+ StyleBuilder& setParent(const StringPiece& str) {
+ mStyle->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) {
+ mStyle->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));
+ mStyle->entries.back().key.id = id;
+ return *this;
+ }
- std::unique_ptr<Style> build() {
- return std::move(mStyle);
- }
+ std::unique_ptr<Style> build() { return std::move(mStyle); }
};
class StyleableBuilder {
-private:
- std::unique_ptr<Styleable> mStyleable = util::make_unique<Styleable>();
+ private:
+ std::unique_ptr<Styleable> mStyleable = util::make_unique<Styleable>();
-public:
- StyleableBuilder& addItem(const StringPiece& str, const Maybe<ResourceId>& id = {}) {
- mStyleable->entries.push_back(Reference(parseNameOrDie(str)));
- mStyleable->entries.back().id = id;
- return *this;
- }
+ public:
+ StyleableBuilder& addItem(const StringPiece& str,
+ const Maybe<ResourceId>& id = {}) {
+ mStyleable->entries.push_back(Reference(parseNameOrDie(str)));
+ mStyleable->entries.back().id = id;
+ return *this;
+ }
- std::unique_ptr<Styleable> build() {
- return std::move(mStyleable);
- }
+ std::unique_ptr<Styleable> build() { return std::move(mStyleable); }
};
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;
+ 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> 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..2d571e7 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -30,7 +30,8 @@
#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 +42,83 @@
namespace test {
struct DummyDiagnosticsImpl : public IDiagnostics {
- void log(Level level, DiagMessageActual& actualMsg) override {
- switch (level) {
- case Level::Note:
- return;
+ void log(Level level, DiagMessageActual& actualMsg) 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 << actualMsg.source << ": warn: " << actualMsg.message << "."
+ << std::endl;
+ break;
- case Level::Error:
- std::cerr << actualMsg.source << ": error: " << actualMsg.message << "." << std::endl;
- break;
- }
+ case Level::Error:
+ std::cerr << actualMsg.source << ": error: " << actualMsg.message << "."
+ << std::endl;
+ break;
}
+ }
};
inline IDiagnostics* getDiagnostics() {
- static DummyDiagnosticsImpl diag;
- return &diag;
+ 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();
+ ResourceNameRef ref;
+ bool result = ResourceUtils::parseResourceName(str, &ref);
+ assert(result && "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;
+ ConfigDescription config;
+ bool result = ConfigDescription::parse(str, &config);
+ assert(result && "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& 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());
}
- 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& resName,
+ const ConfigDescription& config) {
+ return getValueForConfigAndProduct<T>(table, resName, 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& resName) {
+ return getValueForConfig<T>(table, resName, {});
}
class TestFile : public io::IFile {
-private:
- Source mSource;
+ private:
+ Source mSource;
-public:
- explicit TestFile(const StringPiece& path) : mSource(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 mSource;
- }
+ const Source& getSource() const override { return mSource; }
};
-} // 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..6c7f6f7 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -18,10 +18,10 @@
#define AAPT_TEST_CONTEXT_H
#include "NameMangler.h"
-#include "util/Util.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
#include "test/Common.h"
+#include "util/Util.h"
#include <cassert>
#include <list>
@@ -30,152 +30,143 @@
namespace test {
class Context : public IAaptContext {
-public:
- SymbolTable* getExternalSymbols() override {
- return &mSymbols;
- }
+ public:
+ SymbolTable* getExternalSymbols() override { return &mSymbols; }
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
- const std::string& getCompilationPackage() override {
- assert(mCompilationPackage && "package name not set");
- return mCompilationPackage.value();
- }
+ const std::string& getCompilationPackage() override {
+ assert(mCompilationPackage && "package name not set");
+ return mCompilationPackage.value();
+ }
- uint8_t getPackageId() override {
- assert(mPackageId && "package ID not set");
- return mPackageId.value();
- }
+ uint8_t getPackageId() override {
+ assert(mPackageId && "package ID not set");
+ return mPackageId.value();
+ }
- NameMangler* getNameMangler() override {
- return &mNameMangler;
- }
+ NameMangler* getNameMangler() override { return &mNameMangler; }
- bool verbose() override {
- return false;
- }
+ bool verbose() override { return false; }
- int getMinSdkVersion() override {
- return mMinSdkVersion;
- }
+ int getMinSdkVersion() override { return mMinSdkVersion; }
-private:
- friend class ContextBuilder;
+ private:
+ friend class ContextBuilder;
- Maybe<std::string> mCompilationPackage;
- Maybe<uint8_t> mPackageId;
- StdErrDiagnostics mDiagnostics;
- SymbolTable mSymbols;
- NameMangler mNameMangler = NameMangler({});
- int mMinSdkVersion = 0;
+ Maybe<std::string> mCompilationPackage;
+ Maybe<uint8_t> mPackageId;
+ StdErrDiagnostics mDiagnostics;
+ SymbolTable mSymbols;
+ NameMangler mNameMangler = NameMangler({});
+ int mMinSdkVersion = 0;
};
class ContextBuilder {
-private:
- std::unique_ptr<Context> mContext = std::unique_ptr<Context>(new Context());
+ private:
+ std::unique_ptr<Context> mContext = std::unique_ptr<Context>(new Context());
-public:
- ContextBuilder& setCompilationPackage(const StringPiece& package) {
- mContext->mCompilationPackage = package.toString();
- return *this;
- }
+ public:
+ ContextBuilder& setCompilationPackage(const StringPiece& package) {
+ mContext->mCompilationPackage = package.toString();
+ return *this;
+ }
- ContextBuilder& setPackageId(uint8_t id) {
- mContext->mPackageId = id;
- return *this;
- }
+ ContextBuilder& setPackageId(uint8_t id) {
+ mContext->mPackageId = id;
+ return *this;
+ }
- ContextBuilder& setNameManglerPolicy(const NameManglerPolicy& policy) {
- mContext->mNameMangler = NameMangler(policy);
- return *this;
- }
+ ContextBuilder& setNameManglerPolicy(const NameManglerPolicy& policy) {
+ mContext->mNameMangler = NameMangler(policy);
+ return *this;
+ }
- ContextBuilder& addSymbolSource(std::unique_ptr<ISymbolSource> src) {
- mContext->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 minSdk) {
- mContext->mMinSdkVersion = minSdk;
- return *this;
- }
+ ContextBuilder& setMinSdkVersion(int minSdk) {
+ mContext->mMinSdkVersion = minSdk;
+ return *this;
+ }
- std::unique_ptr<Context> build() {
- return std::move(mContext);
- }
+ std::unique_ptr<Context> build() { return std::move(mContext); }
};
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);
+ mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
+ mSymbolSource->mIdMap[id] = symbol.get();
+ mSymbolSource->mSymbols.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);
+ mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
+ mSymbolSource->mIdMap[id] = symbol.get();
+ mSymbolSource->mSymbols.push_back(std::move(symbol));
+ return *this;
+ }
+
+ std::unique_ptr<ISymbolSource> build() { return std::move(mSymbolSource); }
+
+ private:
+ class StaticSymbolSource : public ISymbolSource {
+ public:
+ StaticSymbolSource() = default;
+
+ 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;
}
- 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 = mIdMap.find(id);
+ if (iter != mIdMap.end()) {
+ return cloneSymbol(iter->second);
+ }
+ return nullptr;
}
- std::unique_ptr<ISymbolSource> build() {
- return std::move(mSymbolSource);
+ 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;
}
-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> mSymbolSource =
+ 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..c9188bf 100644
--- a/tools/aapt2/test/Test.h
+++ b/tools/aapt2/test/Test.h
@@ -24,9 +24,7 @@
#include <gtest/gtest.h>
namespace aapt {
-namespace test {
+namespace test {} // namespace test
+} // namespace aapt
-} // namespace test
-} // namespace aapt
-
-#endif // AAPT_TEST_TEST_H
+#endif // AAPT_TEST_TEST_H
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/unflatten/BinaryResourceParser.h
index 12bc13d..99f2bd4 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.h
+++ b/tools/aapt2/unflatten/BinaryResourceParser.h
@@ -39,80 +39,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 dataLen);
- BinaryResourceParser(const BinaryResourceParser&) = delete; // No copy.
+ 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:
- 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);
+ 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);
- 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& mapEntry, Value* value);
- std::unique_ptr<Array> parseArray(const ResourceNameRef& name, const ConfigDescription& config,
- const android::ResTable_map_entry* map);
+ IAaptContext* mContext;
+ ResourceTable* mTable;
- std::unique_ptr<Plural> parsePlural(const ResourceNameRef& name,
- const ConfigDescription& config,
- const android::ResTable_map_entry* map);
+ const Source mSource;
- /**
- * 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* mData;
+ const size_t mDataLen;
- IAaptContext* mContext;
- ResourceTable* mTable;
+ // The standard value string pool for resource values.
+ android::ResStringPool mValuePool;
- const Source mSource;
+ // The string pool that holds the names of the types defined
+ // in this table.
+ android::ResStringPool mTypePool;
- const void* mData;
- const size_t mDataLen;
+ // The string pool that holds the names of the entries defined
+ // in this table.
+ android::ResStringPool mKeyPool;
- // 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> mIdIndex;
};
-} // namespace aapt
+} // namespace aapt
namespace android {
@@ -121,13 +127,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.h b/tools/aapt2/unflatten/ResChunkPullParser.h
index a51d5bf..24fa63d 100644
--- a/tools/aapt2/unflatten/ResChunkPullParser.h
+++ b/tools/aapt2/unflatten/ResChunkPullParser.h
@@ -37,59 +37,62 @@
* pointing to the data portion of a chunk.
*/
class ResChunkPullParser {
-public:
- enum class Event {
- StartDocument,
- EndDocument,
- BadDocument,
+ public:
+ enum class Event {
+ StartDocument,
+ EndDocument,
+ BadDocument,
- Chunk,
- };
+ Chunk,
+ };
- /**
- * 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;
+ ResChunkPullParser(const ResChunkPullParser&) = delete;
- Event getEvent() const;
- const std::string& getLastError() const;
- const android::ResChunk_header* getChunk() 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:
- Event mEvent;
- const android::ResChunk_header* mData;
- size_t mLen;
- const android::ResChunk_header* mCurrentChunk;
- std::string mLastError;
+ private:
+ Event mEvent;
+ const android::ResChunk_header* mData;
+ size_t mLen;
+ const android::ResChunk_header* mCurrentChunk;
+ std::string mLastError;
};
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);
+ 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);
+ return util::deviceToHost32(chunk->size) -
+ util::deviceToHost16(chunk->headerSize);
}
//
@@ -97,28 +100,27 @@
//
inline bool ResChunkPullParser::isGoodEvent(ResChunkPullParser::Event event) {
- return event != Event::EndDocument && event != Event::BadDocument;
+ return event != Event::EndDocument && event != Event::BadDocument;
}
-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)
+ : mEvent(Event::StartDocument),
+ mData(reinterpret_cast<const android::ResChunk_header*>(data)),
+ mLen(len),
+ mCurrentChunk(nullptr) {}
inline ResChunkPullParser::Event ResChunkPullParser::getEvent() const {
- return mEvent;
+ return mEvent;
}
inline const std::string& ResChunkPullParser::getLastError() const {
- return mLastError;
+ return mLastError;
}
inline const android::ResChunk_header* ResChunkPullParser::getChunk() const {
- return mCurrentChunk;
+ return mCurrentChunk;
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_RES_CHUNK_PULL_PARSER_H
+#endif // AAPT_RES_CHUNK_PULL_PARSER_H
diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h
index 685614f..b273733 100644
--- a/tools/aapt2/util/BigBuffer.h
+++ b/tools/aapt2/util/BigBuffer.h
@@ -32,156 +32,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;
-
- /**
- * 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);
-
- /**
- * 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);
-
- /**
- * 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);
-
- /**
- * Moves the specified BigBuffer into this one. When this method
- * returns, buffer is empty.
- */
- void appendBuffer(BigBuffer&& buffer);
-
- /**
- * Pads the block with 'bytes' bytes of zero values.
- */
- void pad(size_t bytes);
-
- /**
- * Pads the block so that it aligns on a 4 byte boundary.
- */
- void align4();
-
- size_t getBlockSize() const;
-
- const_iterator begin() const;
- const_iterator end() const;
-
-private:
- /**
- * Returns a pointer to a buffer of the requested size.
- * The buffer is zero-initialized.
- */
- void* nextBlockImpl(size_t size);
-
size_t mBlockSize;
- size_t mSize;
- std::vector<Block> mBlocks;
+ };
+
+ typedef std::vector<Block>::const_iterator const_iterator;
+
+ /**
+ * Create a BigBuffer with block allocation sizes
+ * of blockSize.
+ */
+ explicit BigBuffer(size_t blockSize);
+
+ BigBuffer(const BigBuffer&) = delete; // No copying.
+
+ BigBuffer(BigBuffer&& rhs);
+
+ /**
+ * Number of occupied bytes in all the allocated blocks.
+ */
+ size_t size() const;
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * Moves the specified BigBuffer into this one. When this method
+ * returns, buffer is empty.
+ */
+ void appendBuffer(BigBuffer&& buffer);
+
+ /**
+ * Pads the block with 'bytes' bytes of zero values.
+ */
+ void pad(size_t bytes);
+
+ /**
+ * Pads the block so that it aligns on a 4 byte boundary.
+ */
+ void align4();
+
+ size_t getBlockSize() const;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ private:
+ /**
+ * Returns a pointer to a buffer of the requested size.
+ * The buffer is zero-initialized.
+ */
+ void* nextBlockImpl(size_t size);
+
+ size_t mBlockSize;
+ size_t mSize;
+ std::vector<Block> mBlocks;
};
-inline BigBuffer::BigBuffer(size_t blockSize) : mBlockSize(blockSize), mSize(0) {
-}
+inline BigBuffer::BigBuffer(size_t blockSize)
+ : mBlockSize(blockSize), mSize(0) {}
-inline BigBuffer::BigBuffer(BigBuffer&& rhs) :
- mBlockSize(rhs.mBlockSize), mSize(rhs.mSize), mBlocks(std::move(rhs.mBlocks)) {
-}
+inline BigBuffer::BigBuffer(BigBuffer&& rhs)
+ : mBlockSize(rhs.mBlockSize),
+ mSize(rhs.mSize),
+ mBlocks(std::move(rhs.mBlocks)) {}
-inline size_t BigBuffer::size() const {
- return mSize;
-}
+inline size_t BigBuffer::size() const { return mSize; }
-inline size_t BigBuffer::getBlockSize() const {
- return mBlockSize;
-}
+inline size_t BigBuffer::getBlockSize() const { return mBlockSize; }
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));
+ 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 void BigBuffer::backUp(size_t count) {
- Block& block = mBlocks.back();
- block.size -= count;
- mSize -= count;
+ Block& block = mBlocks.back();
+ block.size -= count;
+ mSize -= 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;
+ std::move(buffer.mBlocks.begin(), buffer.mBlocks.end(),
+ std::back_inserter(mBlocks));
+ mSize += buffer.mSize;
+ buffer.mBlocks.clear();
+ buffer.mSize = 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);
- }
+ const size_t unaligned = mSize % 4;
+ if (unaligned != 0) {
+ pad(4 - unaligned);
+ }
}
inline BigBuffer::const_iterator BigBuffer::begin() const {
- return mBlocks.begin();
+ return mBlocks.begin();
}
inline BigBuffer::const_iterator BigBuffer::end() const {
- return mBlocks.end();
+ return mBlocks.end();
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_BIG_BUFFER_H
+#endif // AAPT_BIG_BUFFER_H
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 52c2052..d90c6b6 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -39,15 +39,15 @@
#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);
@@ -93,12 +93,14 @@
/**
* 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* outError);
/**
* Reads the file at path and appends each line to the outArgList vector.
*/
-bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outArgList,
+bool appendArgsFromFile(const StringPiece& path,
+ std::vector<std::string>* outArgList,
std::string* outError);
/*
@@ -108,37 +110,36 @@
* FileFilter::setPattern(const std::string&) method.
*/
class FileFilter {
-public:
- explicit FileFilter(IDiagnostics* diag) : mDiag(diag) {
- }
+ public:
+ explicit FileFilter(IDiagnostics* diag) : mDiag(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:
+ IDiagnostics* mDiag;
+ std::vector<std::string> mPatternTokens;
};
-} // namespace file
-} // namespace aapt
+} // namespace file
+} // namespace aapt
-#endif // AAPT_FILES_H
+#endif // AAPT_FILES_H
diff --git a/tools/aapt2/util/ImmutableMap.h b/tools/aapt2/util/ImmutableMap.h
index b1f9e9d..6f48764 100644
--- a/tools/aapt2/util/ImmutableMap.h
+++ b/tools/aapt2/util/ImmutableMap.h
@@ -26,59 +26,57 @@
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;
+ private:
+ std::vector<std::pair<TKey, TValue>> mData;
- explicit ImmutableMap(std::vector<std::pair<TKey, TValue>> data) : mData(std::move(data)) {
+ explicit ImmutableMap(std::vector<std::pair<TKey, TValue>> data)
+ : mData(std::move(data)) {}
+
+ public:
+ using const_iterator = typename decltype(mData)::const_iterator;
+
+ ImmutableMap(ImmutableMap&&) = default;
+ ImmutableMap& operator=(ImmutableMap&&) = default;
+
+ ImmutableMap(const ImmutableMap&) = delete;
+ ImmutableMap& operator=(const ImmutableMap&) = delete;
+
+ 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 endIter = end();
+ auto iter = std::lower_bound(mData.begin(), endIter, key, cmp);
+ if (iter == endIter || iter->first == key) {
+ return iter;
}
+ return endIter;
+ }
-public:
- using const_iterator = typename decltype(mData)::const_iterator;
+ const_iterator begin() const { return mData.begin(); }
- ImmutableMap(ImmutableMap&&) = default;
- ImmutableMap& operator=(ImmutableMap&&) = default;
-
- ImmutableMap(const ImmutableMap&) = delete;
- ImmutableMap& operator=(const ImmutableMap&) = delete;
-
- 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 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();
- }
+ const_iterator end() const { return mData.end(); }
};
-} // 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..90a0198 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -32,303 +32,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 valueOrDefault(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 mNothing;
- typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
+ typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
};
template <typename T>
-Maybe<T>::Maybe()
-: mNothing(true) {
-}
+Maybe<T>::Maybe() : mNothing(true) {}
template <typename T>
Maybe<T>::~Maybe() {
- if (!mNothing) {
- destroy();
- }
+ if (!mNothing) {
+ 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) : mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ new (&mStorage) T(reinterpret_cast<const T&>(rhs.mStorage));
+ }
}
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) : mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
+ }
}
template <typename T>
-Maybe<T>::Maybe(Maybe&& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- rhs.mNothing = true;
+Maybe<T>::Maybe(Maybe&& rhs) : mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ rhs.mNothing = true;
- // Move the value from rhs.
- new (&mStorage) T(std::move(reinterpret_cast<T&>(rhs.mStorage)));
- rhs.destroy();
- }
+ // Move the value from rhs.
+ new (&mStorage) T(std::move(reinterpret_cast<T&>(rhs.mStorage)));
+ 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) : mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ rhs.mNothing = true;
- // Move the value from rhs.
- new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
- rhs.destroy();
- }
+ // Move the value from rhs.
+ new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
+ 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 (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();
+ }
+ 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 (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();
+ }
+ return *this;
}
template <typename T>
-Maybe<T>::Maybe(const T& value)
-: mNothing(false) {
- new (&mStorage) T(value);
+Maybe<T>::Maybe(const T& value) : mNothing(false) {
+ new (&mStorage) T(value);
}
template <typename T>
-Maybe<T>::Maybe(T&& value)
-: mNothing(false) {
- new (&mStorage) T(std::forward<T>(value));
+Maybe<T>::Maybe(T&& value) : mNothing(false) {
+ new (&mStorage) T(std::forward<T>(value));
}
template <typename T>
Maybe<T>::operator bool() const {
- return !mNothing;
+ return !mNothing;
}
template <typename T>
T& Maybe<T>::value() {
- assert(!mNothing && "Maybe<T>::value() called on Nothing");
- return reinterpret_cast<T&>(mStorage);
+ assert(!mNothing && "Maybe<T>::value() called on Nothing");
+ return reinterpret_cast<T&>(mStorage);
}
template <typename T>
const T& Maybe<T>::value() const {
- assert(!mNothing && "Maybe<T>::value() called on Nothing");
- return reinterpret_cast<const T&>(mStorage);
+ assert(!mNothing && "Maybe<T>::value() called on Nothing");
+ return reinterpret_cast<const T&>(mStorage);
}
template <typename T>
T Maybe<T>::valueOrDefault(const T& def) const {
- if (mNothing) {
- return def;
- }
- return reinterpret_cast<const T&>(mStorage);
+ if (mNothing) {
+ return def;
+ }
+ return reinterpret_cast<const T&>(mStorage);
}
template <typename T>
void Maybe<T>::destroy() {
- reinterpret_cast<T&>(mStorage).~T();
+ reinterpret_cast<T&>(mStorage).~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/StringPiece.h b/tools/aapt2/util/StringPiece.h
index 266c003..de93822 100644
--- a/tools/aapt2/util/StringPiece.h
+++ b/tools/aapt2/util/StringPiece.h
@@ -17,11 +17,11 @@
#ifndef AAPT_STRING_PIECE_H
#define AAPT_STRING_PIECE_H
-#include <ostream>
-#include <string>
#include <utils/JenkinsHash.h>
#include <utils/String8.h>
#include <utils/Unicode.h>
+#include <ostream>
+#include <string>
namespace aapt {
@@ -35,45 +35,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* mData;
+ size_t mLength;
};
using StringPiece = BasicStringPiece<char>;
@@ -87,198 +88,210 @@
constexpr const size_t BasicStringPiece<TChar>::npos;
template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece() : mData(nullptr) , mLength(0) {
-}
+inline BasicStringPiece<TChar>::BasicStringPiece()
+ : mData(nullptr), mLength(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)
+ : mData(str.mData), mLength(str.mLength) {}
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)
+ : mData(str.data()), mLength(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)
+ : mData(str), mLength(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)
+ : mData(str), mLength(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)
+ : mData(str), mLength(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) {
+ mData = rhs.mData;
+ mLength = rhs.mLength;
+ 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) {
+ mData = str;
+ mLength = 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 = mLength - start;
+ }
+
+ if (start > mLength || start + len > mLength) {
+ return BasicStringPiece<TChar>();
+ }
+ return BasicStringPiece<TChar>(mData + 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 mData;
}
template <typename TChar>
inline size_t BasicStringPiece<TChar>::length() const {
- return mLength;
+ return mLength;
}
template <typename TChar>
inline size_t BasicStringPiece<TChar>::size() const {
- return mLength;
+ return mLength;
}
template <typename TChar>
inline bool BasicStringPiece<TChar>::empty() const {
- return mLength == 0;
+ return mLength == 0;
}
template <typename TChar>
inline std::basic_string<TChar> BasicStringPiece<TChar>::toString() const {
- return std::basic_string<TChar>(mData, mLength);
+ return std::basic_string<TChar>(mData, mLength);
}
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 (!mData || !rhs.mData) {
+ return false;
+ }
+ if (rhs.mLength > mLength) {
+ return false;
+ }
+ return strstr(mData, rhs.mData) != 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 = mData != nullptr ? mData : &nullStr;
+ const char* e1 = b1 + mLength;
+ const char* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
+ const char* e2 = b2 + rhs.mLength;
- 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>(mLength - rhs.mLength);
}
-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 (!mData || !rhs.mData) {
+ return false;
+ }
+ if (rhs.mLength > mLength) {
+ return false;
+ }
+ return strstr16(mData, rhs.mData) != 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 = mData != nullptr ? mData : &nullStr;
+ const char16_t* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
+ return strzcmp16(b1, mLength, b2, rhs.mLength);
}
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 mData;
}
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 mData + mLength;
}
-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/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.h b/tools/aapt2/util/Util.h
index 9c88354..077e193 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -56,16 +56,14 @@
* 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& allowedChars);
/**
* Tests that the string is a valid Java class name.
@@ -78,7 +76,8 @@
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
@@ -89,111 +88,114 @@
const StringPiece& className);
/**
- * 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;
- };
+::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;
- };
+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;
+ };
}
/**
- * 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);
/**
- * 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);
/**
- * 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);
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& str() 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;
+ 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 mStr;
+ size_t mUtf16Len = 0;
+ bool mQuote = false;
+ bool mTrailingSpace = false;
+ bool mLastCharWasEscape = false;
+ std::string mError;
};
-inline const std::string& StringBuilder::str() const {
- return mStr;
-}
+inline const std::string& StringBuilder::str() const { return mStr; }
-inline const std::string& StringBuilder::error() const {
- return mError;
-}
+inline const std::string& StringBuilder::error() const { return mError; }
-inline size_t StringBuilder::utf16Len() const {
- return mUtf16Len;
-}
+inline size_t StringBuilder::utf16Len() const { return mUtf16Len; }
-inline StringBuilder::operator bool() const {
- return mError.empty();
-}
+inline StringBuilder::operator bool() const { return mError.empty(); }
/**
* Converts a UTF8 string to a UTF16 string.
@@ -216,65 +218,51 @@
* 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 mToken; }
+ 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 mStr;
+ char mSeparator;
+ StringPiece mToken;
+ bool mEnd;
+ };
- Tokenizer(StringPiece str, char sep);
+ Tokenizer(StringPiece str, char sep);
- iterator begin() {
- return mBegin;
- }
+ iterator begin() { return mBegin; }
- iterator end() {
- return mEnd;
- }
+ iterator end() { return mEnd; }
-private:
- const iterator mBegin;
- const iterator mEnd;
+ private:
+ const iterator mBegin;
+ const iterator mEnd;
};
inline Tokenizer tokenize(const StringPiece& str, char sep) {
- return Tokenizer(str, 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
@@ -288,17 +276,19 @@
bool extractResFilePathParts(const StringPiece& path, StringPiece* outPrefix,
StringPiece* outEntry, StringPiece* outSuffix);
-} // 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/xml/XmlActionExecutor.h b/tools/aapt2/xml/XmlActionExecutor.h
index cad508c..ca21b08 100644
--- a/tools/aapt2/xml/XmlActionExecutor.h
+++ b/tools/aapt2/xml/XmlActionExecutor.h
@@ -30,79 +30,84 @@
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.
+ */
+ None,
- /**
- * 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.
+ */
+ Whitelist,
};
/**
- * 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 mMap[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> mMap;
+ std::vector<ActionFuncWithDiag> mActions;
};
/**
- * 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 mMap[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> mMap;
- 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/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index e4f41b0..932303e 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -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 lineNumber = 0;
+ size_t columnNumber = 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,173 @@
*/
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 namespacePrefix;
+ std::string namespaceUri;
- 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 namespaceUri;
+ std::string name;
+ std::string value;
- Maybe<AaptAttribute> compiledAttribute;
- std::unique_ptr<Item> compiledValue;
+ Maybe<AaptAttribute> compiledAttribute;
+ std::unique_ptr<Item> compiledValue;
};
/**
* An Element XML node.
*/
class Element : public BaseNode<Element> {
-public:
- std::string namespaceUri;
- std::string name;
- std::vector<Attribute> attributes;
+ public:
+ std::string namespaceUri;
+ 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& attrNs,
+ const StringPiece& attrName,
+ const StringPiece& attrValue);
+ 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 dataLen,
+ IDiagnostics* diag, const Source& source);
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& localPackage) 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> mPackageDecls;
};
// Implementations
template <typename Derived>
void BaseNode<Derived>::accept(RawVisitor* visitor) {
- visitor->visit(static_cast<Derived*>(this));
+ 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;
+ 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/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index a24d109..ce69df6 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -23,8 +23,8 @@
#include "util/StringPiece.h"
#include "xml/XmlUtil.h"
-#include <algorithm>
#include <expat.h>
+#include <algorithm>
#include <istream>
#include <ostream>
#include <queue>
@@ -36,263 +36,288 @@
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 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);
- 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 getEvent() const;
- const std::string& getLastError() const;
+ const std::string& getLastError() 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& getComment() const;
+ size_t getLineNumber() const;
+ size_t getDepth() 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& getText() 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& getNamespacePrefix() const;
+ const std::string& getNamespaceUri() 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& getElementNamespace() const;
+ const std::string& getElementName() 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& localPackage) 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 namespaceUri;
+ 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 beginAttributes() const;
+ const_iterator endAttributes() const;
+ size_t getAttributeCount() const;
+ const_iterator findAttribute(StringPiece namespaceUri,
+ 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:
+ 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);
- struct EventData {
- Event event;
- size_t lineNumber;
- size_t depth;
- std::string data1;
- std::string data2;
- std::vector<Attribute> attributes;
- };
+ struct EventData {
+ Event event;
+ size_t lineNumber;
+ size_t depth;
+ std::string data1;
+ std::string data2;
+ std::vector<Attribute> attributes;
+ };
- 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;
+ 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 PackageDecl {
- std::string prefix;
- ExtractedPackage package;
- };
- std::vector<PackageDecl> mPackageAliases;
+ struct PackageDecl {
+ std::string prefix;
+ ExtractedPackage package;
+ };
+ std::vector<PackageDecl> mPackageAliases;
};
/**
* 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) {
- 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 ::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 startDepth) {
- Event event;
+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) {}
+ // 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();
+ // 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;
}
- return false;
+ event = parser->next();
+ }
+ return false;
}
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;
- }
+ 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;
+ }
+ return true;
}
inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) {
- return event != Event::kBadDocument && event != Event::kEndDocument;
+ 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 = namespaceUri.compare(rhs.namespaceUri);
+ 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 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;
+ });
- if (iter != endIter && namespaceUri == iter->namespaceUri && name == iter->name) {
- return iter;
- }
- return endIter;
+ if (iter != endIter && namespaceUri == iter->namespaceUri &&
+ name == iter->name) {
+ return iter;
+ }
+ return endIter;
}
-} // 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/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index a6ad79d..96de654 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -26,9 +26,12 @@
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 privateNamespace;
};
/**
@@ -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& namespaceUri);
/**
* Returns an XML Android namespace for the given package of the form:
@@ -68,31 +74,37 @@
*
* http://schemas.android.com/apk/prv/res/<package>
*/
-std::string buildPackageNamespace(const StringPiece& package, bool privateReference=false);
+std::string buildPackageNamespace(const StringPiece& package,
+ bool privateReference = 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& localPackage) 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
+ * 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);
+ const StringPiece& localPackage,
+ Reference* inRef);
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
#endif /* AAPT_XML_XMLUTIL_H */