diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index df3ce19b05e54..04fdae9f2db4b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -106,7 +106,7 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; -import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; +import static android.os._Original_Build.VERSION_CODES.JELLY_BEAN_MR1; import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE; /** diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java index 5386b17586824..7f8d9928d7ce5 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java @@ -16,13 +16,13 @@ package com.android.layoutlib.bridge.bars; -import android.os.Build.VERSION_CODES; +import android.os._Original_Build.VERSION_CODES; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static android.os.Build.VERSION_CODES.*; +import static android.os._Original_Build.VERSION_CODES.*; /** * Various helper methods to simulate older versions of platform. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index a6e5fb841bff8..8bb2c593f7a19 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -49,7 +49,7 @@ import android.widget.TextView; import java.io.IOException; import java.io.InputStream; -import static android.os.Build.VERSION_CODES.LOLLIPOP; +import static android.os._Original_Build.VERSION_CODES.LOLLIPOP; /** * Base "bar" class for the window decor around the the edited layout. diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java index bed5806aadad2..d59b41980179b 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java @@ -37,6 +37,7 @@ import java.util.TreeMap; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Class that generates a new JAR from a list of classes, some of which are to be kept as-is @@ -127,7 +128,10 @@ public class AsmGenerator { // Create the map of classes to rename. mRenameClasses = new HashMap<>(); mClassesNotRenamed = new HashSet<>(); - String[] renameClasses = createInfo.getRenamedClasses(); + String[] renameClasses = Stream.concat( + Arrays.stream(createInfo.getRenamedClasses()), + Arrays.stream(createInfo.getRefactoredClasses())) + .toArray(String[]::new); int n = renameClasses.length; for (int i = 0; i < n; i += 2) { assert i + 1 < n; @@ -140,7 +144,10 @@ public class AsmGenerator { // Create a map of classes to be refactored. mRefactorClasses = new HashMap<>(); - String[] refactorClasses = createInfo.getJavaPkgClasses(); + String[] refactorClasses = Stream.concat( + Arrays.stream(createInfo.getJavaPkgClasses()), + Arrays.stream(createInfo.getRefactoredClasses())) + .toArray(String[]::new); n = refactorClasses.length; for (int i = 0; i < n; i += 2) { assert i + 1 < n; diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index a8582c60bc64b..b0aa3c2989a59 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -36,65 +36,41 @@ import java.util.Set; */ public final class CreateInfo implements ICreateInfo { - /** - * Returns the list of class from layoutlib_create to inject in layoutlib. - * The list can be empty but must not be null. - */ @Override public Class[] getInjectedClasses() { return INJECTED_CLASSES; } - /** - * Returns the list of methods to rewrite as delegates. - * The list can be empty but must not be null. - */ @Override public String[] getDelegateMethods() { return DELEGATE_METHODS; } - /** - * Returns the list of classes on which to delegate all native methods. - * The list can be empty but must not be null. - */ @Override public String[] getDelegateClassNatives() { return DELEGATE_CLASS_NATIVES; } - /** - * Returns the list of classes to rename, must be an even list: the binary FQCN - * of class to replace followed by the new FQCN. - * The list can be empty but must not be null. - */ @Override public String[] getRenamedClasses() { return RENAMED_CLASSES; } - /** - * Returns the list of classes for which the methods returning them should be deleted. - * The array contains a list of null terminated section starting with the name of the class - * to rename in which the methods are deleted, followed by a list of return types identifying - * the methods to delete. - * The list can be empty but must not be null. - */ @Override public String[] getDeleteReturns() { return DELETE_RETURNS; } - /** - * Returns the list of classes to refactor, must be an even list: the binary FQCN of class to - * replace followed by the new FQCN. All references to the old class should be updated to the - * new class. The list can be empty but must not be null. - */ @Override public String[] getJavaPkgClasses() { return JAVA_PKG_CLASSES; } + @Override + public String[] getRefactoredClasses() { + return REFACTOR_CLASSES; + } + @Override public Set getExcludedClasses() { String[] refactoredClasses = getJavaPkgClasses(); @@ -333,10 +309,22 @@ public final class CreateInfo implements ICreateInfo { "java.text.SimpleDateFormat", "android.icu.text.SimpleDateFormat", }; + /** + * List of classes to refactor. This is similar to combining {@link #getRenamedClasses()} and + * {@link #getJavaPkgClasses()}. + * Classes included here will be renamed and then all their references in any other classes + * will be also modified. + * FQCN of class to refactor followed by its new FQCN. + */ + private final static String[] REFACTOR_CLASSES = + new String[] { + "android.os.Build", "android.os._Original_Build", + }; + private final static String[] EXCLUDED_CLASSES = new String[] { "android.preference.PreferenceActivity", - "org.kxml2.io.KXmlParser" + "org.kxml2.io.KXmlParser", }; /** diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java index 48abde4517e6a..eca1c078faec8 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java @@ -51,6 +51,15 @@ public interface ICreateInfo { */ String[] getRenamedClasses(); + /** + * List of classes to refactor. This is similar to combining {@link #getRenamedClasses()} and + * {@link #getJavaPkgClasses()}. + * Classes included here will be renamed and then all their references in any other classes + * will be also modified. + * FQCN of class to refactor followed by its new FQCN. + */ + String[] getRefactoredClasses(); + /** * Returns the list of classes for which the methods returning them should be deleted. * The array contains a list of null terminated section starting with the name of the class diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java index 4d5d5d2c4a6e9..e718fb9133ee5 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java @@ -35,6 +35,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; @@ -54,8 +55,6 @@ import static org.junit.Assert.assertTrue; * Unit tests for some methods of {@link AsmGenerator}. */ public class AsmGeneratorTest { - - private static final String[] EMPTY_STRING_ARRAY = new String[0]; private MockLog mLog; private ArrayList mOsJarPath; private String mOsDestJar; @@ -81,6 +80,7 @@ public class AsmGeneratorTest { @After public void tearDown() throws Exception { if (mTempFile != null) { + //noinspection ResultOfMethodCallIgnored mTempFile.delete(); mTempFile = null; } @@ -89,23 +89,7 @@ public class AsmGeneratorTest { @Test public void testClassRenaming() throws IOException, LogAbortException { - ICreateInfo ci = new ICreateInfo() { - @Override - public Class[] getInjectedClasses() { - // classes to inject in the final JAR - return new Class[0]; - } - - @Override - public String[] getDelegateMethods() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getDelegateClassNatives() { - return EMPTY_STRING_ARRAY; - } - + ICreateInfo ci = new CreateInfoAdapter() { @Override public String[] getRenamedClasses() { // classes to rename (so that we can replace them) @@ -114,37 +98,6 @@ public class AsmGeneratorTest { "not.an.actual.ClassName", "anoter.fake.NewClassName", }; } - - @Override - public String[] getJavaPkgClasses() { - return EMPTY_STRING_ARRAY; - } - - @Override - public Set getExcludedClasses() { - return null; - } - - @Override - public String[] getDeleteReturns() { - // methods deleted from their return type. - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedFields() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedClasses() { - return EMPTY_STRING_ARRAY; - } - - @Override - public Map getInjectedMethodsMap() { - return Collections.emptyMap(); - } }; AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci); @@ -154,7 +107,7 @@ public class AsmGeneratorTest { new String[] { // include classes "**" }, - Collections.emptySet() /* excluded classes */, + Collections.emptySet() /* excluded classes */, new String[]{} /* include files */); aa.analyze(); agen.generate(); @@ -165,8 +118,8 @@ public class AsmGeneratorTest { } @Test - public void testClassRefactoring() throws IOException, LogAbortException { - ICreateInfo ci = new ICreateInfo() { + public void testJavaClassRefactoring() throws IOException, LogAbortException { + ICreateInfo ci = new CreateInfoAdapter() { @Override public Class[] getInjectedClasses() { // classes to inject in the final JAR @@ -175,22 +128,6 @@ public class AsmGeneratorTest { }; } - @Override - public String[] getDelegateMethods() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getDelegateClassNatives() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getRenamedClasses() { - // classes to rename (so that we can replace them) - return EMPTY_STRING_ARRAY; - } - @Override public String[] getJavaPkgClasses() { // classes to refactor (so that we can replace them) @@ -203,27 +140,6 @@ public class AsmGeneratorTest { public Set getExcludedClasses() { return Collections.singleton("java.lang.JavaClass"); } - - @Override - public String[] getDeleteReturns() { - // methods deleted from their return type. - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedFields() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedClasses() { - return EMPTY_STRING_ARRAY; - } - - @Override - public Map getInjectedMethodsMap() { - return Collections.emptyMap(); - } }; AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci); @@ -233,7 +149,7 @@ public class AsmGeneratorTest { new String[] { // include classes "**" }, - Collections.emptySet(), + Collections.emptySet(), new String[] { /* include files */ "mock_android/data/data*" }); @@ -242,47 +158,64 @@ public class AsmGeneratorTest { Map output = new TreeMap<>(); Map filesFound = new TreeMap<>(); parseZip(mOsDestJar, output, filesFound); - boolean injectedClassFound = false; + RecordingClassVisitor cv = new RecordingClassVisitor(); for (ClassReader cr: output.values()) { - TestClassVisitor cv = new TestClassVisitor(); cr.accept(cv, 0); - injectedClassFound |= cv.mInjectedClassFound; } - assertTrue(injectedClassFound); + assertTrue(cv.mVisitedClasses.contains( + "com/android/tools/layoutlib/create/dataclass/JavaClass")); + assertFalse(cv.mVisitedClasses.contains( + JAVA_CLASS_NAME)); assertArrayEquals(new String[] {"mock_android/data/dataFile"}, filesFound.keySet().toArray()); } @Test - public void testClassExclusion() throws IOException, LogAbortException { - ICreateInfo ci = new ICreateInfo() { + public void testClassRefactoring() throws IOException, LogAbortException { + ICreateInfo ci = new CreateInfoAdapter() { @Override public Class[] getInjectedClasses() { - return new Class[0]; + // classes to inject in the final JAR + return new Class[] { + com.android.tools.layoutlib.create.dataclass.JavaClass.class + }; } @Override - public String[] getDelegateMethods() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getDelegateClassNatives() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getRenamedClasses() { - // classes to rename (so that we can replace them) - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getJavaPkgClasses() { + public String[] getRefactoredClasses() { // classes to refactor (so that we can replace them) - return EMPTY_STRING_ARRAY; + return new String[] { + "mock_android.view.View", "mock_android.view._Original_View", + }; } + }; + AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci); + + AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen, + null, // derived from + new String[] { // include classes + "**" + }, + Collections.emptySet(), + new String[] {}); + aa.analyze(); + agen.generate(); + Map output = new TreeMap<>(); + parseZip(mOsDestJar, output, new TreeMap<>()); + RecordingClassVisitor cv = new RecordingClassVisitor(); + for (ClassReader cr: output.values()) { + cr.accept(cv, 0); + } + assertTrue(cv.mVisitedClasses.contains( + "mock_android/view/_Original_View")); + assertFalse(cv.mVisitedClasses.contains( + "mock_android/view/View")); + } + + @Test + public void testClassExclusion() throws IOException, LogAbortException { + ICreateInfo ci = new CreateInfoAdapter() { @Override public Set getExcludedClasses() { Set set = new HashSet<>(2); @@ -290,27 +223,6 @@ public class AsmGeneratorTest { set.add("java.lang.JavaClass"); return set; } - - @Override - public String[] getDeleteReturns() { - // methods deleted from their return type. - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedFields() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedClasses() { - return EMPTY_STRING_ARRAY; - } - - @Override - public Map getInjectedMethodsMap() { - return Collections.emptyMap(); - } }; AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci); @@ -340,55 +252,7 @@ public class AsmGeneratorTest { public void testMethodInjection() throws IOException, LogAbortException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { - ICreateInfo ci = new ICreateInfo() { - @Override - public Class[] getInjectedClasses() { - return new Class[0]; - } - - @Override - public String[] getDelegateMethods() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getDelegateClassNatives() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getRenamedClasses() { - // classes to rename (so that we can replace them) - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getJavaPkgClasses() { - // classes to refactor (so that we can replace them) - return EMPTY_STRING_ARRAY; - } - - @Override - public Set getExcludedClasses() { - return Collections.emptySet(); - } - - @Override - public String[] getDeleteReturns() { - // methods deleted from their return type. - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedFields() { - return EMPTY_STRING_ARRAY; - } - - @Override - public String[] getPromotedClasses() { - return EMPTY_STRING_ARRAY; - } - + ICreateInfo ci = new CreateInfoAdapter() { @Override public Map getInjectedMethodsMap() { return Collections.singletonMap("mock_android.util.EmptyArray", @@ -474,97 +338,94 @@ public class AsmGeneratorTest { } } - private class TestClassVisitor extends ClassVisitor { + /** + * {@link ClassVisitor} that records every class that sees. + */ + private static class RecordingClassVisitor extends ClassVisitor { + private Set mVisitedClasses = new HashSet<>(); - boolean mInjectedClassFound = false; - - TestClassVisitor() { + private RecordingClassVisitor() { super(Main.ASM_VERSION); } - @Override - public void visit(int version, int access, String name, String signature, - String superName, String[] interfaces) { - assertTrue(!getBase(name).equals(JAVA_CLASS_NAME)); - if (name.equals("com/android/tools/layoutlib/create/dataclass/JavaClass")) { - mInjectedClassFound = true; + private void addClass(String className) { + if (className == null) { + return; } - super.visit(version, access, name, signature, superName, interfaces); + + int pos = className.indexOf('$'); + if (pos > 0) { + // For inner classes, add also the base class + mVisitedClasses.add(className.substring(0, pos)); + } + mVisitedClasses.add(className); } @Override - public FieldVisitor visitField(int access, String name, String desc, - String signature, Object value) { - assertTrue(testType(Type.getType(desc))); + public void visit(int version, int access, String name, String signature, String superName, + String[] interfaces) { + addClass(superName); + Arrays.stream(interfaces).forEach(this::addClass); + } + + private void processType(Type type) { + switch (type.getSort()) { + case Type.OBJECT: + addClass(type.getInternalName()); + break; + case Type.ARRAY: + addClass(type.getElementType().getInternalName()); + break; + case Type.METHOD: + processType(type.getReturnType()); + Arrays.stream(type.getArgumentTypes()).forEach(this::processType); + break; + } + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, + Object value) { + processType(Type.getType(desc)); return super.visitField(access, name, desc, signature, value); } - @SuppressWarnings("hiding") @Override - public MethodVisitor visitMethod(int access, String name, String desc, - String signature, String[] exceptions) { + public MethodVisitor visitMethod(int access, String name, String desc, String signature, + String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); return new MethodVisitor(Main.ASM_VERSION, mv) { @Override - public void visitFieldInsn(int opcode, String owner, String name, - String desc) { - assertTrue(!getBase(owner).equals(JAVA_CLASS_NAME)); - assertTrue(testType(Type.getType(desc))); + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + addClass(owner); + processType(Type.getType(desc)); super.visitFieldInsn(opcode, owner, name, desc); } @Override public void visitLdcInsn(Object cst) { if (cst instanceof Type) { - assertTrue(testType((Type)cst)); + processType((Type) cst); } super.visitLdcInsn(cst); } @Override public void visitTypeInsn(int opcode, String type) { - assertTrue(!getBase(type).equals(JAVA_CLASS_NAME)); + addClass(type); super.visitTypeInsn(opcode, type); } @Override - public void visitMethodInsn(int opcode, String owner, String name, - String desc, boolean itf) { - assertTrue(!getBase(owner).equals(JAVA_CLASS_NAME)); - assertTrue(testType(Type.getType(desc))); + public void visitMethodInsn(int opcode, String owner, String name, String desc, + boolean itf) { + addClass(owner); + processType(Type.getType(desc)); super.visitMethodInsn(opcode, owner, name, desc, itf); } }; } - - private boolean testType(Type type) { - int sort = type.getSort(); - if (sort == Type.OBJECT) { - assertTrue(!getBase(type.getInternalName()).equals(JAVA_CLASS_NAME)); - } else if (sort == Type.ARRAY) { - assertTrue(!getBase(type.getElementType().getInternalName()) - .equals(JAVA_CLASS_NAME)); - } else if (sort == Type.METHOD) { - boolean r = true; - for (Type t : type.getArgumentTypes()) { - r &= testType(t); - } - return r & testType(type.getReturnType()); - } - return true; - } - - private String getBase(String className) { - if (className == null) { - return null; - } - int pos = className.indexOf('$'); - if (pos > 0) { - return className.substring(0, pos); - } - return className; - } } } diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java new file mode 100644 index 0000000000000..ad7cb9a0ed401 --- /dev/null +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 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.tools.layoutlib.create; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +class CreateInfoAdapter implements ICreateInfo { + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + + @Override + public Class[] getInjectedClasses() { + return new Class[0]; + } + + @Override + public String[] getDelegateMethods() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getDelegateClassNatives() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getRenamedClasses() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getRefactoredClasses() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getDeleteReturns() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getJavaPkgClasses() { + return EMPTY_STRING_ARRAY; + } + + @Override + public Set getExcludedClasses() { + return Collections.emptySet(); + } + + @Override + public String[] getPromotedFields() { + return EMPTY_STRING_ARRAY; + } + + @Override + public String[] getPromotedClasses() { + return EMPTY_STRING_ARRAY; + } + + @Override + public Map getInjectedMethodsMap() { + return Collections.emptyMap(); + } +}