19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage com.android.tools.layoutlib.create;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.objectweb.asm.ClassReader;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.objectweb.asm.ClassVisitor;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.objectweb.asm.ClassWriter;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2358037a4404e639d28695a0f1c17053b53b96b970Deepanshu Guptaimport java.io.ByteArrayOutputStream;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileOutputStream;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.InputStream;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Arrays;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashMap;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashSet;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Map;
31bc101806249eb883f89c4a770a8c27f9ac315837Raphaelimport java.util.Map.Entry;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Set;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.TreeMap;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.jar.JarEntry;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.jar.JarOutputStream;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Class that generates a new JAR from a list of classes, some of which are to be kept as-is
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and some of which are to be stubbed partially or totally.
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class AsmGenerator {
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Output logger. */
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final Log mLog;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** The path of the destination JAR to create. */
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final String mOsDestJar;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** List of classes to inject in the final JAR from _this_ archive. */
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final Class<?>[] mInjectClasses;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** The set of methods to stub out. */
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final Set<String> mStubMethods;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** All classes to output as-is, except if they have native methods. */
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Map<String, ClassReader> mKeep;
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** All dependencies that must be completely stubbed. */
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Map<String, ClassReader> mDeps;
5558037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta    /** All files that are to be copied as-is. */
5658037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta    private Map<String, InputStream> mCopyFiles;
571160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta    /** All classes where certain method calls need to be rewritten. */
581160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta    private Set<String> mReplaceMethodCallsClasses;
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Counter of number of classes renamed during transform. */
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mRenameCount;
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** FQCN Names of the classes to rename: map old-FQCN => new-FQCN */
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final HashMap<String, String> mRenameClasses;
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** FQCN Names of "old" classes that were NOT renamed. This starts with the full list of
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  old-FQCN to rename and they get erased as they get renamed. At the end, classes still
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  left here are not in the code base anymore and thus were not renamed. */
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private HashSet<String> mClassesNotRenamed;
67bc101806249eb883f89c4a770a8c27f9ac315837Raphael    /** A map { FQCN => set { list of return types to delete from the FQCN } }. */
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private HashMap<String, Set<String>> mDeleteReturns;
69bc101806249eb883f89c4a770a8c27f9ac315837Raphael    /** A map { FQCN => set { method names } } of methods to rewrite as delegates.
70bc101806249eb883f89c4a770a8c27f9ac315837Raphael     *  The special name {@link DelegateClassAdapter#ALL_NATIVES} can be used as in internal set. */
71bc101806249eb883f89c4a770a8c27f9ac315837Raphael    private final HashMap<String, Set<String>> mDelegateMethods;
721cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta    /** FQCN Names of classes to refactor. All reference to old-FQCN will be updated to new-FQCN.
731cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta     * map old-FQCN => new-FQCN */
741cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta    private final HashMap<String, String> mRefactorClasses;
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a new generator that can generate the output JAR with the stubbed classes.
78bc101806249eb883f89c4a770a8c27f9ac315837Raphael     *
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param log Output logger.
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param osDestJar The path of the destination JAR to create.
81bc101806249eb883f89c4a770a8c27f9ac315837Raphael     * @param createInfo Creation parameters. Must not be null.
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
83bc101806249eb883f89c4a770a8c27f9ac315837Raphael    public AsmGenerator(Log log, String osDestJar, ICreateInfo createInfo) {
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLog = log;
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOsDestJar = osDestJar;
86bc101806249eb883f89c4a770a8c27f9ac315837Raphael        mInjectClasses = createInfo.getInjectedClasses();
87bc101806249eb883f89c4a770a8c27f9ac315837Raphael        mStubMethods = new HashSet<String>(Arrays.asList(createInfo.getOverriddenMethods()));
88bc101806249eb883f89c4a770a8c27f9ac315837Raphael
89bc101806249eb883f89c4a770a8c27f9ac315837Raphael        // Create the map/set of methods to change to delegates
90bc101806249eb883f89c4a770a8c27f9ac315837Raphael        mDelegateMethods = new HashMap<String, Set<String>>();
91bc101806249eb883f89c4a770a8c27f9ac315837Raphael        for (String signature : createInfo.getDelegateMethods()) {
92bc101806249eb883f89c4a770a8c27f9ac315837Raphael            int pos = signature.indexOf('#');
93bc101806249eb883f89c4a770a8c27f9ac315837Raphael            if (pos <= 0 || pos >= signature.length() - 1) {
94bc101806249eb883f89c4a770a8c27f9ac315837Raphael                continue;
95bc101806249eb883f89c4a770a8c27f9ac315837Raphael            }
96bc101806249eb883f89c4a770a8c27f9ac315837Raphael            String className = binaryToInternalClassName(signature.substring(0, pos));
97bc101806249eb883f89c4a770a8c27f9ac315837Raphael            String methodName = signature.substring(pos + 1);
98bc101806249eb883f89c4a770a8c27f9ac315837Raphael            Set<String> methods = mDelegateMethods.get(className);
99bc101806249eb883f89c4a770a8c27f9ac315837Raphael            if (methods == null) {
100bc101806249eb883f89c4a770a8c27f9ac315837Raphael                methods = new HashSet<String>();
101bc101806249eb883f89c4a770a8c27f9ac315837Raphael                mDelegateMethods.put(className, methods);
102bc101806249eb883f89c4a770a8c27f9ac315837Raphael            }
103bc101806249eb883f89c4a770a8c27f9ac315837Raphael            methods.add(methodName);
104bc101806249eb883f89c4a770a8c27f9ac315837Raphael        }
105bc101806249eb883f89c4a770a8c27f9ac315837Raphael        for (String className : createInfo.getDelegateClassNatives()) {
106280372e2c7db128254b5f65395ea843ef285729eRaphael            className = binaryToInternalClassName(className);
107bc101806249eb883f89c4a770a8c27f9ac315837Raphael            Set<String> methods = mDelegateMethods.get(className);
108bc101806249eb883f89c4a770a8c27f9ac315837Raphael            if (methods == null) {
109bc101806249eb883f89c4a770a8c27f9ac315837Raphael                methods = new HashSet<String>();
110bc101806249eb883f89c4a770a8c27f9ac315837Raphael                mDelegateMethods.put(className, methods);
111bc101806249eb883f89c4a770a8c27f9ac315837Raphael            }
112bc101806249eb883f89c4a770a8c27f9ac315837Raphael            methods.add(DelegateClassAdapter.ALL_NATIVES);
113bc101806249eb883f89c4a770a8c27f9ac315837Raphael        }
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Create the map of classes to rename.
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mRenameClasses = new HashMap<String, String>();
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mClassesNotRenamed = new HashSet<String>();
118bc101806249eb883f89c4a770a8c27f9ac315837Raphael        String[] renameClasses = createInfo.getRenamedClasses();
119bc101806249eb883f89c4a770a8c27f9ac315837Raphael        int n = renameClasses.length;
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < n; i += 2) {
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            assert i + 1 < n;
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // The ASM class names uses "/" separators, whereas regular FQCN use "."
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String oldFqcn = binaryToInternalClassName(renameClasses[i]);
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String newFqcn = binaryToInternalClassName(renameClasses[i + 1]);
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mRenameClasses.put(oldFqcn, newFqcn);
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mClassesNotRenamed.add(oldFqcn);
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
128bc101806249eb883f89c4a770a8c27f9ac315837Raphael
1291cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta        // Create a map of classes to be refactored.
1301cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta        mRefactorClasses = new HashMap<String, String>();
1311cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta        String[] refactorClasses = createInfo.getJavaPkgClasses();
1321cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta        n = refactorClasses.length;
1331cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta        for (int i = 0; i < n; i += 2) {
1341cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta            assert i + 1 < n;
1351cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta            String oldFqcn = binaryToInternalClassName(refactorClasses[i]);
1361cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta            String newFqcn = binaryToInternalClassName(refactorClasses[i + 1]);
1371160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta            mRefactorClasses.put(oldFqcn, newFqcn);
1381cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta        }
1391cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // create the map of renamed class -> return type of method to delete.
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDeleteReturns = new HashMap<String, Set<String>>();
142bc101806249eb883f89c4a770a8c27f9ac315837Raphael        String[] deleteReturns = createInfo.getDeleteReturns();
143bc101806249eb883f89c4a770a8c27f9ac315837Raphael        Set<String> returnTypes = null;
144bc101806249eb883f89c4a770a8c27f9ac315837Raphael        String renamedClass = null;
145bc101806249eb883f89c4a770a8c27f9ac315837Raphael        for (String className : deleteReturns) {
146bc101806249eb883f89c4a770a8c27f9ac315837Raphael            // if we reach the end of a section, add it to the main map
147bc101806249eb883f89c4a770a8c27f9ac315837Raphael            if (className == null) {
148bc101806249eb883f89c4a770a8c27f9ac315837Raphael                if (returnTypes != null) {
149bc101806249eb883f89c4a770a8c27f9ac315837Raphael                    mDeleteReturns.put(renamedClass, returnTypes);
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
151bc101806249eb883f89c4a770a8c27f9ac315837Raphael
152bc101806249eb883f89c4a770a8c27f9ac315837Raphael                renamedClass = null;
153bc101806249eb883f89c4a770a8c27f9ac315837Raphael                continue;
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
155bc101806249eb883f89c4a770a8c27f9ac315837Raphael
156bc101806249eb883f89c4a770a8c27f9ac315837Raphael            // if the renamed class is null, this is the beginning of a section
157bc101806249eb883f89c4a770a8c27f9ac315837Raphael            if (renamedClass == null) {
158bc101806249eb883f89c4a770a8c27f9ac315837Raphael                renamedClass = binaryToInternalClassName(className);
159bc101806249eb883f89c4a770a8c27f9ac315837Raphael                continue;
160bc101806249eb883f89c4a770a8c27f9ac315837Raphael            }
161bc101806249eb883f89c4a770a8c27f9ac315837Raphael
162bc101806249eb883f89c4a770a8c27f9ac315837Raphael            // just a standard return type, we add it to the list.
163bc101806249eb883f89c4a770a8c27f9ac315837Raphael            if (returnTypes == null) {
164bc101806249eb883f89c4a770a8c27f9ac315837Raphael                returnTypes = new HashSet<String>();
165bc101806249eb883f89c4a770a8c27f9ac315837Raphael            }
166bc101806249eb883f89c4a770a8c27f9ac315837Raphael            returnTypes.add(binaryToInternalClassName(className));
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
169bc101806249eb883f89c4a770a8c27f9ac315837Raphael
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the list of classes that have not been renamed yet.
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p/>
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The names are "internal class names" rather than FQCN, i.e. they use "/" instead "."
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * as package separators.
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Set<String> getClassesNotRenamed() {
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mClassesNotRenamed;
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Utility that returns the internal ASM class name from a fully qualified binary class
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * name. E.g. it returns android/view/View from android.view.View.
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    String binaryToInternalClassName(String className) {
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (className == null) {
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return className.replace('.', '/');
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Sets the map of classes to output as-is, except if they have native methods */
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setKeep(Map<String, ClassReader> keep) {
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mKeep = keep;
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Sets the map of dependencies that must be completely stubbed */
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setDeps(Map<String, ClassReader> deps) {
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDeps = deps;
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
201bc101806249eb883f89c4a770a8c27f9ac315837Raphael
20258037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta    /** Sets the map of files to output as-is. */
20358037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta    public void setCopyFiles(Map<String, InputStream> copyFiles) {
20458037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta        mCopyFiles = copyFiles;
20558037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta    }
20658037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta
2071160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta    public void setRewriteMethodCallClasses(Set<String> rewriteMethodCallClasses) {
2081160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        mReplaceMethodCallsClasses = rewriteMethodCallClasses;
20958037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta    }
21058037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Generates the final JAR */
2121160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta    public void generate() throws IOException {
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
214bc101806249eb883f89c4a770a8c27f9ac315837Raphael
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (Class<?> clazz : mInjectClasses) {
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String name = classToEntryPath(clazz);
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            InputStream is = ClassLoader.getSystemResourceAsStream(name);
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ClassReader cr = new ClassReader(is);
219c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta            byte[] b = transform(cr, true);
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            name = classNameToEntryPath(transformName(cr.getClassName()));
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            all.put(name, b);
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
223bc101806249eb883f89c4a770a8c27f9ac315837Raphael
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (Entry<String, ClassReader> entry : mDeps.entrySet()) {
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ClassReader cr = entry.getValue();
226c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta            byte[] b = transform(cr, true);
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String name = classNameToEntryPath(transformName(cr.getClassName()));
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            all.put(name, b);
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (Entry<String, ClassReader> entry : mKeep.entrySet()) {
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ClassReader cr = entry.getValue();
233c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta            byte[] b = transform(cr, true);
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String name = classNameToEntryPath(transformName(cr.getClassName()));
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            all.put(name, b);
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
23858037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta        for (Entry<String, InputStream> entry : mCopyFiles.entrySet()) {
23958037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta            try {
24058037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta                byte[] b = inputStreamToByteArray(entry.getValue());
24158037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta                all.put(entry.getKey(), b);
24258037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta            } catch (IOException e) {
24358037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta                // Ignore.
24458037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta            }
24558037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta
24658037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta        }
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLog.info("# deps classes: %d", mDeps.size());
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLog.info("# keep classes: %d", mKeep.size());
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLog.info("# renamed     : %d", mRenameCount);
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        createJar(new FileOutputStream(mOsDestJar), all);
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLog.info("Created JAR file %s", mOsDestJar);
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Writes the JAR file.
257bc101806249eb883f89c4a770a8c27f9ac315837Raphael     *
258bc101806249eb883f89c4a770a8c27f9ac315837Raphael     * @param outStream The file output stream were to write the JAR.
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param all The map of all classes to output.
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws IOException if an I/O error has occurred
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    void createJar(FileOutputStream outStream, Map<String,byte[]> all) throws IOException {
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        JarOutputStream jar = new JarOutputStream(outStream);
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (Entry<String, byte[]> entry : all.entrySet()) {
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String name = entry.getKey();
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            JarEntry jar_entry = new JarEntry(name);
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            jar.putNextEntry(jar_entry);
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            jar.write(entry.getValue());
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            jar.closeEntry();
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        jar.flush();
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        jar.close();
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Utility method that converts a fully qualified java name into a JAR entry path
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * e.g. for the input "android.view.View" it returns "android/view/View.class"
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    String classNameToEntryPath(String className) {
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return className.replaceAll("\\.", "/").concat(".class");
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
282bc101806249eb883f89c4a770a8c27f9ac315837Raphael
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Utility method to get the JAR entry path from a Class name.
285c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta     * e.g. it returns something like "com/foo/OuterClass$InnerClass1$InnerClass2.class"
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String classToEntryPath(Class<?> clazz) {
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String name = "";
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class<?> parent;
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while ((parent = clazz.getEnclosingClass()) != null) {
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            name = "$" + clazz.getSimpleName() + name;
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            clazz = parent;
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
294bc101806249eb883f89c4a770a8c27f9ac315837Raphael        return classNameToEntryPath(clazz.getCanonicalName() + name);
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Transforms a class.
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p/>
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * There are 3 kind of transformations:
301bc101806249eb883f89c4a770a8c27f9ac315837Raphael     *
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * 1- For "mock" dependencies classes, we want to remove all code from methods and replace
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * by a stub. Native methods must be implemented with this stub too. Abstract methods are
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * left intact. Modified classes must be overridable (non-private, non-final).
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Native methods must be made non-final, non-private.
306bc101806249eb883f89c4a770a8c27f9ac315837Raphael     *
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * 2- For "keep" classes, we want to rewrite all native methods as indicated above.
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If a class has native methods, it must also be made non-private, non-final.
309bc101806249eb883f89c4a770a8c27f9ac315837Raphael     *
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Note that unfortunately static methods cannot be changed to non-static (since static and
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * non-static are invoked differently.)
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    byte[] transform(ClassReader cr, boolean stubNativesOnly) {
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean hasNativeMethods = hasNativeMethods(cr);
316bc101806249eb883f89c4a770a8c27f9ac315837Raphael
317bc101806249eb883f89c4a770a8c27f9ac315837Raphael        // Get the class name, as an internal name (e.g. com/android/SomeClass$InnerClass)
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String className = cr.getClassName();
319bc101806249eb883f89c4a770a8c27f9ac315837Raphael
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String newName = transformName(className);
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // transformName returns its input argument if there's no need to rename the class
3221160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        if (!newName.equals(className)) {
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mRenameCount++;
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // This class is being renamed, so remove it from the list of classes not renamed.
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mClassesNotRenamed.remove(className);
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLog.debug("Transform %s%s%s%s", className,
3291160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta                newName.equals(className) ? "" : " (renamed to " + newName + ")",
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                hasNativeMethods ? " -- has natives" : "",
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                stubNativesOnly ? " -- stub natives only" : "");
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Rewrite the new class from scratch, without reusing the constant pool from the
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // original class reader.
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
336bc101806249eb883f89c4a770a8c27f9ac315837Raphael
3371160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        ClassVisitor cv = cw;
3381160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta
3391160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        if (mReplaceMethodCallsClasses.contains(className)) {
3401160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta            cv = new ReplaceMethodCallsAdapter(cv);
3411160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        }
3421160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta
3431160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        cv = new RefactorClassAdapter(cv, mRefactorClasses);
3441160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        if (!newName.equals(className)) {
3451cf5df38f4bdafa1beb2674ca548ad6d9650766bDeepanshu Gupta            cv = new RenameClassAdapter(cv, className, newName);
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
347bc101806249eb883f89c4a770a8c27f9ac315837Raphael
348c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta        cv = new TransformClassAdapter(mLog, mStubMethods, mDeleteReturns.get(className),
349c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta                newName, cv, stubNativesOnly);
350e2e8ba904b306f11dfc5b71e00ab9ac013358cd7Raphael
351bc101806249eb883f89c4a770a8c27f9ac315837Raphael        Set<String> delegateMethods = mDelegateMethods.get(className);
352bc101806249eb883f89c4a770a8c27f9ac315837Raphael        if (delegateMethods != null && !delegateMethods.isEmpty()) {
353bc101806249eb883f89c4a770a8c27f9ac315837Raphael            // If delegateMethods only contains one entry ALL_NATIVES and the class is
354bc101806249eb883f89c4a770a8c27f9ac315837Raphael            // known to have no native methods, just skip this step.
355bc101806249eb883f89c4a770a8c27f9ac315837Raphael            if (hasNativeMethods ||
356bc101806249eb883f89c4a770a8c27f9ac315837Raphael                    !(delegateMethods.size() == 1 &&
357bc101806249eb883f89c4a770a8c27f9ac315837Raphael                            delegateMethods.contains(DelegateClassAdapter.ALL_NATIVES))) {
358e2e8ba904b306f11dfc5b71e00ab9ac013358cd7Raphael                cv = new DelegateClassAdapter(mLog, cv, className, delegateMethods);
359bc101806249eb883f89c4a770a8c27f9ac315837Raphael            }
360bc101806249eb883f89c4a770a8c27f9ac315837Raphael        }
361bc101806249eb883f89c4a770a8c27f9ac315837Raphael
362c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta        cr.accept(cv, 0);
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return cw.toByteArray();
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Should this class be renamed, this returns the new name. Otherwise it returns the
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * original name.
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param className The internal ASM name of the class that may have to be renamed
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return A new transformed name or the original input argument.
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    String transformName(String className) {
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String newName = mRenameClasses.get(className);
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (newName != null) {
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return newName;
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int pos = className.indexOf('$');
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (pos > 0) {
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Is this an inner class of a renamed class?
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String base = className.substring(0, pos);
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            newName = mRenameClasses.get(base);
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (newName != null) {
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return newName + className.substring(pos);
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
387bc101806249eb883f89c4a770a8c27f9ac315837Raphael
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return className;
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns true if a class has any native methods.
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    boolean hasNativeMethods(ClassReader cr) {
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ClassHasNativeVisitor cv = new ClassHasNativeVisitor();
396c5a58437e62698f34abde93ec785c4cbe36aec2dDeepanshu Gupta        cr.accept(cv, 0);
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return cv.hasNativeMethods();
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
40058037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta    private byte[] inputStreamToByteArray(InputStream is) throws IOException {
40158037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
40258037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta        byte[] data = new byte[8192];  // 8KB
40358037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta        int n;
40458037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta        while ((n = is.read(data, 0, data.length)) != -1) {
40558037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta            buffer.write(data, 0, n);
40658037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta        }
40758037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta        return buffer.toByteArray();
40858037a4404e639d28695a0f1c17053b53b96b970Deepanshu Gupta    }
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
410