[android-platform-tools-base] 01/01: Imported Debian patch 1.5.0-1
Markus Koschany
apo-guest at moszumanska.debian.org
Thu Feb 18 00:27:10 GMT 2016
This is an automated email from the git hooks/post-receive script.
apo-guest pushed a commit to branch master
in repository android-platform-tools-base.
commit 6b8dbcbbfed96cde735a59d1383b642bcfa42074
Author: Markus Koschany <apo at debian.org>
Date: Thu Feb 18 00:47:02 2016 +0100
Imported Debian patch 1.5.0-1
---
debian/android-platform-tools-base.install | 27 +
debian/changelog | 5 +
debian/compat | 1 +
debian/control | 73 +
debian/copyright | 341 +
debian/libandroid-tools-annotations-java.jlibs | 2 +
...ndroid-tools-annotations-java.lintian-overrides | 3 +
debian/libandroid-tools-annotations-java.poms | 1 +
debian/libandroid-tools-common-java.jlibs | 2 +
.../libandroid-tools-common-java.lintian-overrides | 3 +
debian/libandroid-tools-common-java.poms | 1 +
debian/maven.rules | 12 +
debian/patches/SdkTestCase.patch | 26 +
debian/patches/build.gradle.patch | 1102 +++
debian/patches/disable-lint.patch | 9620 ++++++++++++++++++++
debian/patches/gradle-experimental.patch | 62 +
debian/patches/project-test-lib.patch | 35 +
debian/patches/series | 7 +
debian/patches/settings.gradle.patch | 87 +
debian/patches/trove3.patch | 154 +
debian/poms/android-tools-annotations-pom.xml | 26 +
debian/poms/android-tools-common-pom.xml | 40 +
debian/rules | 11 +
debian/source/format | 1 +
debian/watch | 2 +
25 files changed, 11644 insertions(+)
diff --git a/debian/android-platform-tools-base.install b/debian/android-platform-tools-base.install
new file mode 100644
index 0000000..72a90fd
--- /dev/null
+++ b/debian/android-platform-tools-base.install
@@ -0,0 +1,27 @@
+out/build/base/asset-studio/build/libs/asset-studio-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/testutils/build/libs/testutils-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/google-services/build/libs/google-services-1.5.0.jar usr/share/android-platform-tools-base
+out/build/base/layoutlib-api/build/libs/layoutlib-api-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/sdklib-test/build/libs/sdklib-test-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/archquery/build/libs/archquery-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/draw9patch/build/libs/draw9patch-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/builder-test-api/build/libs/builder-test-api-1.5.0.jar usr/share/android-platform-tools-base
+out/build/base/perflib/build/libs/perflib-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/screenshot2/build/libs/screenshot2-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/profile/build/libs/profile.jar usr/share/android-platform-tools-base
+out/build/base/ant-tasks/build/libs/ant-tasks-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/sdklib/build/libs/sdklib-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/builder-model/build/libs/builder-model-1.5.0.jar usr/share/android-platform-tools-base
+out/build/base/builder/build/libs/builder-1.5.0.jar usr/share/android-platform-tools-base
+out/build/base/gradle-core/build/libs/gradle-core-1.5.0.jar usr/share/android-platform-tools-base
+out/build/base/jack/jack-api/build/libs/jack-api-0.9.0.jar usr/share/android-platform-tools-base
+out/build/base/jack/jill-api/build/libs/jill-api-0.9.0.jar usr/share/android-platform-tools-base
+out/build/base/gradle/build/libs/gradle-1.5.0.jar usr/share/android-platform-tools-base
+out/build/base/dvlib/build/libs/dvlib-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/manifest-merger/build/libs/manifest-merger-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/ninepatch/build/libs/ninepatch-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/chartlib/build/libs/chartlib-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/sdk-common/build/libs/sdk-common-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/rule-api/build/libs/rule-api-24.5.0.jar usr/share/android-platform-tools-base
+out/build/base/api-generator/build/libs/api-generator.jar usr/share/android-platform-tools-base
+out/build/base/ddmlib/build/libs/ddmlib-24.5.0.jar usr/share/android-platform-tools-base
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..79a70e5
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+android-platform-tools-base (1.5.0-1) unstable; urgency=medium
+
+ * Initial release. (Closes: #814797)
+
+ -- Markus Koschany <apo at debian.org> Thu, 18 Feb 2016 00:47:02 +0100
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..892d7df
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,73 @@
+Source: android-platform-tools-base
+Section: java
+Priority: optional
+Maintainer: Android Tools Maintainer <android-tools-devel at lists.alioth.debian.org>
+Uploaders:
+ Markus Koschany <apo at debian.org>
+Build-Depends:
+ debhelper (>= 9),
+ default-jdk,
+ gradle-debian-helper,
+ javahelper,
+ junit4,
+ libandroid-tools-annotations-java,
+ libandroid-tools-common-java,
+ libasm4-java,
+ libbcpkix-java,
+ libbcprov-java,
+ libcommons-compress-java,
+ libhttpmime-java,
+ libintellij-annotations-java,
+ libjavawriter-java,
+ libjsr305-java,
+ libkxml2-java,
+ libtrove3-java,
+ maven-debian-helper,
+ maven-repo-helper,
+ proguard
+Standards-Version: 3.9.7
+Vcs-Browser: https://anonscm.debian.org/cgit/pkg-java/android-platform-tools-base.git
+Vcs-Git: https://anonscm.debian.org/git/pkg-java/android-platform-tools-base.git
+Homepage: https://android.googlesource.com/platform/tools/base/
+
+Package: android-platform-tools-base
+Architecture: all
+Depends:
+ ${misc:Depends}
+Description: base tools for developing applications for the Android system
+ This package includes various tools for developing and building Android
+ applications, e.g.
+ .
+ * Draw 9-patch
+ Allows you to easily create a NinePatch graphic using a WYSIWYG
+ editor. It also previews stretched versions of the image, and
+ highlights the area in which content is allowed.
+ .
+ * JOBB
+ Allows you to build encrypted and unencrypted APK expansion files
+ in Opaque Binary Blob (OBB) format.
+ .
+ * Android plugin for Gradle
+ The Android build system consists of an Android plugin for Gradle.
+ You can build your Android apps from within Android Studio and from
+ the command line on your machine or on machines where Android Studio
+ is not installed (such as continuous integration servers).
+ .
+ * Manifest Merger, ddmlib, layoutlib, chartlib, Jack & Jill (Java Android
+ Compiler Kit)
+
+Package: libandroid-tools-annotations-java
+Architecture: all
+Depends:
+ ${misc:Depends}
+Description: annotations used throughout the Android tools libraries
+ This package provides support for annotations which can be found in all
+ Android tools libraries.
+
+Package: libandroid-tools-common-java
+Architecture: all
+Depends:
+ ${misc:Depends}
+Description: common library used by other Android tools libraries
+ This package provides common tasks and classes which are used by other Android
+ tools libraries.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..6afe3f0
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,341 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: Android Platform Tools Base
+Source: https://android.googlesource.com/platform/tools/base/
+Files-Excluded:
+ *.so
+
+Files: *
+Copyright: 2009-2015, The Android Open Source Project
+License: Apache-2.0
+
+Files: base/rule-api/src/main/java/com/android/ide/common/api/RuleAction.java
+ base/rule-api/src/main/java/com/android/ide/common/api/*
+ base/asset-studio/src/test/java/com/android/assetstudiolib/*
+ base/sdklib/src/test/java/com/android/sdklib/internal/repository/AddonsListFetcherTest.java
+ base/sdklib/src/test/java/com/android/sdklib/internal/repository/*.java
+ base/sdklib/src/test/java/com/android/sdklib/internal/build/SymbolLoaderTest.java
+ base/sdklib/src/test/java/com/android/sdklib/repository/*.java
+ base/device_validator/app/src/com/android/validator/*
+ base/testutils/src/main/java/com/android/testutils/SdkTestCase.java
+ base/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMergerTest.java
+ base/build-system/builder/src/test/java/com/android/builder/internal/SymbolWriterTest.java
+ base/sdk-common/src/main/java/com/android/ide/common/resources/ScanningContext.java
+ base/common/src/test/java/com/android/io/NonClosingInputStreamTest.java
+Copyright: 2009-2015, The Android Open Source Project
+License: EPL-1.0
+
+Files: base/build-system/integration-test/test-projects/ndkSanAngeles2/*
+Copyright: 2009, The Android Open Source Project
+ 2004-2005, Jetro Lauha
+License: LGPL-2.1+ or BSD-3-clause
+
+Files: base/jobb/src/main/java/Twofish/Twofish*
+Copyright: The Cryptix Foundation Limited
+License: Cryptix-General-License
+
+Files: base/jobb/src/main/java/com/android/jobb/Base64.java
+Copyright: 2000, The Legion Of The Bouncy Castle
+License: BSD-3-clause
+
+Files: debian/*
+Copyright: 2016, Markus Koschany <apo at debian.org>
+License: Apache-2.0
+
+License: LGPL-2.1+
+ On Debian systems the full license text of the GNU Lesser General Public
+ License 2.1 can be found in /usr/share/common-licenses/LGPL-2.1.
+
+License: Apache-2.0
+ 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.
+ .
+ On Debian systems, the complete text of the Apache version 2.0 license
+ can be found in "/usr/share/common-licenses/Apache-2.0".
+
+License: Cryptix-General-License
+ Cryptix General License
+ .
+ Copyright (c) 1995-2005 The Cryptix Foundation Limited.
+ .
+ All rights reserved.
+ .
+ Redistribution and use in source and binary forms, with or without
+ .
+ modification, are permitted provided that the following conditions are
+ .
+ met:
+ .
+ 1. Redistributions of source code must retain the copyright notice,
+ this list of conditions and the following disclaimer.
+ .
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE CRYPTIX FOUNDATION LIMITED AND
+ .
+ CONTRIBUTORS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ .
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ .
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ .
+ IN NO EVENT SHALL THE CRYPTIX FOUNDATION LIMITED OR CONTRIBUTORS BE
+ .
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ .
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ .
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ .
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ .
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ .
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ .
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+License: BSD-3-clause
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the Software
+ is furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN~
+ THE SOFTWARE.
+
+License: EPL-1.0
+ THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+ PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE
+ PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+ .
+ 1. DEFINITIONS
+ .
+ "Contribution" means:
+ .
+ a) in the case of the initial Contributor, the initial code and documentation
+ distributed under this Agreement, and
+ b) in the case of each subsequent Contributor:
+ .
+ i) changes to the Program, and
+ .
+ ii) additions to the Program;
+ .
+ where such changes and/or additions to the Program originate from and are
+ distributed by that particular Contributor. A Contribution 'originates'
+ from a Contributor if it was added to the Program by such Contributor itself
+ or anyone acting on such Contributor's behalf. Contributions do not include
+ additions to the Program which: (i) are separate modules of software
+ distributed in conjunction with the Program under their own license
+ agreement, and (ii) are not derivative works of the Program.
+ .
+ "Contributor" means any person or entity that distributes the Program.
+ .
+ "Licensed Patents " mean patent claims licensable by a Contributor which are
+ necessarily infringed by the use or sale of its Contribution alone or when
+ combined with the Program.
+ .
+ "Program" means the Contributions distributed in accordance with this
+ Agreement.
+ .
+ "Recipient" means anyone who receives the Program under this Agreement,
+ including all Contributors.
+ .
+ 2. GRANT OF RIGHTS
+ .
+ a) Subject to the terms of this Agreement, each Contributor hereby grants
+ Recipient a non-exclusive, worldwide, royalty-free copyright license to
+ reproduce, prepare derivative works of, publicly display, publicly perform,
+ distribute and sublicense the Contribution of such Contributor, if any,
+ and such derivative works, in source code and object code form.
+ .
+ b) Subject to the terms of this Agreement, each Contributor hereby grants
+ Recipient a non-exclusive, worldwide, royalty-free patent license under
+ Licensed Patents to make, use, sell, offer to sell, import and otherwise
+ transfer the Contribution of such Contributor, if any, in source code and
+ object code form. This patent license shall apply to the combination of
+ the Contribution and the Program if, at the time the Contribution is added
+ by the Contributor, such addition of the Contribution causes such
+ combination to be covered by the Licensed Patents. The patent license shall
+ not apply to any other combinations which include the Contribution. No
+ hardware per se is licensed hereunder.
+ .
+ c) Recipient understands that although each Contributor grants the licenses
+ to its Contributions set forth herein, no assurances are provided by any
+ Contributor that the Program does not infringe the patent or other
+ intellectual property rights of any other entity. Each Contributor disclaims
+ any liability to Recipient for claims brought by any other entity based on
+ infringement of intellectual property rights or otherwise. As a condition to
+ exercising the rights and licenses granted hereunder, each Recipient hereby
+ assumes sole responsibility to secure any other intellectual property rights
+ needed, if any. For example, if a third party patent license is required to
+ allow Recipient to distribute the Program, it is Recipient's responsibility
+ to acquire that license before distributing the Program.
+ .
+ d) Each Contributor represents that to its knowledge it has sufficient
+ copyright rights in its Contribution, if any, to grant the copyright license
+ set forth in this Agreement.
+ .
+ 3. REQUIREMENTS
+ .
+ A Contributor may choose to distribute the Program in object code form under
+ its own license agreement, provided that:
+ .
+ a) it complies with the terms and conditions of this Agreement; and
+ .
+ b) its license agreement:
+ .
+ i) effectively disclaims on behalf of all Contributors all warranties and
+ conditions, express and implied, including warranties or conditions of title
+ and non-infringement, and implied warranties or conditions of merchantability
+ and fitness for a particular purpose;
+ .
+ ii) effectively excludes on behalf of all Contributors all liability for
+ damages, including direct, indirect, special, incidental and consequential
+ damages, such as lost profits;
+ .
+ iii) states that any provisions which differ from this Agreement are offered
+ by that Contributor alone and not by any other party; and
+ .
+ iv) states that source code for the Program is available from such
+ Contributor, and informs licensees how to obtain it in a reasonable manner on
+ or through a medium customarily used for software exchange.
+ .
+ When the Program is made available in source code form:
+ .
+ a) it must be made available under this Agreement; and
+ .
+ b) a copy of this Agreement must be included with each copy of the Program.
+ .
+ Contributors may not remove or alter any copyright notices contained within
+ the Program.
+ .
+ Each Contributor must identify itself as the originator of its Contribution,
+ if any, in a manner that reasonably allows subsequent Recipients to identify
+ the originator of the Contribution.
+ .
+ 4. COMMERCIAL DISTRIBUTION
+ .
+ Commercial distributors of software may accept certain responsibilities with
+ respect to end users, business partners and the like. While this license is
+ intended to facilitate the commercial use of the Program, the Contributor who
+ includes the Program in a commercial product offering should do so in a manner
+ which does not create potential liability for other Contributors. Therefore,
+ if a Contributor includes the Program in a commercial product offering, such
+ Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
+ every other Contributor ("Indemnified Contributor") against any losses,
+ damages and costs (collectively "Losses") arising from claims, lawsuits and
+ other legal actions brought by a third party against the Indemnified
+ Contributor to the extent caused by the acts or omissions of such Commercial
+ Contributor in connection with its distribution of the Program in a commercial
+ product offering. The obligations in this section do not apply to any claims
+ or Losses relating to any actual or alleged intellectual property
+ infringement. In order to qualify, an Indemnified Contributor must:
+ a) promptly notify the Commercial Contributor in writing of such claim, and
+ b) allow the Commercial Contributor to control, and cooperate with the
+ Commercial Contributor in, the defense and any related settlement
+ negotiations. The Indemnified Contributor may participate in any such claim
+ at its own expense.
+ .
+ For example, a Contributor might include the Program in a commercial product
+ offering, Product X. That Contributor is then a Commercial Contributor. If
+ that Commercial Contributor then makes performance claims, or offers
+ warranties related to Product X, those performance claims and warranties are
+ such Commercial Contributor's responsibility alone. Under this section, the
+ Commercial Contributor would have to defend claims against the other
+ Contributors related to those performance claims and warranties, and if a
+ court requires any other Contributor to pay any damages as a result, the
+ Commercial Contributor must pay those damages.
+ .
+ 5. NO WARRANTY
+ .
+ EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON
+ AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
+ EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR
+ CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+ PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the
+ appropriateness of using and distributing the Program and assumes all
+ risks associated with its exercise of rights under this Agreement ,
+ including but not limited to the risks and costs of program errors,
+ compliance with applicable laws, damage to or loss of data, programs or
+ equipment, and unavailability or interruption of operations.
+ .
+ 6. DISCLAIMER OF LIABILITY
+ .
+ EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
+ CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
+ LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+ EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGES.
+ .
+ 7. GENERAL
+ .
+ If any provision of this Agreement is invalid or unenforceable under
+ applicable law, it shall not affect the validity or enforceability of the
+ remainder of the terms of this Agreement, and without further action by
+ the parties hereto, such provision shall be reformed to the minimum extent
+ necessary to make such provision valid and enforceable.
+ .
+ If Recipient institutes patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Program itself
+ (excluding combinations of the Program with other software or hardware)
+ infringes such Recipient's patent(s), then such Recipient's rights granted
+ under Section 2(b) shall terminate as of the date such litigation is filed.
+ .
+ All Recipient's rights under this Agreement shall terminate if it fails to
+ comply with any of the material terms or conditions of this Agreement and
+ does not cure such failure in a reasonable period of time after becoming
+ aware of such noncompliance. If all Recipient's rights under this Agreement
+ terminate, Recipient agrees to cease use and distribution of the Program as
+ soon as reasonably practicable. However, Recipient's obligations under this
+ Agreement and any licenses granted by Recipient relating to the Program
+ shall continue and survive.
+ .
+ Everyone is permitted to copy and distribute copies of this Agreement, but
+ in order to avoid inconsistency the Agreement is copyrighted and may only
+ be modified in the following manner. The Agreement Steward reserves the
+ right to publish new versions (including revisions) of this Agreement from
+ time to time. No one other than the Agreement Steward has the right to
+ modify this Agreement. The Eclipse Foundation is the initial Agreement
+ Steward. The Eclipse Foundation may assign the responsibility to serve as
+ the Agreement Steward to a suitable separate entity. Each new version of
+ the Agreement will be given a distinguishing version number. The Program
+ (including Contributions) may always be distributed subject to the version
+ of the Agreement under which it was received. In addition, after a new
+ version of the Agreement is published, Contributor may elect to distribute
+ the Program (including its Contributions) under the new version. Except as
+ expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
+ rights or licenses to the intellectual property of any Contributor under
+ this Agreement, whether expressly, by implication, estoppel or otherwise.
+ All rights in the Program not expressly granted under this Agreement are
+ reserved.
+ .
+ This Agreement is governed by the laws of the State of New York and the
+ intellectual property laws of the United States of America. No party to
+ this Agreement will bring a legal action under this Agreement more than
+ one year after the cause of action arose. Each party waives its rights to
+ a jury trial in any resulting litigation.
diff --git a/debian/libandroid-tools-annotations-java.jlibs b/debian/libandroid-tools-annotations-java.jlibs
new file mode 100644
index 0000000..3fad08f
--- /dev/null
+++ b/debian/libandroid-tools-annotations-java.jlibs
@@ -0,0 +1,2 @@
+com.android.tools.annotations.jar
+
diff --git a/debian/libandroid-tools-annotations-java.lintian-overrides b/debian/libandroid-tools-annotations-java.lintian-overrides
new file mode 100644
index 0000000..4d50e31
--- /dev/null
+++ b/debian/libandroid-tools-annotations-java.lintian-overrides
@@ -0,0 +1,3 @@
+# Java 8 is the new default now, hence all jars will work.
+incompatible-java-bytecode-format
+
diff --git a/debian/libandroid-tools-annotations-java.poms b/debian/libandroid-tools-annotations-java.poms
new file mode 100644
index 0000000..a4084b0
--- /dev/null
+++ b/debian/libandroid-tools-annotations-java.poms
@@ -0,0 +1 @@
+debian/poms/android-tools-annotations-pom.xml --no-parent --usj-name=com.android.tools.annotations --artifact=com.android.tools.annotations.jar
diff --git a/debian/libandroid-tools-common-java.jlibs b/debian/libandroid-tools-common-java.jlibs
new file mode 100644
index 0000000..eb3dc31
--- /dev/null
+++ b/debian/libandroid-tools-common-java.jlibs
@@ -0,0 +1,2 @@
+com.android.tools.common.jar
+
diff --git a/debian/libandroid-tools-common-java.lintian-overrides b/debian/libandroid-tools-common-java.lintian-overrides
new file mode 100644
index 0000000..4d50e31
--- /dev/null
+++ b/debian/libandroid-tools-common-java.lintian-overrides
@@ -0,0 +1,3 @@
+# Java 8 is the new default now, hence all jars will work.
+incompatible-java-bytecode-format
+
diff --git a/debian/libandroid-tools-common-java.poms b/debian/libandroid-tools-common-java.poms
new file mode 100644
index 0000000..a77a0d2
--- /dev/null
+++ b/debian/libandroid-tools-common-java.poms
@@ -0,0 +1 @@
+debian/poms/android-tools-common-pom.xml --no-parent --usj-name=com.android.tools.common --artifact=com.android.tools.common.jar
diff --git a/debian/maven.rules b/debian/maven.rules
new file mode 100644
index 0000000..31bf657
--- /dev/null
+++ b/debian/maven.rules
@@ -0,0 +1,12 @@
+s/net.sf.kxml/kxml2/ * * s/.*/debian/ * *
+org.ow2.asm asm * s/.*/4.x/ * *
+org.ow2.asm asm-tree * s/.*/4.x/ * *
+org.ow2.asm asm-analysis * s/.*/4.x/ * *
+com.google.code.findbugs jsr305 * s/.*/0.x/ * *
+org.bouncycastle s/bcpkix-jdk15on/bcpkix/ * s/.*/1.51/ * *
+org.bouncycastle s/bcprov-jdk15on/bcprov/ * s/.*/1.51/ * *
+junit junit * s/.*/4.x/ * *
+org.gradle s/messaging/gradle-messaging/ * s/.*/debian/ * *
+org.gradle s/core/gradle-core/ * s/.*/debian/ * *
+org.gradle s/wrapper/gradle-wrapper/ * s/.*/debian/ * *
+org.gradle s/baseServices/gradle-base-services/ * s/.*/debian/ * *
diff --git a/debian/patches/SdkTestCase.patch b/debian/patches/SdkTestCase.patch
new file mode 100644
index 0000000..5666a3d
--- /dev/null
+++ b/debian/patches/SdkTestCase.patch
@@ -0,0 +1,26 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sat, 6 Feb 2016 22:13:21 +0100
+Subject: SdkTestCase
+
+---
+ .../testutils/src/main/java/com/android/testutils/SdkTestCase.java | 7 -------
+ 1 file changed, 7 deletions(-)
+
+diff --git a/base/testutils/src/main/java/com/android/testutils/SdkTestCase.java b/base/testutils/src/main/java/com/android/testutils/SdkTestCase.java
+index 75877a5..2a3eded 100644
+--- a/base/testutils/src/main/java/com/android/testutils/SdkTestCase.java
++++ b/base/testutils/src/main/java/com/android/testutils/SdkTestCase.java
+@@ -353,13 +353,6 @@ public abstract class SdkTestCase extends TestCase {
+ tempFile.delete();
+ }
+
+- Files.copy(new InputSupplier<InputStream>() {
+- @Override
+- public InputStream getInput() throws IOException {
+- return contents;
+- }
+- }, tempFile);
+-
+ return tempFile;
+ }
+
diff --git a/debian/patches/build.gradle.patch b/debian/patches/build.gradle.patch
new file mode 100644
index 0000000..e72125c
--- /dev/null
+++ b/debian/patches/build.gradle.patch
@@ -0,0 +1,1102 @@
+From: Markus Koschany <apo at debian.org>
+Date: Thu, 21 Jan 2016 20:52:18 +0100
+Subject: build.gradle
+
+Patch upstream's build system for Debian.
+
+Forwarded: not-needed
+---
+ base/annotations/build.gradle | 7 ++---
+ base/asset-studio/build.gradle | 5 ++--
+ base/build-system/builder-model/build.gradle | 13 +++-------
+ base/build-system/builder-test-api/build.gradle | 8 +-----
+ base/build-system/builder/build.gradle | 19 +++-----------
+ base/build-system/google-services/build.gradle | 7 +----
+ base/build-system/gradle-core/build.gradle | 6 +----
+ base/build-system/gradle-experimental/build.gradle | 7 +----
+ base/build-system/gradle/build.gradle | 9 +------
+ base/build-system/integration-test/build.gradle | 5 ++--
+ .../3rdPartyTests/buildSrc/build.gradle | 3 +--
+ base/build-system/manifest-merger/build.gradle | 7 ++---
+ base/build-system/project-test-lib/build.gradle | 2 --
+ base/build-system/project-test/build.gradle | 2 --
+ base/build.gradle | 1 -
+ base/chartlib/build.gradle | 3 +--
+ base/common/build.gradle | 7 ++---
+ base/ddmlib/build.gradle | 7 ++---
+ base/device_validator/dvlib/build.gradle | 8 ++----
+ base/draw9patch/build.gradle | 17 ++----------
+ base/jack/jack-api/build.gradle | 3 ---
+ base/jack/jill-api/build.gradle | 3 ---
+ base/jobb/build.gradle | 16 ++----------
+ base/layoutlib-api/build.gradle | 7 ++---
+ base/legacy/ant-tasks/build.gradle | 4 +--
+ base/legacy/archquery/build.gradle | 5 ++--
+ base/lint/cli/build.gradle | 20 +++------------
+ base/lint/libs/lint-api/build.gradle | 7 ++---
+ base/lint/libs/lint-checks/build.gradle | 7 ++---
+ base/lint/libs/lint-tests/build.gradle | 5 +---
+ base/misc/screenshot2/build.gradle | 15 ++---------
+ base/ninepatch/build.gradle | 4 +--
+ base/perflib/build.gradle | 3 +--
+ base/rpclib/build.gradle | 3 +--
+ base/rule-api/build.gradle | 4 +--
+ base/sdk-common/build.gradle | 7 ++---
+ base/sdk-common/generate-locale-data/build.gradle | 5 ++--
+ base/sdklib/build.gradle | 7 ++---
+ base/sdklib/test.gradle | 2 +-
+ base/templates/build.gradle | 10 --------
+ base/testutils/build.gradle | 5 +---
+ build.gradle | 30 ++--------------------
+ 42 files changed, 65 insertions(+), 250 deletions(-)
+
+diff --git a/base/annotations/build.gradle b/base/annotations/build.gradle
+index dbfcea6..d448bbf 100644
+--- a/base/annotations/build.gradle
++++ b/base/annotations/build.gradle
+@@ -1,13 +1,10 @@
+ apply plugin: 'java'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools'
+ archivesBaseName = 'annotations'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ project.ext.pomName = 'Android Tools Annotations library'
+ project.ext.pomDesc = 'annotations used throughout the Android tools libraries.'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+diff --git a/base/asset-studio/build.gradle b/base/asset-studio/build.gradle
+index 1e186ad..35c11b6 100644
+--- a/base/asset-studio/build.gradle
++++ b/base/asset-studio/build.gradle
+@@ -1,10 +1,9 @@
+ apply plugin: 'java'
+-apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools'
+ archivesBaseName = 'asset-studio'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:layoutlib-api')
+diff --git a/base/build-system/builder-model/build.gradle b/base/build-system/builder-model/build.gradle
+index 0f2fc5f..123de89 100644
+--- a/base/build-system/builder-model/build.gradle
++++ b/base/build-system/builder-model/build.gradle
+@@ -1,6 +1,5 @@
+ apply plugin: 'java'
+-apply plugin: 'clone-artifacts'
+-apply plugin: 'sdk-java-lib'
++
+
+ dependencies {
+ compile project(':base:annotations')
+@@ -8,20 +7,16 @@ dependencies {
+
+ group = 'com.android.tools.build'
+ archivesBaseName = 'builder-model'
+-version = rootProject.ext.buildVersion
++version = '1.5.0'
+
+ project.ext.pomName = 'Android Builder Model library'
+ project.ext.pomDesc = 'Model for the Builder library.'
+
+-project.ext.apiVersion = rootProject.ext.apiVersion ?: 0
++project.ext.apiVersion = '3'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+ ["Model-Version": version, "Model-Api-Version": apiVersion].each { key, value ->
+ jar.manifest.attributes((key): value)
+- sdkJar.manifest.attributes((key): value)
+ }
+
+ def generated = new File("${project.buildDir}/generated/java")
+@@ -53,4 +48,4 @@ public final class Version {
+ """
+ }
+
+-tasks.compileJava.dependsOn generateVersionConstantsJava
+\ No newline at end of file
++tasks.compileJava.dependsOn generateVersionConstantsJava
+diff --git a/base/build-system/builder-test-api/build.gradle b/base/build-system/builder-test-api/build.gradle
+index d24b00f..07524a0 100644
+--- a/base/build-system/builder-test-api/build.gradle
++++ b/base/build-system/builder-test-api/build.gradle
+@@ -1,6 +1,4 @@
+ apply plugin: 'java'
+-apply plugin: 'jacoco'
+-apply plugin: 'clone-artifacts'
+
+ dependencies {
+ compile project(':base:ddmlib')
+@@ -10,12 +8,8 @@ dependencies {
+
+ group = 'com.android.tools.build'
+ archivesBaseName = 'builder-test-api'
+-version = rootProject.ext.buildVersion
++version = '1.5.0'
+
+ project.ext.pomName = 'Android Builder Test API library'
+ project.ext.pomDesc = 'API for the Test extension point in the Builder library.'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+-
+diff --git a/base/build-system/builder/build.gradle b/base/build-system/builder/build.gradle
+index 050a4a1..c1379b6 100644
+--- a/base/build-system/builder/build.gradle
++++ b/base/build-system/builder/build.gradle
+@@ -1,6 +1,4 @@
+ apply plugin: 'java'
+-apply plugin: 'clone-artifacts'
+-apply plugin: 'jacoco'
+
+ evaluationDependsOn(':base:builder-model')
+ evaluationDependsOn(':base:builder-test-api')
+@@ -27,7 +25,7 @@ dependencies {
+ testCompile 'junit:junit:4.12'
+ testCompile 'org.mockito:mockito-all:1.9.5'
+ testCompile project(':base:testutils')
+- testCompile project(':base:sdklib').sourceSets.test.output
++ testCompile project(':base:sdklib')
+ }
+
+ test {
+@@ -36,14 +34,11 @@ test {
+
+ group = 'com.android.tools.build'
+ archivesBaseName = 'builder'
+-version = rootProject.ext.buildVersion
++version = '1.5.0'
+
+ project.ext.pomName = 'Android Builder library'
+ project.ext.pomDesc = 'Library to build Android applications.'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+ jar.manifest.attributes("Builder-Version": version)
+
+@@ -58,8 +53,6 @@ sourceSets {
+ }
+
+ task generateVersionConstantsJava {
+- inputs.property("apiVersion", apiVersion)
+- inputs.property("version", version)
+ ext.versionFile = new File(generated, "com/android/builder/Version.java")
+ outputs.file(versionFile)
+ }
+@@ -70,8 +63,8 @@ package com.android.builder;
+
+ public final class Version {
+ private Version() {}
+- public static final String ANDROID_GRADLE_PLUGIN_VERSION = "$version";
+- public static final int BUILDER_MODEL_API_VERSION = $apiVersion;
++ public static final String ANDROID_GRADLE_PLUGIN_VERSION = "1.5";
++ public static final int BUILDER_MODEL_API_VERSION = 3;
+ }
+ """
+ }
+@@ -95,11 +88,7 @@ dependencies {
+ sourceSets.main.compileClasspath += [configurations.provided]
+ sourceSets.test.runtimeClasspath += [configurations.provided]
+ tasks.compileJava.dependsOn(configurations.provided)
+-tasks.sourcesJar.dependsOn(configurations.sourcesProvided)
+
+ tasks.jar {
+ from({zipTree(configurations.provided.singleFile)})
+ }
+-tasks.sourcesJar {
+- from({zipTree(configurations.sourcesProvided.singleFile)})
+-}
+\ No newline at end of file
+diff --git a/base/build-system/google-services/build.gradle b/base/build-system/google-services/build.gradle
+index e5afe75..663364e 100644
+--- a/base/build-system/google-services/build.gradle
++++ b/base/build-system/google-services/build.gradle
+@@ -1,7 +1,4 @@
+ apply plugin: 'groovy'
+-apply plugin: 'clone-artifacts'
+-apply plugin: 'idea'
+-apply plugin: 'jacoco'
+
+ dependencies {
+ compile gradleApi()
+@@ -16,13 +13,11 @@ ext {
+
+ group = 'com.google.gms'
+ archivesBaseName = 'google-services'
+-version = rootProject.ext.buildVersion
++version = '1.5.0'
+
+ project.ext.pomName = 'Gradle Plug-in for Google Services'
+ project.ext.pomDesc = 'Gradle plug-in for Google Services'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+
+ groovydoc {
+ exclude "**/internal/**"
+diff --git a/base/build-system/gradle-core/build.gradle b/base/build-system/gradle-core/build.gradle
+index 2505e6d..7e3a673 100644
+--- a/base/build-system/gradle-core/build.gradle
++++ b/base/build-system/gradle-core/build.gradle
+@@ -1,6 +1,4 @@
+ apply plugin: 'groovy'
+-apply plugin: 'jacoco'
+-apply plugin: 'clone-artifacts'
+
+ configurations {
+ provided
+@@ -36,13 +34,11 @@ tasks.compileJava.dependsOn ":setupGradleInIde"
+
+ group = 'com.android.tools.build'
+ archivesBaseName = 'gradle-core'
+-version = rootProject.ext.buildVersion
++version = '1.5.0'
+
+ project.ext.pomName = 'Core Library for Android Gradle Plug-in'
+ project.ext.pomDesc = 'Core library to build Android Gradle plugin.'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+
+ test {
+ environment("CUSTOM_REPO", rootProject.file("../out/repo"))
+diff --git a/base/build-system/gradle-experimental/build.gradle b/base/build-system/gradle-experimental/build.gradle
+index 9e9154a..ce579e0 100644
+--- a/base/build-system/gradle-experimental/build.gradle
++++ b/base/build-system/gradle-experimental/build.gradle
+@@ -1,7 +1,4 @@
+ apply plugin: 'groovy'
+-apply plugin: 'clone-artifacts'
+-apply plugin: 'idea'
+-apply plugin: 'jacoco'
+
+ // Extract gradle libraries to ensure gradle-core is compatible with older version.
+ String gradleVersion = "2.5"
+@@ -29,13 +26,11 @@ dependencies {
+
+ group = 'com.android.tools.build'
+ archivesBaseName = 'gradle-experimental'
+-version = rootProject.ext.experimentalVersion
++version = '0.4.0'
+
+ project.ext.pomName = 'Gradle Plug-in for Android Using Component Model'
+ project.ext.pomDesc = 'Gradle plug-in to build Android applications.'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+
+ jar.manifest.attributes("Plugin-Version": version)
+ jar.manifest.attributes("Inception-Date":"${Calendar.getInstance().get(Calendar.YEAR)}:" +
+diff --git a/base/build-system/gradle/build.gradle b/base/build-system/gradle/build.gradle
+index 5bcf41f..b36e23e 100644
+--- a/base/build-system/gradle/build.gradle
++++ b/base/build-system/gradle/build.gradle
+@@ -1,8 +1,4 @@
+ apply plugin: 'groovy'
+-apply plugin: 'clone-artifacts'
+-apply plugin: 'idea'
+-apply plugin: 'jacoco'
+-apply plugin: 'license-report'
+
+ dependencies {
+ compile project(':base:gradle-core')
+@@ -14,14 +10,11 @@ dependencies {
+
+ group = 'com.android.tools.build'
+ archivesBaseName = 'gradle'
+-version = rootProject.ext.buildVersion
++version = '1.5.0'
+
+ project.ext.pomName = 'Gradle Plug-in for Android'
+ project.ext.pomDesc = 'Gradle plug-in to build Android applications.'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-
+ jar.manifest.attributes("Plugin-Version": version)
+ jar.manifest.attributes("Inception-Date":"${Calendar.getInstance().get(Calendar.YEAR)}:" +
+ "${Calendar.getInstance().get(Calendar.MONTH)}:${Calendar.getInstance().get(Calendar.DATE)}")
+diff --git a/base/build-system/integration-test/build.gradle b/base/build-system/integration-test/build.gradle
+index 36a3527..ae0d7e5 100644
+--- a/base/build-system/integration-test/build.gradle
++++ b/base/build-system/integration-test/build.gradle
+@@ -1,5 +1,4 @@
+ apply plugin: 'groovy'
+-apply plugin: 'jacoco'
+
+ repositories {
+ maven { url = uri(rootProject.cloneArtifacts.repository) }
+@@ -30,8 +29,8 @@ dependencies {
+ def testEnvironment = [
+ PROJECT_BUILD_DIR: project.buildDir,
+ CUSTOM_REPO: rootProject.file("../out/repo"),
+- CUSTOM_GRADLE: System.env.CUSTOM_GRADLE ?: rootProject.ext.buildVersion,
+- CUSTOM_EXPERIMENTAL_GRADLE: System.env.CUSTOM_EXPERIMENTAL_GRADLE ?:rootProject.ext.experimentalVersion,
++ CUSTOM_GRADLE: System.env.CUSTOM_GRADLE ?: '1.5.0',
++ CUSTOM_EXPERIMENTAL_GRADLE: System.env.CUSTOM_EXPERIMENTAL_GRADLE ?:'0.4.0',
+ ANDROID_HOME: System.env.ANDROID_HOME,
+ ANDROID_NDK_HOME: System.env.ANDROID_NDK_HOME,
+ CUSTOM_BUILDTOOLS: System.env.CUSTOM_BUILDTOOLS,
+diff --git a/base/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle b/base/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle
+index 82e0baa..2dfbb57 100644
+--- a/base/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle
++++ b/base/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle
+@@ -1,5 +1,4 @@
+ apply plugin: 'java'
+-apply plugin: 'idea'
+
+ def env = new Object() {
+ String gradleVersion
+@@ -11,4 +10,4 @@ apply from: "../../commonLocalRepo.gradle"
+
+ dependencies {
+ compile "com.android.tools.build:builder-test-api:$env.gradleVersion"
+-}
+\ No newline at end of file
++}
+diff --git a/base/build-system/manifest-merger/build.gradle b/base/build-system/manifest-merger/build.gradle
+index 3ca48a0..4d3d7db 100644
+--- a/base/build-system/manifest-merger/build.gradle
++++ b/base/build-system/manifest-merger/build.gradle
+@@ -1,12 +1,12 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ evaluationDependsOn(':base:sdklib')
+
+ group = 'com.android.tools.build'
+ archivesBaseName = 'manifest-merger'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:common')
+@@ -32,7 +32,4 @@ test {
+ project.ext.pomName = 'Android Tools Manifest Merger library'
+ project.ext.pomDesc = 'A Library to merge Android manifests.'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+diff --git a/base/build-system/project-test-lib/build.gradle b/base/build-system/project-test-lib/build.gradle
+index 82ddb41..d7fc943 100644
+--- a/base/build-system/project-test-lib/build.gradle
++++ b/base/build-system/project-test-lib/build.gradle
+@@ -1,6 +1,4 @@
+ apply plugin: 'java'
+-apply plugin: 'clone-artifacts'
+-apply plugin: 'idea'
+
+ def toolingApiVersion = gradle.gradleVersion
+ dependencies {
+diff --git a/base/build-system/project-test/build.gradle b/base/build-system/project-test/build.gradle
+index b28ca5d..c4b0060 100644
+--- a/base/build-system/project-test/build.gradle
++++ b/base/build-system/project-test/build.gradle
+@@ -1,6 +1,4 @@
+ apply plugin: 'java'
+-apply plugin: 'clone-artifacts'
+-apply plugin: 'idea'
+ apply plugin: 'application'
+
+ dependencies {
+diff --git a/base/build.gradle b/base/build.gradle
+index b07f6c9..7a52da9 100644
+--- a/base/build.gradle
++++ b/base/build.gradle
+@@ -2,6 +2,5 @@ subprojects { Project project ->
+ // only configure leaf projects.
+ if (!project.getSubprojects().isEmpty()) return
+
+- apply from: "$rootDir/buildSrc/base/baseJava.gradle"
+ }
+
+diff --git a/base/chartlib/build.gradle b/base/chartlib/build.gradle
+index 24c58d6..dff471b 100644
+--- a/base/chartlib/build.gradle
++++ b/base/chartlib/build.gradle
+@@ -3,7 +3,7 @@ apply plugin: 'jacoco'
+
+ group = 'com.android.tools.chartlib'
+ archivesBaseName = 'chartlib'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:common')
+@@ -14,4 +14,3 @@ dependencies {
+ project.ext.pomName = 'Android Tools chartlib'
+ project.ext.pomDesc = 'Library with Swing chart widgets'
+
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+diff --git a/base/common/build.gradle b/base/common/build.gradle
+index 904c8f9..21ef059 100644
+--- a/base/common/build.gradle
++++ b/base/common/build.gradle
+@@ -1,6 +1,6 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ dependencies {
+ compile project(':base:annotations')
+@@ -11,11 +11,8 @@ dependencies {
+
+ group = 'com.android.tools'
+ archivesBaseName = 'common'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ project.ext.pomName = 'Android Tools common library'
+ project.ext.pomDesc = 'common library used by other Android tools libraries.'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+diff --git a/base/ddmlib/build.gradle b/base/ddmlib/build.gradle
+index fd300c1..5695891 100644
+--- a/base/ddmlib/build.gradle
++++ b/base/ddmlib/build.gradle
+@@ -1,10 +1,10 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools.ddms'
+ archivesBaseName = 'ddmlib'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:common')
+@@ -23,6 +23,3 @@ sourceSets {
+ project.ext.pomName = 'Android Tools ddmlib'
+ project.ext.pomDesc = 'Library providing APIs to talk to Android devices'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+diff --git a/base/device_validator/dvlib/build.gradle b/base/device_validator/dvlib/build.gradle
+index 4111d48..4feee01 100644
+--- a/base/device_validator/dvlib/build.gradle
++++ b/base/device_validator/dvlib/build.gradle
+@@ -1,6 +1,6 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ dependencies {
+ compile project(':base:common')
+@@ -9,14 +9,10 @@ dependencies {
+
+ group = 'com.android.tools'
+ archivesBaseName = 'dvlib'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ // configure the manifest of the sdkJar task
+-sdkJar.manifest.attributes("Main-Class": "com.android.validator.DeviceValidator")
+
+ project.ext.pomName = 'Android Tools dvlib'
+ project.ext.pomDesc = 'A Library to manage the Android device database XML files.'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+diff --git a/base/draw9patch/build.gradle b/base/draw9patch/build.gradle
+index b54a7f0..0a2fd6b 100644
+--- a/base/draw9patch/build.gradle
++++ b/base/draw9patch/build.gradle
+@@ -1,10 +1,10 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools'
+ archivesBaseName = 'draw9patch'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ testCompile 'junit:junit:4.12'
+@@ -15,16 +15,3 @@ sourceSets {
+ test.resources.srcDir 'src/test/java'
+ }
+
+-sdk {
+- linux {
+- item('etc/draw9patch') { executable true}
+- }
+- mac {
+- item('etc/draw9patch') { executable true}
+- }
+- windows {
+- item 'etc/draw9patch.bat'
+- }
+-}
+-
+-sdkJar.manifest.attributes("Main-Class": "com.android.draw9patch.Application")
+diff --git a/base/jack/jack-api/build.gradle b/base/jack/jack-api/build.gradle
+index d5bf9de..f7f2260 100644
+--- a/base/jack/jack-api/build.gradle
++++ b/base/jack/jack-api/build.gradle
+@@ -17,9 +17,6 @@ compileJava.classpath += configurations.provided
+ project.ext.pomName = 'Android Jack API'
+ project.ext.pomDesc = 'API to dynamically load Jack'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+ javadoc {
+ classpath += configurations.provided
+diff --git a/base/jack/jill-api/build.gradle b/base/jack/jill-api/build.gradle
+index 5c0edf4..5668fdc 100644
+--- a/base/jack/jill-api/build.gradle
++++ b/base/jack/jill-api/build.gradle
+@@ -17,9 +17,6 @@ compileJava.classpath += configurations.provided
+ project.ext.pomName = 'Android Jill API'
+ project.ext.pomDesc = 'API to dynamically load Jill'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+ javadoc {
+ classpath += configurations.provided
+diff --git a/base/jobb/build.gradle b/base/jobb/build.gradle
+index bf112f1..ba3eaba 100644
+--- a/base/jobb/build.gradle
++++ b/base/jobb/build.gradle
+@@ -1,5 +1,5 @@
+ apply plugin: 'java'
+-apply plugin: 'sdk-java-lib'
++
+
+ dependencies {
+ compile project(':external:fat32lib')
+@@ -7,19 +7,7 @@ dependencies {
+
+ group = 'com.android.tools.build'
+ archivesBaseName = 'jobb'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ // configure the manifest of the sdkJar task
+-sdkJar.manifest.attributes("Main-Class": "com.android.jobb.Main")
+
+-sdk {
+- linux {
+- item('etc/jobb') { executable true }
+- }
+- mac {
+- item('etc/jobb') { executable true }
+- }
+- windows {
+- item 'etc/jobb.bat'
+- }
+-}
+diff --git a/base/layoutlib-api/build.gradle b/base/layoutlib-api/build.gradle
+index 2ac4682..8fdaabc 100644
+--- a/base/layoutlib-api/build.gradle
++++ b/base/layoutlib-api/build.gradle
+@@ -1,10 +1,10 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools.layoutlib'
+ archivesBaseName = 'layoutlib-api'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:common')
+@@ -18,7 +18,4 @@ dependencies {
+ project.ext.pomName = 'Android Tools layoutlib-api'
+ project.ext.pomDesc = 'Library to use the rendering library for Android layouts: layoutlib'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+diff --git a/base/legacy/ant-tasks/build.gradle b/base/legacy/ant-tasks/build.gradle
+index 65288b6..38e6674 100644
+--- a/base/legacy/ant-tasks/build.gradle
++++ b/base/legacy/ant-tasks/build.gradle
+@@ -1,9 +1,9 @@
+ apply plugin: 'java'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools.build'
+ archivesBaseName = 'ant-tasks'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:manifest-merger')
+diff --git a/base/legacy/archquery/build.gradle b/base/legacy/archquery/build.gradle
+index f38f236..1f17700 100644
+--- a/base/legacy/archquery/build.gradle
++++ b/base/legacy/archquery/build.gradle
+@@ -1,9 +1,8 @@
+ apply plugin: 'java'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools'
+ archivesBaseName = 'archquery'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ // configure the manifest of the sdkJar task.
+-sdkJar.manifest.attributes("Main-Class": "com.android.archquery.Main")
+diff --git a/base/lint/cli/build.gradle b/base/lint/cli/build.gradle
+index 7d7fe85..5ffa9e5 100644
+--- a/base/lint/cli/build.gradle
++++ b/base/lint/cli/build.gradle
+@@ -1,10 +1,10 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools.lint'
+ archivesBaseName = 'lint'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:lint-checks')
+@@ -20,24 +20,10 @@ test {
+ maxParallelForks = Runtime.runtime.availableProcessors() / 2
+ }
+
+-sdk {
+- linux {
+- item('etc/lint') { executable true }
+- }
+- mac {
+- item('etc/lint') { executable true }
+- }
+- windows {
+- item 'etc/lint.bat'
+- }
+-}
++
+
+ // configure the manifest of the sdkJar task.
+-sdkJar.manifest.attributes("Main-Class": "com.android.tools.lint.Main")
+
+ project.ext.pomName = 'Android Lint Tool'
+ project.ext.pomDesc = 'Lint tools. Both a Command line tool and a library to add lint features to other tools'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+diff --git a/base/lint/libs/lint-api/build.gradle b/base/lint/libs/lint-api/build.gradle
+index df9d553..2ab953e 100644
+--- a/base/lint/libs/lint-api/build.gradle
++++ b/base/lint/libs/lint-api/build.gradle
+@@ -1,10 +1,10 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools.lint'
+ archivesBaseName = 'lint-api'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:sdk-common')
+@@ -23,7 +23,4 @@ sourceSets {
+ project.ext.pomName = 'Android Tools Lint API'
+ project.ext.pomDesc = 'API to build lint checks'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+diff --git a/base/lint/libs/lint-checks/build.gradle b/base/lint/libs/lint-checks/build.gradle
+index b71cb35..b5f24dc 100644
+--- a/base/lint/libs/lint-checks/build.gradle
++++ b/base/lint/libs/lint-checks/build.gradle
+@@ -1,10 +1,10 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools.lint'
+ archivesBaseName = 'lint-checks'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:lint-api')
+@@ -19,7 +19,4 @@ sourceSets {
+ project.ext.pomName = 'Android Lint Checks'
+ project.ext.pomDesc = 'Checks for Android Lint'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+diff --git a/base/lint/libs/lint-tests/build.gradle b/base/lint/libs/lint-tests/build.gradle
+index 7781b8a..c55c01f 100644
+--- a/base/lint/libs/lint-tests/build.gradle
++++ b/base/lint/libs/lint-tests/build.gradle
+@@ -3,7 +3,7 @@ apply plugin: 'jacoco'
+
+ group = 'com.android.tools.lint'
+ archivesBaseName = 'lint-tests'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:lint')
+@@ -24,7 +24,4 @@ sourceSets {
+ project.ext.pomName = 'Android Tools Lint Test API'
+ project.ext.pomDesc = 'API to build lint check tests'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+diff --git a/base/misc/screenshot2/build.gradle b/base/misc/screenshot2/build.gradle
+index 51a060f..8704ad4 100644
+--- a/base/misc/screenshot2/build.gradle
++++ b/base/misc/screenshot2/build.gradle
+@@ -1,22 +1,11 @@
+ apply plugin: 'java'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools'
+ archivesBaseName = 'screenshot2'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:ddmlib')
+ }
+
+-sdk {
+- linux {
+- item('etc/screenshot2') { executable true }
+- }
+- mac {
+- item('etc/screenshot2') { executable true }
+- }
+-}
+-
+-// configure the manifest of the sdkJar task.
+-sdkJar.manifest.attributes("Main-Class": "com.android.screenshot.Screenshot")
+diff --git a/base/ninepatch/build.gradle b/base/ninepatch/build.gradle
+index 7699e1b..17a49b6 100644
+--- a/base/ninepatch/build.gradle
++++ b/base/ninepatch/build.gradle
+@@ -1,10 +1,10 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools'
+ archivesBaseName = 'ninepatch'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ testCompile 'junit:junit:4.12'
+diff --git a/base/perflib/build.gradle b/base/perflib/build.gradle
+index a923c0d..dae11eb 100644
+--- a/base/perflib/build.gradle
++++ b/base/perflib/build.gradle
+@@ -3,7 +3,7 @@ apply plugin: 'jacoco'
+
+ group = 'com.android.tools.perflib'
+ archivesBaseName = 'perflib'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:ddmlib')
+@@ -17,4 +17,3 @@ dependencies {
+ project.ext.pomName = 'Android Tools perflib'
+ project.ext.pomDesc = 'Library to handle android performance data'
+
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+diff --git a/base/rpclib/build.gradle b/base/rpclib/build.gradle
+index bccfc24..69be33b 100644
+--- a/base/rpclib/build.gradle
++++ b/base/rpclib/build.gradle
+@@ -2,7 +2,7 @@ apply plugin: 'java'
+
+ group = 'com.android.tools.rpclib'
+ archivesBaseName = 'rpclib'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:common')
+@@ -13,4 +13,3 @@ dependencies {
+ project.ext.pomName = 'Android Tools rpclib'
+ project.ext.pomDesc = 'Library to handle Gaze RPC'
+
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+diff --git a/base/rule-api/build.gradle b/base/rule-api/build.gradle
+index ed2f763..b7937cc 100644
+--- a/base/rule-api/build.gradle
++++ b/base/rule-api/build.gradle
+@@ -1,9 +1,9 @@
+ apply plugin: 'java'
+-apply plugin: 'sdk-java-lib'
++
+
+ group = 'com.android.tools'
+ archivesBaseName = 'rule-api'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:common')
+diff --git a/base/sdk-common/build.gradle b/base/sdk-common/build.gradle
+index cc946c4..f432f69 100644
+--- a/base/sdk-common/build.gradle
++++ b/base/sdk-common/build.gradle
+@@ -1,11 +1,11 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+ apply plugin: 'application'
+
+ group = 'com.android.tools'
+ archivesBaseName = 'sdk-common'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+ mainClassName = 'com.android.ide.common.vectordrawable.VdPreview'
+
+ dependencies {
+@@ -23,7 +23,4 @@ dependencies {
+ project.ext.pomName = 'Android Tools sdk-common library'
+ project.ext.pomDesc = 'sdk-common library used by other Android tools libraries.'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+diff --git a/base/sdk-common/generate-locale-data/build.gradle b/base/sdk-common/generate-locale-data/build.gradle
+index 2531629..7535bcb 100644
+--- a/base/sdk-common/generate-locale-data/build.gradle
++++ b/base/sdk-common/generate-locale-data/build.gradle
+@@ -1,5 +1,5 @@
+ apply plugin: 'java'
+-apply plugin: 'sdk-java-lib'
++
+
+ dependencies {
+ compile project(':base:sdk-common')
+@@ -9,5 +9,4 @@ dependencies {
+
+ group = 'com.android.tools'
+ archivesBaseName = 'generate-locale-data'
+-version = rootProject.ext.baseVersion
+-sdkJar.manifest.attributes("Main-Class": "com.android.ide.common.generate.locale.LocaleTableGenerator")
++version = '24.5.0'
+diff --git a/base/sdklib/build.gradle b/base/sdklib/build.gradle
+index bc76ff4..fd3c225 100644
+--- a/base/sdklib/build.gradle
++++ b/base/sdklib/build.gradle
+@@ -1,12 +1,12 @@
+ apply plugin: 'java'
+ apply plugin: 'jacoco'
+-apply plugin: 'sdk-java-lib'
++
+
+ evaluationDependsOn(':base:dvlib')
+
+ group = 'com.android.tools'
+ archivesBaseName = 'sdklib'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:layoutlib-api')
+@@ -62,7 +62,4 @@ copyXsd.doLast {
+ project.ext.pomName = 'Android Tools sdklib'
+ project.ext.pomDesc = 'A library to parse and download the Android SDK.'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+diff --git a/base/sdklib/test.gradle b/base/sdklib/test.gradle
+index 4324376..1c402d0 100755
+--- a/base/sdklib/test.gradle
++++ b/base/sdklib/test.gradle
+@@ -5,7 +5,7 @@ evaluationDependsOn(':base:dvlib')
+
+ group = 'com.android.tools'
+ archivesBaseName = 'sdklib-test'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:sdklib')
+diff --git a/base/templates/build.gradle b/base/templates/build.gradle
+index 238c922..e69de29 100644
+--- a/base/templates/build.gradle
++++ b/base/templates/build.gradle
+@@ -1,10 +0,0 @@
+-apply plugin: 'sdk-files'
+-
+-sdk {
+- common {
+- item('eclipse/projects') { into 'templates/projects' }
+- item('eclipse/activities') { into 'templates/activities' }
+- item('eclipse/gradle') { into 'templates/gradle' }
+- item('eclipse/other') { into 'templates/other' }
+- }
+-}
+\ No newline at end of file
+diff --git a/base/testutils/build.gradle b/base/testutils/build.gradle
+index b69a4fb..8b8deb3 100644
+--- a/base/testutils/build.gradle
++++ b/base/testutils/build.gradle
+@@ -3,7 +3,7 @@ apply plugin: 'jacoco'
+
+ group = 'com.android.tools'
+ archivesBaseName = 'testutils'
+-version = rootProject.ext.baseVersion
++version = '24.5.0'
+
+ dependencies {
+ compile project(':base:common')
+@@ -19,6 +19,3 @@ sourceSets {
+ project.ext.pomName = 'Android Tools Test Utilities'
+ project.ext.pomDesc = 'API used by lint testing infrastructure'
+
+-apply from: "$rootDir/buildSrc/base/publish.gradle"
+-apply from: "$rootDir/buildSrc/base/bintray.gradle"
+-apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+diff --git a/build.gradle b/build.gradle
+index a6c30b5..bf35058 100644
+--- a/build.gradle
++++ b/build.gradle
+@@ -1,15 +1,6 @@
+-apply plugin: 'clone-artifacts'
+
+ // Currently, the minimum requirement to run Android SDK tools is Java 1.6
+ // So make sure that we are compiling with 1.6
+-task('checkJavaVersion') << {
+- def jvmVersion = System.getProperty('java.version')
+- def requiredVersion = System.getenv('JAVA_FOR_TESTS') ?: '1.6'
+- if (!jvmVersion.startsWith(requiredVersion)) {
+- throw new RuntimeException("Tools need to be compiled with Java $requiredVersion, you are using Java $jvmVersion.")
+- }
+-}
+-final def checkJavaVersionTask = tasks['checkJavaVersion']
+
+ allprojects { subproject ->
+ tasks.withType(JavaForkOptions) {
+@@ -23,15 +14,10 @@ allprojects { subproject ->
+ final def buildTasks = ['build', 'compileJava', 'compileGroovy', 'classes', 'assemble', 'javadoc', 'groovydoc', 'check']
+ // Task.doFirst does not run if the task has no work to do. Need to be more aggressive than that.
+ // Some projects won't have all of these tasks, so need to use findByName.
+- buildTasks.each { subproject.tasks.findByName(it)?.dependsOn(checkJavaVersionTask) }
++ buildTasks.each { subproject.tasks.findByName(it) }
+ }
+ }
+
+-// artifact cloning destinations
+-cloneArtifacts {
+- repository = "$rootDir/../prebuilts/tools/common/m2/repository"
+-}
+-
+ if (System.env.USE_EXTERNAL_REPO != null) {
+ allprojects {
+ repositories {
+@@ -55,7 +41,7 @@ if (System.env.DIST_DIR != null && System.env.OUT_DIR != null) {
+ ext.androidHostDist = file(System.env.DIST_DIR)
+ } else {
+ // ext.androidHostOut is shared by all tools/{base,build,swt} gradle projects/
+- ext.androidHostOut = file("$rootDir/../out")
++ ext.androidHostOut = file("$rootDir/out")
+ ext.androidHostDist = new File(ext.androidHostOut, "dist")
+ }
+
+@@ -64,14 +50,9 @@ if (System.env.DIST_DIR != null && System.env.OUT_DIR != null) {
+ buildDir = new File(ext.androidHostOut, "build/root")
+
+
+-// apply this after the buildDir has been changed.
+-apply plugin: 'sdk-tools'
+-
+
+ ext.localRepo = project.hasProperty('localRepo') ? localRepo : "$ext.androidHostOut/repo"
+
+-apply from: "$rootDir/buildSrc/base/version.gradle"
+-
+ subprojects { Project project ->
+ // Change buildDir first so that all plugins pick up the new value.
+ project.buildDir = project.file("$project.parent.buildDir/../$project.name/build")
+@@ -117,17 +98,12 @@ task setupGradleInIde << {
+ if (gradleDistLink.exists()) {
+ gradleDistLink.delete()
+ }
+- String link = dir.path.substring(project.ext.androidHostOut.path.length() + 1)
+- String command = "ln -s $link ${gradleDistLink.path}"
+- command.execute()
+ }
+
+ // basic task for custom distribution of project should the build server.
+ task dist << {
+ }
+
+-apply plugin: 'offline-repo'
+-
+ task clean << {
+ delete 'build'
+
+@@ -137,7 +113,6 @@ task clean << {
+ }
+ }
+ }
+-apply plugin: 'presubmit-runner'
+ // Task for initializing a fresh repo.
+ task init {
+ dependsOn prepareRepo
+@@ -150,4 +125,3 @@ task init {
+ // The hierarchy viewer tests require loading SWT jar files.
+ // That fails with the error saying "can't load 32 bit library on 64 bit JVM
+ // Disable these tests from running until that is fixed
+-tasks.findByPath(':swt:hierarchyviewer2lib:test').enabled = false
diff --git a/debian/patches/disable-lint.patch b/debian/patches/disable-lint.patch
new file mode 100644
index 0000000..c913129
--- /dev/null
+++ b/debian/patches/disable-lint.patch
@@ -0,0 +1,9620 @@
+From: Markus Koschany <apo at debian.org>
+Date: Wed, 17 Feb 2016 18:55:28 +0100
+Subject: disable lint
+
+Temporarily necessary until lombok-ast is available in Debian and Lint can be
+built from source.
+
+Forwarded: not-needed
+---
+ .../com/android/builder/model/AndroidProject.java | 6 -
+ base/build-system/gradle-core/build.gradle | 2 +-
+ .../com/android/build/gradle/AndroidConfig.java | 4 -
+ .../gradle/internal/ApplicationTaskManager.java | 1 -
+ .../build/gradle/internal/LibraryTaskManager.java | 73 +-
+ .../build/gradle/internal/LintGradleClient.java | 198 --
+ .../build/gradle/internal/LintGradleProject.java | 658 -----
+ .../build/gradle/internal/LintGradleRequest.java | 69 -
+ .../android/build/gradle/internal/TaskManager.java | 108 +-
+ .../build/gradle/internal/dsl/LintOptions.java | 798 ------
+ .../internal/model/DefaultAndroidProject.java | 11 -
+ .../build/gradle/internal/model/ModelBuilder.java | 5 -
+ .../internal/variant/LibraryVariantData.java | 15 +-
+ .../build/gradle/tasks/ExtractAnnotations.groovy | 265 --
+ .../build/gradle/tasks/GroovyGradleDetector.java | 242 --
+ .../com/android/build/gradle/tasks/Lint.groovy | 295 ---
+ .../build/gradle/tasks/PackageApplication.java | 46 -
+ .../build/gradle/tasks/ResourceUsageAnalyzer.java | 2568 --------------------
+ .../build/gradle/tasks/ShrinkResources.groovy | 220 --
+ .../gradle/tasks/annotations/ApiDatabase.java | 356 ---
+ .../annotations/ExtractAnnotationsDriver.java | 400 ---
+ .../build/gradle/tasks/annotations/Extractor.java | 2484 -------------------
+ .../gradle/tasks/annotations/TypedefCollector.java | 154 --
+ .../gradle/tasks/annotations/TypedefRemover.java | 164 --
+ .../com/android/build/gradle/BaseExtension.java | 19 -
+ 25 files changed, 4 insertions(+), 9157 deletions(-)
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleClient.java
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleProject.java
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleRequest.java
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LintOptions.java
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GroovyGradleDetector.java
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ApiDatabase.java
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
+ delete mode 100644 base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java
+
+diff --git a/base/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java b/base/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
+index 068f365..2dfa09c 100644
+--- a/base/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
++++ b/base/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
+@@ -185,12 +185,6 @@ public interface AndroidProject {
+ AaptOptions getAaptOptions();
+
+ /**
+- * Returns the lint options.
+- */
+- @NonNull
+- LintOptions getLintOptions();
+-
+- /**
+ * Returns the dependencies that were not successfully resolved. The returned list gets
+ * populated only if the system property {@link #PROPERTY_BUILD_MODEL_ONLY} has been
+ * set to {@code true}.
+diff --git a/base/build-system/gradle-core/build.gradle b/base/build-system/gradle-core/build.gradle
+index e36483b..f8ed473 100644
+--- a/base/build-system/gradle-core/build.gradle
++++ b/base/build-system/gradle-core/build.gradle
+@@ -16,7 +16,7 @@ ext.proguardVersion = "5.2.1"
+
+ dependencies {
+ compile project(':base:builder')
+- compile project(':base:lint')
++ //compile project(':base:lint')
+ compile "net.sf.proguard:proguard-gradle:${project.ext.proguardVersion}"
+
+ // Add gradleApi to classpath for compilation, but use provided configuration so that groovy is
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java
+index ec7373f..46eca5e 100644
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java
++++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java
+@@ -26,7 +26,6 @@ import com.android.build.gradle.internal.dsl.AdbOptions;
+ import com.android.build.gradle.internal.dsl.CoreBuildType;
+ import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+ import com.android.build.gradle.internal.dsl.DexOptions;
+-import com.android.build.gradle.internal.dsl.LintOptions;
+ import com.android.build.gradle.internal.dsl.PackagingOptions;
+ import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+ import com.android.build.gradle.internal.dsl.Splits;
+@@ -99,9 +98,6 @@ public interface AndroidConfig {
+ /** JaCoCo options. */
+ JacocoExtension getJacoco();
+
+- /** Lint options. */
+- LintOptions getLintOptions();
+-
+ /** Packaging options. */
+ PackagingOptions getPackagingOptions();
+
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java
+index 649c43e..3badeb9 100644
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java
++++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java
+@@ -216,7 +216,6 @@ public class ApplicationTaskManager extends TaskManager {
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+- createLintTasks(tasks, variantScope);
+ return null;
+ }
+ });
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java
+index 2f2c7a3..f2af17d 100644
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java
++++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java
+@@ -36,7 +36,7 @@ import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+ import com.android.build.gradle.internal.variant.LibVariantOutputData;
+ import com.android.build.gradle.internal.variant.LibraryVariantData;
+ import com.android.build.gradle.internal.variant.VariantHelper;
+-import com.android.build.gradle.tasks.ExtractAnnotations;
++//import com.android.build.gradle.tasks.ExtractAnnotations;
+ import com.android.build.gradle.tasks.MergeResources;
+ import com.android.builder.core.AndroidBuilder;
+ import com.android.builder.core.BuilderConstants;
+@@ -306,26 +306,9 @@ public class LibraryTaskManager extends TaskManager {
+
+ });
+
+- // copy lint.jar into the bundle folder
+- Copy lintCopy = project.getTasks().create(
+- variantScope.getTaskName("copy", "Lint"), Copy.class);
+- lintCopy.dependsOn(LINT_COMPILE);
+- lintCopy.from(new File(
+- variantScope.getGlobalScope().getIntermediatesDir(),
+- "lint/lint.jar"));
+- lintCopy.into(new File(
+- variantScope.getGlobalScope().getIntermediatesDir(),
+- DIR_BUNDLES + "/" + dirName));
+
+ final Zip bundle = project.getTasks().create(variantScope.getTaskName("bundle"), Zip.class);
+
+- if (variantData.getVariantDependency().isAnnotationsPresent()) {
+- libVariantData.generateAnnotationsTask =
+- createExtractAnnotations(project, variantData);
+- }
+- if (libVariantData.generateAnnotationsTask != null) {
+- bundle.dependsOn(libVariantData.generateAnnotationsTask);
+- }
+
+ final boolean instrumented = variantConfig.getBuildType().isTestCoverageEnabled();
+
+@@ -438,18 +421,11 @@ public class LibraryTaskManager extends TaskManager {
+ jar.exclude(packageName + "/Manifest$*.class");
+ jar.exclude(packageName + "/BuildConfig.class");
+ }
+-
+- if (libVariantData.generateAnnotationsTask != null) {
+- // In case extract annotations strips out private typedef annotation classes
+- jar.dependsOn(libVariantData.generateAnnotationsTask);
+- }
+ return null;
+ }
+ });
+ }
+
+- bundle.dependsOn(packageRes.getName(), packageRenderscript, lintCopy, packageJniLibs,
+- mergeProGuardFileTask);
+ TaskManager.optionalDependsOn(bundle, pcData.getClassGeneratingTasks());
+ TaskManager.optionalDependsOn(bundle, pcData.getLibraryGeneratingTasks());
+
+@@ -551,58 +527,11 @@ public class LibraryTaskManager extends TaskManager {
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+- createLintTasks(tasks, variantScope);
+ return null;
+ }
+ });
+ }
+
+- public ExtractAnnotations createExtractAnnotations(
+- final Project project,
+- final BaseVariantData variantData) {
+- final GradleVariantConfiguration config = variantData.getVariantConfiguration();
+-
+- final ExtractAnnotations task = project.getTasks().create(
+- variantData.getScope().getTaskName("extract", "Annotations"),
+- ExtractAnnotations.class);
+- task.setDescription(
+- "Extracts Android annotations for the " + variantData.getVariantConfiguration()
+- .getFullName()
+- + " variant into the archive file");
+- task.setGroup(BasePlugin.BUILD_GROUP);
+- task.variant = variantData;
+- task.setDestinationDir(new File(
+- variantData.getScope().getGlobalScope().getIntermediatesDir(),
+- ANNOTATIONS + "/" + config.getDirName()));
+- task.output = new File(task.getDestinationDir(), FN_ANNOTATIONS_ZIP);
+- task.classDir = new File(variantData.getScope().getGlobalScope().getIntermediatesDir(),
+- "classes/" + variantData.getVariantConfiguration().getDirName());
+- task.setSource(variantData.getJavaSources());
+- task.encoding = getExtension().getCompileOptions().getEncoding();
+- task.setSourceCompatibility(
+- getExtension().getCompileOptions().getSourceCompatibility().toString());
+- ConventionMappingHelper.map(task, "classpath", new Callable<ConfigurableFileCollection>() {
+- @Override
+- public ConfigurableFileCollection call() throws Exception {
+- return project.files(androidBuilder.getCompileClasspath(config));
+- }
+- });
+- task.dependsOn(variantData.getScope().getJavacTask().getName());
+-
+- // Setup the boot classpath just before the task actually runs since this will
+- // force the sdk to be parsed. (Same as in compileTask)
+- task.doFirst(new Action<Task>() {
+- @Override
+- public void execute(Task task) {
+- if (task instanceof ExtractAnnotations) {
+- ExtractAnnotations extractAnnotations = (ExtractAnnotations) task;
+- extractAnnotations.bootClasspath = androidBuilder.getBootClasspathAsStrings();
+- }
+- }
+- });
+-
+- return task;
+- }
+
+ private Task getAssembleDefault() {
+ if (assembleDefault == null) {
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleClient.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleClient.java
+deleted file mode 100644
+index 34d2240..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleClient.java
++++ /dev/null
+@@ -1,198 +0,0 @@
+-/*
+- * Copyright (C) 2013 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.build.gradle.internal;
+-
+-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
+-import static java.io.File.separator;
+-
+-import com.android.annotations.NonNull;
+-import com.android.annotations.Nullable;
+-import com.android.builder.model.AndroidProject;
+-import com.android.builder.model.Variant;
+-import com.android.tools.lint.LintCliClient;
+-import com.android.tools.lint.LintCliFlags;
+-import com.android.tools.lint.Warning;
+-import com.android.tools.lint.client.api.IssueRegistry;
+-import com.android.tools.lint.client.api.LintRequest;
+-import com.android.tools.lint.detector.api.Issue;
+-import com.android.tools.lint.detector.api.Project;
+-import com.google.common.collect.Lists;
+-import com.google.common.collect.Maps;
+-import com.google.common.collect.Sets;
+-
+-import java.io.File;
+-import java.io.IOException;
+-import java.util.Collections;
+-import java.util.List;
+-import java.util.Map;
+-
+-public class LintGradleClient extends LintCliClient {
+- private final AndroidProject mModelProject;
+- private final String mVariantName;
+- private final org.gradle.api.Project mGradleProject;
+- private List<File> mCustomRules = Lists.newArrayList();
+- private File mSdkHome;
+-
+- public LintGradleClient(
+- @NonNull IssueRegistry registry,
+- @NonNull LintCliFlags flags,
+- @NonNull org.gradle.api.Project gradleProject,
+- @NonNull AndroidProject modelProject,
+- @Nullable File sdkHome,
+- @Nullable String variantName) {
+- super(flags);
+- mGradleProject = gradleProject;
+- mModelProject = modelProject;
+- mVariantName = variantName;
+- mSdkHome = sdkHome;
+- mRegistry = registry;
+- }
+-
+- public void setCustomRules(List<File> customRules) {
+- mCustomRules = customRules;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> findRuleJars(@NonNull Project project) {
+- return mCustomRules;
+- }
+-
+- @NonNull
+- @Override
+- protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+- // Should not be called by lint since we supply an explicit set of projects
+- // to the LintRequest
+- throw new IllegalStateException();
+- }
+-
+- @Override
+- public File getSdkHome() {
+- if (mSdkHome != null) {
+- return mSdkHome;
+- }
+- return super.getSdkHome();
+- }
+-
+- @Override
+- @Nullable
+- public File getCacheDir(boolean create) {
+- File dir = new File(mGradleProject.getRootProject().getBuildDir(),
+- FD_INTERMEDIATES + separator + "lint-cache"); //$NON-NLS-1$
+- if (dir.exists() || create && dir.mkdirs()) {
+- return dir;
+- }
+-
+- return super.getCacheDir(create);
+- }
+-
+- @Override
+- @NonNull
+- protected LintRequest createLintRequest(@NonNull List<File> files) {
+- return new LintGradleRequest(this, mModelProject, mGradleProject, mVariantName, files);
+- }
+-
+- /** Run lint with the given registry and return the resulting warnings */
+- @NonNull
+- public List<Warning> run(@NonNull IssueRegistry registry) throws IOException {
+- run(registry, Collections.<File>emptyList());
+- return mWarnings;
+- }
+-
+- /**
+- * Given a list of results from separate variants, merge them into a single
+- * list of warnings, and mark their
+- * @param warningMap a map from variant to corresponding warnings
+- * @param project the project model
+- * @return a merged list of issues
+- */
+- @NonNull
+- public static List<Warning> merge(
+- @NonNull Map<Variant,List<Warning>> warningMap,
+- @NonNull AndroidProject project) {
+- // Easy merge?
+- if (warningMap.size() == 1) {
+- return warningMap.values().iterator().next();
+- }
+- int maxCount = 0;
+- for (List<Warning> warnings : warningMap.values()) {
+- int size = warnings.size();
+- maxCount = Math.max(size, maxCount);
+- }
+- if (maxCount == 0) {
+- return Collections.emptyList();
+- }
+-
+- int totalVariantCount = project.getVariants().size();
+-
+- List<Warning> merged = Lists.newArrayListWithExpectedSize(2 * maxCount);
+-
+- // Map fro issue to message to line number to file name to canonical warning
+- Map<Issue,Map<String, Map<Integer, Map<String, Warning>>>> map =
+- Maps.newHashMapWithExpectedSize(2 * maxCount);
+-
+- for (Map.Entry<Variant,List<Warning>> entry : warningMap.entrySet()) {
+- Variant variant = entry.getKey();
+- List<Warning> warnings = entry.getValue();
+- for (Warning warning : warnings) {
+- Map<String,Map<Integer,Map<String,Warning>>> messageMap = map.get(warning.issue);
+- if (messageMap == null) {
+- messageMap = Maps.newHashMap();
+- map.put(warning.issue, messageMap);
+- }
+- Map<Integer, Map<String, Warning>> lineMap = messageMap.get(warning.message);
+- if (lineMap == null) {
+- lineMap = Maps.newHashMap();
+- messageMap.put(warning.message, lineMap);
+- }
+- Map<String, Warning> fileMap = lineMap.get(warning.line);
+- if (fileMap == null) {
+- fileMap = Maps.newHashMap();
+- lineMap.put(warning.line, fileMap);
+- }
+- String fileName = warning.file != null ? warning.file.getName() : "<unknown>";
+- Warning canonical = fileMap.get(fileName);
+- if (canonical == null) {
+- canonical = warning;
+- fileMap.put(fileName, canonical);
+- canonical.variants = Sets.newHashSet();
+- canonical.gradleProject = project;
+- merged.add(canonical);
+- }
+- canonical.variants.add(variant);
+- }
+- }
+-
+- // Clear out variants on any nodes that define all
+- for (Warning warning : merged) {
+- if (warning.variants != null && warning.variants.size() == totalVariantCount) {
+- // If this error is present in all variants, just clear it out
+- warning.variants = null;
+- }
+-
+- }
+-
+- Collections.sort(merged);
+- return merged;
+- }
+-
+- @Override
+- protected void addProgressPrinter() {
+- // No progress printing from the Gradle lint task; gradle tasks
+- // do not really do that, even for long-running jobs.
+- }
+-}
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleProject.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleProject.java
+deleted file mode 100644
+index 8193a8e..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleProject.java
++++ /dev/null
+@@ -1,658 +0,0 @@
+-package com.android.build.gradle.internal;
+-
+-import static com.android.SdkConstants.APPCOMPAT_LIB_ARTIFACT;
+-import static com.android.SdkConstants.SUPPORT_LIB_ARTIFACT;
+-import static java.io.File.separatorChar;
+-
+-import com.android.annotations.NonNull;
+-import com.android.annotations.Nullable;
+-import com.android.builder.model.AndroidArtifact;
+-import com.android.builder.model.AndroidLibrary;
+-import com.android.builder.model.AndroidProject;
+-import com.android.builder.model.ApiVersion;
+-import com.android.builder.model.BuildTypeContainer;
+-import com.android.builder.model.Dependencies;
+-import com.android.builder.model.JavaLibrary;
+-import com.android.builder.model.ProductFlavor;
+-import com.android.builder.model.ProductFlavorContainer;
+-import com.android.builder.model.SourceProvider;
+-import com.android.builder.model.SourceProviderContainer;
+-import com.android.builder.model.Variant;
+-import com.android.sdklib.AndroidTargetHash;
+-import com.android.sdklib.AndroidVersion;
+-import com.android.tools.lint.detector.api.LintUtils;
+-import com.android.tools.lint.detector.api.Project;
+-import com.android.utils.Pair;
+-import com.android.utils.XmlUtils;
+-import com.google.common.base.Charsets;
+-import com.google.common.collect.Lists;
+-import com.google.common.collect.Sets;
+-import com.google.common.io.Files;
+-
+-import org.w3c.dom.Document;
+-
+-import java.io.File;
+-import java.io.IOException;
+-import java.util.ArrayList;
+-import java.util.Collection;
+-import java.util.Collections;
+-import java.util.List;
+-import java.util.Set;
+-
+-/**
+- * An implementation of Lint's {@link Project} class wrapping a Gradle model (project or
+- * library)
+- */
+-public class LintGradleProject extends Project {
+- protected AndroidVersion mMinSdkVersion;
+- protected AndroidVersion mTargetSdkVersion;
+-
+- private LintGradleProject(
+- @NonNull LintGradleClient client,
+- @NonNull File dir,
+- @NonNull File referenceDir,
+- @NonNull File manifest) {
+- super(client, dir, referenceDir);
+- mGradleProject = true;
+- mMergeManifests = true;
+- mDirectLibraries = Lists.newArrayList();
+- readManifest(manifest);
+- }
+-
+- /**
+- * Creates a {@link com.android.build.gradle.internal.LintGradleProject} from
+- * the given {@link com.android.builder.model.AndroidProject} definition for
+- * a given {@link com.android.builder.model.Variant}, and returns it along with
+- * a set of lint custom rule jars applicable for the given model project.
+- *
+- * @param client the client
+- * @param project the model project
+- * @param variant the variant
+- * @param gradleProject the gradle project
+- * @return a pair of new project and list of custom rule jars
+- */
+- @NonNull
+- public static Pair<LintGradleProject, List<File>> create(
+- @NonNull LintGradleClient client,
+- @NonNull AndroidProject project,
+- @NonNull Variant variant,
+- @NonNull org.gradle.api.Project gradleProject) {
+- File dir = gradleProject.getProjectDir();
+- AppGradleProject lintProject = new AppGradleProject(client, dir,
+- dir, project, variant);
+-
+- List<File> customRules = Lists.newArrayList();
+- File appLintJar = new File(gradleProject.getBuildDir(),
+- "lint" + separatorChar + "lint.jar");
+- if (appLintJar.exists()) {
+- customRules.add(appLintJar);
+- }
+-
+- Set<AndroidLibrary> libraries = Sets.newHashSet();
+- Dependencies dependencies = variant.getMainArtifact().getDependencies();
+- for (AndroidLibrary library : dependencies.getLibraries()) {
+- lintProject.addDirectLibrary(createLibrary(client, library, libraries, customRules));
+- }
+-
+- return Pair.<LintGradleProject,List<File>>of(lintProject, customRules);
+- }
+-
+- @Override
+- protected void initialize() {
+- // Deliberately not calling super; that code is for ADT compatibility
+- }
+-
+- protected void readManifest(File manifest) {
+- if (manifest.exists()) {
+- try {
+- String xml = Files.toString(manifest, Charsets.UTF_8);
+- Document document = XmlUtils.parseDocumentSilently(xml, true);
+- if (document != null) {
+- readManifest(document);
+- }
+- } catch (IOException e) {
+- mClient.log(e, "Could not read manifest %1$s", manifest);
+- }
+- }
+- }
+-
+- @Override
+- public boolean isGradleProject() {
+- return true;
+- }
+-
+- protected static boolean dependsOn(@NonNull Dependencies dependencies,
+- @NonNull String artifact) {
+- for (AndroidLibrary library : dependencies.getLibraries()) {
+- if (dependsOn(library, artifact)) {
+- return true;
+- }
+- }
+- return false;
+- }
+-
+- protected static boolean dependsOn(@NonNull AndroidLibrary library, @NonNull String artifact) {
+- if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
+- if (library.getJarFile().getName().startsWith("support-v4-")) {
+- return true;
+- }
+-
+- } else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
+- File bundle = library.getBundle();
+- if (bundle.getName().startsWith("appcompat-v7-")) {
+- return true;
+- }
+- }
+-
+- for (AndroidLibrary dependency : library.getLibraryDependencies()) {
+- if (dependsOn(dependency, artifact)) {
+- return true;
+- }
+- }
+-
+- return false;
+- }
+-
+- void addDirectLibrary(@NonNull Project project) {
+- mDirectLibraries.add(project);
+- }
+-
+- @NonNull
+- private static LibraryProject createLibrary(@NonNull LintGradleClient client,
+- @NonNull AndroidLibrary library,
+- @NonNull Set<AndroidLibrary> seen, List<File> customRules) {
+- seen.add(library);
+- File dir = library.getFolder();
+- LibraryProject project = new LibraryProject(client, dir, dir, library);
+-
+- File ruleJar = library.getLintJar();
+- if (ruleJar.exists()) {
+- customRules.add(ruleJar);
+- }
+-
+- for (AndroidLibrary dependent : library.getLibraryDependencies()) {
+- if (!seen.contains(dependent)) {
+- project.addDirectLibrary(createLibrary(client, dependent, seen, customRules));
+- }
+- }
+-
+- return project;
+- }
+-
+- private static class AppGradleProject extends LintGradleProject {
+- private AndroidProject mProject;
+- private Variant mVariant;
+- private List<SourceProvider> mProviders;
+- private List<SourceProvider> mTestProviders;
+-
+- private AppGradleProject(
+- @NonNull LintGradleClient client,
+- @NonNull File dir,
+- @NonNull File referenceDir,
+- @NonNull AndroidProject project,
+- @NonNull Variant variant) {
+- //TODO FIXME: handle multi-apk
+- super(client, dir, referenceDir,
+- variant.getMainArtifact().getOutputs().iterator().next().getGeneratedManifest());
+-
+- mProject = project;
+- mVariant = variant;
+- }
+-
+- @Override
+- public boolean isLibrary() {
+- return mProject.isLibrary();
+- }
+-
+- @Override
+- public AndroidProject getGradleProjectModel() {
+- return mProject;
+- }
+-
+- @Override
+- public Variant getCurrentVariant() {
+- return mVariant;
+- }
+-
+- private List<SourceProvider> getSourceProviders() {
+- if (mProviders == null) {
+- List<SourceProvider> providers = Lists.newArrayList();
+- AndroidArtifact mainArtifact = mVariant.getMainArtifact();
+-
+- providers.add(mProject.getDefaultConfig().getSourceProvider());
+-
+- for (String flavorName : mVariant.getProductFlavors()) {
+- for (ProductFlavorContainer flavor : mProject.getProductFlavors()) {
+- if (flavorName.equals(flavor.getProductFlavor().getName())) {
+- providers.add(flavor.getSourceProvider());
+- break;
+- }
+- }
+- }
+-
+- SourceProvider multiProvider = mainArtifact.getMultiFlavorSourceProvider();
+- if (multiProvider != null) {
+- providers.add(multiProvider);
+- }
+-
+- String buildTypeName = mVariant.getBuildType();
+- for (BuildTypeContainer buildType : mProject.getBuildTypes()) {
+- if (buildTypeName.equals(buildType.getBuildType().getName())) {
+- providers.add(buildType.getSourceProvider());
+- break;
+- }
+- }
+-
+- SourceProvider variantProvider = mainArtifact.getVariantSourceProvider();
+- if (variantProvider != null) {
+- providers.add(variantProvider);
+- }
+-
+- mProviders = providers;
+- }
+-
+- return mProviders;
+- }
+-
+- private List<SourceProvider> getTestSourceProviders() {
+- if (mTestProviders == null) {
+- List<SourceProvider> providers = Lists.newArrayList();
+-
+- ProductFlavorContainer defaultConfig = mProject.getDefaultConfig();
+- for (SourceProviderContainer extra : defaultConfig.getExtraSourceProviders()) {
+- String artifactName = extra.getArtifactName();
+- if (AndroidProject.ARTIFACT_ANDROID_TEST.equals(artifactName)) {
+- providers.add(extra.getSourceProvider());
+- }
+- }
+-
+- for (String flavorName : mVariant.getProductFlavors()) {
+- for (ProductFlavorContainer flavor : mProject.getProductFlavors()) {
+- if (flavorName.equals(flavor.getProductFlavor().getName())) {
+- for (SourceProviderContainer extra : flavor.getExtraSourceProviders()) {
+- String artifactName = extra.getArtifactName();
+- if (AndroidProject.ARTIFACT_ANDROID_TEST.equals(artifactName)) {
+- providers.add(extra.getSourceProvider());
+- }
+- }
+- }
+- }
+- }
+-
+- String buildTypeName = mVariant.getBuildType();
+- for (BuildTypeContainer buildType : mProject.getBuildTypes()) {
+- if (buildTypeName.equals(buildType.getBuildType().getName())) {
+- for (SourceProviderContainer extra : buildType.getExtraSourceProviders()) {
+- String artifactName = extra.getArtifactName();
+- if (AndroidProject.ARTIFACT_ANDROID_TEST.equals(artifactName)) {
+- providers.add(extra.getSourceProvider());
+- }
+- }
+- }
+- }
+-
+- mTestProviders = providers;
+- }
+-
+- return mTestProviders;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getManifestFiles() {
+- if (mManifestFiles == null) {
+- mManifestFiles = Lists.newArrayList();
+- for (SourceProvider provider : getSourceProviders()) {
+- File manifestFile = provider.getManifestFile();
+- if (manifestFile.exists()) { // model returns path whether or not it exists
+- mManifestFiles.add(manifestFile);
+- }
+- }
+- }
+-
+- return mManifestFiles;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getProguardFiles() {
+- if (mProguardFiles == null) {
+- ProductFlavor flavor = mProject.getDefaultConfig().getProductFlavor();
+- mProguardFiles = Lists.newArrayList();
+- for (File file : flavor.getProguardFiles()) {
+- if (file.exists()) {
+- mProguardFiles.add(file);
+- }
+- }
+- try {
+- for (File file : flavor.getConsumerProguardFiles()) {
+- if (file.exists()) {
+- mProguardFiles.add(file);
+- }
+- }
+- } catch (Throwable t) {
+- // On some models, this threw
+- // org.gradle.tooling.model.UnsupportedMethodException:
+- // Unsupported method: BaseConfig.getConsumerProguardFiles().
+- // Playing it safe for a while.
+- }
+- }
+-
+- return mProguardFiles;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getResourceFolders() {
+- if (mResourceFolders == null) {
+- mResourceFolders = Lists.newArrayList();
+- for (SourceProvider provider : getSourceProviders()) {
+- Collection<File> resDirs = provider.getResDirectories();
+- for (File res : resDirs) {
+- if (res.exists()) { // model returns path whether or not it exists
+- mResourceFolders.add(res);
+- }
+- }
+- }
+-
+- for (File file : mVariant.getMainArtifact().getGeneratedResourceFolders()) {
+- if (file.exists()) {
+- mResourceFolders.add(file);
+- }
+- }
+-
+- }
+-
+- return mResourceFolders;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getJavaSourceFolders() {
+- if (mJavaSourceFolders == null) {
+- mJavaSourceFolders = Lists.newArrayList();
+- for (SourceProvider provider : getSourceProviders()) {
+- Collection<File> srcDirs = provider.getJavaDirectories();
+- for (File srcDir : srcDirs) {
+- if (srcDir.exists()) { // model returns path whether or not it exists
+- mJavaSourceFolders.add(srcDir);
+- }
+- }
+- }
+-
+- for (File file : mVariant.getMainArtifact().getGeneratedSourceFolders()) {
+- if (file.exists()) {
+- mJavaSourceFolders.add(file);
+- }
+- }
+- }
+-
+- return mJavaSourceFolders;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getTestSourceFolders() {
+- if (mTestSourceFolders == null) {
+- mTestSourceFolders = Lists.newArrayList();
+- for (SourceProvider provider : getTestSourceProviders()) {
+- Collection<File> srcDirs = provider.getJavaDirectories();
+- for (File srcDir : srcDirs) {
+- if (srcDir.exists()) { // model returns path whether or not it exists
+- mTestSourceFolders.add(srcDir);
+- }
+- }
+- }
+- }
+-
+- return mTestSourceFolders;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getJavaClassFolders() {
+- if (mJavaClassFolders == null) {
+- mJavaClassFolders = new ArrayList<File>(1);
+- File outputClassFolder = mVariant.getMainArtifact().getClassesFolder();
+- if (outputClassFolder.exists()) {
+- mJavaClassFolders.add(outputClassFolder);
+- }
+- }
+-
+- return mJavaClassFolders;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getJavaLibraries() {
+- if (mJavaLibraries == null) {
+- Collection<JavaLibrary> libs = mVariant.getMainArtifact().getDependencies().getJavaLibraries();
+- mJavaLibraries = Lists.newArrayListWithExpectedSize(libs.size());
+- for (JavaLibrary lib : libs) {
+- File jar = lib.getJarFile();
+- if (jar.exists()) {
+- mJavaLibraries.add(jar);
+- }
+- }
+- }
+- return mJavaLibraries;
+- }
+-
+- @Nullable
+- @Override
+- public String getPackage() {
+- // For now, lint only needs the manifest package; not the potentially variant specific
+- // package. As part of the Gradle work on the Lint API we should make two separate
+- // package lookup methods -- one for the manifest package, one for the build package
+- if (mPackage == null) { // only used as a fallback in case manifest somehow is null
+- String packageName = mProject.getDefaultConfig().getProductFlavor().getApplicationId();
+- if (packageName != null) {
+- return packageName;
+- }
+- }
+-
+- return mPackage; // from manifest
+- }
+-
+- @Override
+- @NonNull
+- public AndroidVersion getMinSdkVersion() {
+- if (mMinSdkVersion == null) {
+- ApiVersion minSdk = mVariant.getMergedFlavor().getMinSdkVersion();
+- if (minSdk == null) {
+- ProductFlavor flavor = mProject.getDefaultConfig().getProductFlavor();
+- minSdk = flavor.getMinSdkVersion();
+- }
+- if (minSdk != null) {
+- mMinSdkVersion = LintUtils.convertVersion(minSdk, mClient.getTargets());
+- } else {
+- mMinSdkVersion = super.getMinSdkVersion(); // from manifest
+- }
+- }
+-
+- return mMinSdkVersion;
+- }
+-
+- @Override
+- @NonNull
+- public AndroidVersion getTargetSdkVersion() {
+- if (mTargetSdkVersion == null) {
+- ApiVersion targetSdk = mVariant.getMergedFlavor().getTargetSdkVersion();
+- if (targetSdk == null) {
+- ProductFlavor flavor = mProject.getDefaultConfig().getProductFlavor();
+- targetSdk = flavor.getTargetSdkVersion();
+- }
+- if (targetSdk != null) {
+- mTargetSdkVersion = LintUtils.convertVersion(targetSdk, mClient.getTargets());
+- } else {
+- mTargetSdkVersion = super.getTargetSdkVersion(); // from manifest
+- }
+- }
+-
+- return mTargetSdkVersion;
+- }
+-
+- @Override
+- public int getBuildSdk() {
+- String compileTarget = mProject.getCompileTarget();
+- AndroidVersion version = AndroidTargetHash.getPlatformVersion(compileTarget);
+- if (version != null) {
+- return version.getApiLevel();
+- }
+-
+- return super.getBuildSdk();
+- }
+-
+- @Nullable
+- @Override
+- public Boolean dependsOn(@NonNull String artifact) {
+- if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
+- if (mSupportLib == null) {
+- Dependencies dependencies = mVariant.getMainArtifact().getDependencies();
+- mSupportLib = dependsOn(dependencies, artifact);
+- }
+- return mSupportLib;
+- } else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
+- if (mAppCompat == null) {
+- Dependencies dependencies = mVariant.getMainArtifact().getDependencies();
+- mAppCompat = dependsOn(dependencies, artifact);
+- }
+- return mAppCompat;
+- } else {
+- return super.dependsOn(artifact);
+- }
+- }
+- }
+-
+- private static class LibraryProject extends LintGradleProject {
+- private AndroidLibrary mLibrary;
+-
+- private LibraryProject(
+- @NonNull LintGradleClient client,
+- @NonNull File dir,
+- @NonNull File referenceDir,
+- @NonNull AndroidLibrary library) {
+- super(client, dir, referenceDir, library.getManifest());
+- mLibrary = library;
+-
+- // TODO: Make sure we don't use this project for any source library projects!
+- mReportIssues = false;
+- }
+-
+- @Override
+- public boolean isLibrary() {
+- return true;
+- }
+-
+- @Override
+- public AndroidLibrary getGradleLibraryModel() {
+- return mLibrary;
+- }
+-
+- @Override
+- public Variant getCurrentVariant() {
+- return null;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getManifestFiles() {
+- if (mManifestFiles == null) {
+- File manifest = mLibrary.getManifest();
+- if (manifest.exists()) {
+- mManifestFiles = Collections.singletonList(manifest);
+- } else {
+- mManifestFiles = Collections.emptyList();
+- }
+- }
+-
+- return mManifestFiles;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getProguardFiles() {
+- if (mProguardFiles == null) {
+- File proguardRules = mLibrary.getProguardRules();
+- if (proguardRules.exists()) {
+- mProguardFiles = Collections.singletonList(proguardRules);
+- } else {
+- mProguardFiles = Collections.emptyList();
+- }
+- }
+-
+- return mProguardFiles;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getResourceFolders() {
+- if (mResourceFolders == null) {
+- File folder = mLibrary.getResFolder();
+- if (folder.exists()) {
+- mResourceFolders = Collections.singletonList(folder);
+- } else {
+- mResourceFolders = Collections.emptyList();
+- }
+- }
+-
+- return mResourceFolders;
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getJavaSourceFolders() {
+- return Collections.emptyList();
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getTestSourceFolders() {
+- return Collections.emptyList();
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getJavaClassFolders() {
+- return Collections.emptyList();
+- }
+-
+- @NonNull
+- @Override
+- public List<File> getJavaLibraries() {
+- if (mJavaLibraries == null) {
+- mJavaLibraries = Lists.newArrayList();
+- File jarFile = mLibrary.getJarFile();
+- if (jarFile.exists()) {
+- mJavaLibraries.add(jarFile);
+- }
+-
+- for (File local : mLibrary.getLocalJars()) {
+- if (local.exists()) {
+- mJavaLibraries.add(local);
+- }
+- }
+- }
+-
+- return mJavaLibraries;
+- }
+-
+- @Nullable
+- @Override
+- public Boolean dependsOn(@NonNull String artifact) {
+- if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
+- if (mSupportLib == null) {
+- mSupportLib = dependsOn(mLibrary, artifact);
+- }
+- return mSupportLib;
+- } else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
+- if (mAppCompat == null) {
+- mAppCompat = dependsOn(mLibrary, artifact);
+- }
+- return mAppCompat;
+- } else {
+- return super.dependsOn(artifact);
+- }
+- }
+- }
+-}
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleRequest.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleRequest.java
+deleted file mode 100644
+index 8a13bcb..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LintGradleRequest.java
++++ /dev/null
+@@ -1,69 +0,0 @@
+-package com.android.build.gradle.internal;
+-
+-import com.android.annotations.NonNull;
+-import com.android.annotations.Nullable;
+-import com.android.builder.model.AndroidProject;
+-import com.android.builder.model.Variant;
+-import com.android.tools.lint.client.api.LintRequest;
+-import com.android.tools.lint.detector.api.Project;
+-import com.android.utils.Pair;
+-
+-import java.io.File;
+-import java.util.Collection;
+-import java.util.Collections;
+-import java.util.List;
+-
+-class LintGradleRequest extends LintRequest {
+- @NonNull private final LintGradleClient mLintClient;
+- @NonNull private final org.gradle.api.Project mGradleProject;
+- @Nullable private final String mVariantName;
+- @NonNull private final AndroidProject mModelProject;
+-
+- public LintGradleRequest(
+- @NonNull LintGradleClient client,
+- @NonNull AndroidProject modelProject,
+- @NonNull org.gradle.api.Project gradleProject,
+- @Nullable String variantName,
+- @NonNull List<File> files) {
+- super(client, files);
+- mLintClient = client;
+- mModelProject = modelProject;
+- mGradleProject = gradleProject;
+- mVariantName = variantName;
+- }
+-
+- @Nullable
+- @Override
+- public Collection<Project> getProjects() {
+- if (mProjects == null) {
+- Variant variant = findVariant(mModelProject, mVariantName);
+- if (variant == null) {
+- mProjects = Collections.emptyList();
+- return mProjects;
+- }
+- Pair<LintGradleProject,List<File>> result = LintGradleProject.create(
+- mLintClient, mModelProject, variant, mGradleProject);
+- mProjects = Collections.<Project>singletonList(result.getFirst());
+- mLintClient.setCustomRules(result.getSecond());
+- }
+-
+- return mProjects;
+- }
+-
+- private static Variant findVariant(@NonNull AndroidProject project,
+- @Nullable String variantName) {
+- if (variantName != null) {
+- for (Variant variant : project.getVariants()) {
+- if (variantName.equals(variant.getName())) {
+- return variant;
+- }
+- }
+- }
+-
+- if (!project.getVariants().isEmpty()) {
+- return project.getVariants().iterator().next();
+- }
+-
+- return null;
+- }
+-}
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java
+index 4c646b5..0db3585 100644
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java
++++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java
+@@ -96,7 +96,6 @@ import com.android.build.gradle.tasks.GenerateSplitAbiRes;
+ import com.android.build.gradle.tasks.JackTask;
+ import com.android.build.gradle.tasks.JavaResourcesProvider;
+ import com.android.build.gradle.tasks.JillTask;
+-import com.android.build.gradle.tasks.Lint;
+ import com.android.build.gradle.tasks.MergeAssets;
+ import com.android.build.gradle.tasks.MergeManifests;
+ import com.android.build.gradle.tasks.MergeResources;
+@@ -109,7 +108,7 @@ import com.android.build.gradle.tasks.ProcessAndroidResources;
+ import com.android.build.gradle.tasks.ProcessManifest;
+ import com.android.build.gradle.tasks.ProcessTestManifest;
+ import com.android.build.gradle.tasks.RenderscriptCompile;
+-import com.android.build.gradle.tasks.ShrinkResources;
++//import com.android.build.gradle.tasks.ShrinkResources;
+ import com.android.build.gradle.tasks.SplitZipAlign;
+ import com.android.build.gradle.tasks.ZipAlign;
+ import com.android.build.gradle.tasks.factory.JavaCompileConfigAction;
+@@ -368,24 +367,12 @@ public abstract class TaskManager {
+ }
+ });
+
+- tasks.create(LINT, Lint.class, new Action<Lint>() {
+- @Override
+- public void execute(Lint lintTask) {
+- lintTask.setDescription("Runs lint on all variants.");
+- lintTask.setVariantName("");
+- lintTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+- lintTask.setLintOptions(getExtension().getLintOptions());
+- lintTask.setSdkHome(sdkHandler.getSdkFolder());
+- lintTask.setToolingRegistry(toolingRegistry);
+- }
+- });
+ tasks.named(JavaBasePlugin.CHECK_TASK_NAME, new Action<Task>() {
+ @Override
+ public void execute(Task it) {
+ it.dependsOn(LINT);
+ }
+ });
+- createLintCompileTask(tasks);
+ }
+
+ public void createMockableJarTask() {
+@@ -1166,100 +1153,21 @@ public abstract class TaskManager {
+ }
+
+ // TODO - should compile src/lint/java from src/lint/java and jar it into build/lint/lint.jar
+- private void createLintCompileTask(TaskFactory tasks) {
+-
+- // TODO: move doFirst into dedicated task class.
+- tasks.create(LINT_COMPILE, Task.class,
+- new Action<Task>() {
+- @Override
+- public void execute(Task lintCompile) {
+- final File outputDir =
+- new File(getGlobalScope().getIntermediatesDir(), "lint");
+-
+- lintCompile.doFirst(new Action<Task>() {
+- @Override
+- public void execute(Task task) {
+- // create the directory for lint output if it does not exist.
+- if (!outputDir.exists()) {
+- boolean mkdirs = outputDir.mkdirs();
+- if (!mkdirs) {
+- throw new GradleException(
+- "Unable to create lint output directory.");
+- }
+- }
+- }
+- });
+- }
+- });
+- }
+
+ /**
+ * Is the given variant relevant for lint?
+ */
+- private static boolean isLintVariant(
+- @NonNull BaseVariantData<? extends BaseVariantOutputData> baseVariantData) {
+- // Only create lint targets for variants like debug and release, not debugTest
+- VariantConfiguration config = baseVariantData.getVariantConfiguration();
+- return !config.getType().isForTesting();
+- }
+
+ /**
+ * Add tasks for running lint on individual variants. We've already added a
+ * lint task earlier which runs on all variants.
+ */
+- public void createLintTasks(TaskFactory tasks, final VariantScope scope) {
+- final BaseVariantData<? extends BaseVariantOutputData> baseVariantData =
+- scope.getVariantData();
+- if (!isLintVariant(baseVariantData)) {
+- return;
+- }
+
+ // wire the main lint task dependency.
+- tasks.named(LINT, new Action<Task>() {
+- @Override
+- public void execute(Task it) {
+- it.dependsOn(LINT_COMPILE);
+- it.dependsOn(scope.getJavacTask().getName());
+- }
+- });
+-
+- AndroidTask<Lint> variantLintCheck = androidTasks.create(
+- tasks, new Lint.ConfigAction(scope));
+- variantLintCheck.dependsOn(tasks, LINT_COMPILE, scope.getJavacTask());
+- }
+
+- private void createLintVitalTask(@NonNull ApkVariantData variantData) {
+- checkState(getExtension().getLintOptions().isCheckReleaseBuilds());
+- // TODO: re-enable with Jack when possible
+- if (!variantData.getVariantConfiguration().getBuildType().isDebuggable() &&
+- !variantData.getVariantConfiguration().getUseJack()) {
+- String variantName = variantData.getVariantConfiguration().getFullName();
+- String capitalizedVariantName = StringHelper.capitalize(variantName);
+- String taskName = "lintVital" + capitalizedVariantName;
+- final Lint lintReleaseCheck = project.getTasks().create(taskName, Lint.class);
+- // TODO: Make this task depend on lintCompile too (resolve initialization order first)
+- optionalDependsOn(lintReleaseCheck, variantData.javacTask);
+- lintReleaseCheck.setLintOptions(getExtension().getLintOptions());
+- lintReleaseCheck.setSdkHome(sdkHandler.getSdkFolder());
+- lintReleaseCheck.setVariantName(variantName);
+- lintReleaseCheck.setToolingRegistry(toolingRegistry);
+- lintReleaseCheck.setFatalOnly(true);
+- lintReleaseCheck.setDescription(
+- "Runs lint on just the fatal issues in the " + capitalizedVariantName
+- + " build.");
+- //variantData.assembleVariantTask.dependsOn lintReleaseCheck
+
+ // If lint is being run, we do not need to run lint vital.
+ // TODO: Find a better way to do this.
+- project.getGradle().getTaskGraph().whenReady(new Closure<Void>(this, this) {
+- public void doCall(TaskExecutionGraph taskGraph) {
+- if (taskGraph.hasTask(LINT)) {
+- lintReleaseCheck.setEnabled(false);
+- }
+- }
+- });
+- }
+- }
+
+ private void createUnitTestTask(@NonNull TaskFactory tasks,
+ @NonNull final TestVariantData variantData) {
+@@ -1981,16 +1889,6 @@ public abstract class TaskManager {
+ // we insert the ShrinkResources task into the chain, such that its
+ // input is the ProcessAndroidResources packageOutputFile, and its
+ // output is what the PackageApplication task reads.
+- AndroidTask<ShrinkResources> shrinkTask = null;
+-
+- if (config.isMinifyEnabled() && config.getBuildType().isShrinkResources() &&
+- !config.getUseJack()) {
+- shrinkTask = androidTasks.create(
+- tasks, new ShrinkResources.ConfigAction(variantOutputScope));
+- shrinkTask.dependsOn(tasks, variantScope.getObfuscationTask(),
+- variantOutputScope.getManifestProcessorTask(),
+- variantOutputScope.getProcessResourcesTask());
+- }
+
+ AndroidTask<PackageApplication> packageApp = androidTasks.create(
+ tasks, new PackageApplication.ConfigAction(variantOutputScope));
+@@ -2001,7 +1899,6 @@ public abstract class TaskManager {
+
+ packageApp.optionalDependsOn(
+ tasks,
+- shrinkTask,
+ // TODO: When Jack is converted, add activeDexTask to VariantScope.
+ variantOutputScope.getVariantScope().getDexTask(),
+ variantOutputScope.getVariantScope().getJavaCompilerTask(),
+@@ -2157,9 +2054,6 @@ public abstract class TaskManager {
+ installTask.dependsOn(tasks, variantData.assembleVariantTask);
+ }
+
+- if (getExtension().getLintOptions().isCheckReleaseBuilds()) {
+- createLintVitalTask(variantData);
+- }
+
+ // add an uninstall task
+ final AndroidTask<UninstallTask> uninstallTask = androidTasks.create(
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LintOptions.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LintOptions.java
+deleted file mode 100644
+index eff135e..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LintOptions.java
++++ /dev/null
+@@ -1,798 +0,0 @@
+-/*
+- * Copyright (C) 2013 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.build.gradle.internal.dsl;
+-
+-import static com.android.SdkConstants.DOT_XML;
+-import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
+-import static com.android.tools.lint.detector.api.Severity.ERROR;
+-import static com.android.tools.lint.detector.api.Severity.FATAL;
+-import static com.android.tools.lint.detector.api.Severity.IGNORE;
+-import static com.android.tools.lint.detector.api.Severity.INFORMATIONAL;
+-import static com.android.tools.lint.detector.api.Severity.WARNING;
+-
+-import com.android.annotations.NonNull;
+-import com.android.annotations.Nullable;
+-import com.android.tools.lint.HtmlReporter;
+-import com.android.tools.lint.LintCliClient;
+-import com.android.tools.lint.LintCliFlags;
+-import com.android.tools.lint.TextReporter;
+-import com.android.tools.lint.XmlReporter;
+-import com.android.tools.lint.checks.BuiltinIssueRegistry;
+-import com.android.tools.lint.detector.api.Issue;
+-import com.android.tools.lint.detector.api.Severity;
+-import com.google.common.collect.Maps;
+-import com.google.common.collect.Sets;
+-
+-import org.gradle.api.GradleException;
+-import org.gradle.api.tasks.Input;
+-import org.gradle.api.tasks.InputFile;
+-import org.gradle.api.tasks.Optional;
+-import org.gradle.api.tasks.OutputFile;
+-
+-import java.io.BufferedWriter;
+-import java.io.File;
+-import java.io.FileWriter;
+-import java.io.IOException;
+-import java.io.PrintWriter;
+-import java.io.Serializable;
+-import java.io.Writer;
+-import java.util.Map;
+-import java.util.Set;
+-
+-/**
+- * DSL object for configuring lint options.
+- */
+-public class LintOptions implements com.android.builder.model.LintOptions, Serializable {
+- public static final String STDOUT = "stdout";
+- public static final String STDERR = "stderr";
+- private static final long serialVersionUID = 1L;
+-
+- @NonNull
+- private Set<String> disable = Sets.newHashSet();
+- @NonNull
+- private Set<String> enable = Sets.newHashSet();
+- @Nullable
+- private Set<String> check = Sets.newHashSet();
+- private boolean abortOnError = true;
+- private boolean absolutePaths = true;
+- private boolean noLines;
+- private boolean quiet;
+- private boolean checkAllWarnings;
+- private boolean ignoreWarnings;
+- private boolean warningsAsErrors;
+- private boolean showAll;
+- private boolean checkReleaseBuilds = true;
+- private boolean explainIssues = true;
+- @Nullable
+- private File lintConfig;
+- private boolean textReport;
+- @Nullable
+- private File textOutput;
+- private boolean htmlReport = true;
+- @Nullable
+- private File htmlOutput;
+- private boolean xmlReport = true;
+- @Nullable
+- private File xmlOutput;
+-
+- private Map<String,Severity> severities = Maps.newHashMap();
+-
+- public LintOptions() {
+- }
+-
+- public LintOptions(
+- @NonNull Set<String> disable,
+- @NonNull Set<String> enable,
+- @Nullable Set<String> check,
+- @Nullable File lintConfig,
+- boolean textReport,
+- @Nullable File textOutput,
+- boolean htmlReport,
+- @Nullable File htmlOutput,
+- boolean xmlReport,
+- @Nullable File xmlOutput,
+- boolean abortOnError,
+- boolean absolutePaths,
+- boolean noLines,
+- boolean quiet,
+- boolean checkAllWarnings,
+- boolean ignoreWarnings,
+- boolean warningsAsErrors,
+- boolean showAll,
+- boolean explainIssues,
+- boolean checkReleaseBuilds,
+- @Nullable Map<String,Integer> severityOverrides) {
+- this.disable = disable;
+- this.enable = enable;
+- this.check = check;
+- this.lintConfig = lintConfig;
+- this.textReport = textReport;
+- this.textOutput = textOutput;
+- this.htmlReport = htmlReport;
+- this.htmlOutput = htmlOutput;
+- this.xmlReport = xmlReport;
+- this.xmlOutput = xmlOutput;
+- this.abortOnError = abortOnError;
+- this.absolutePaths = absolutePaths;
+- this.noLines = noLines;
+- this.quiet = quiet;
+- this.checkAllWarnings = checkAllWarnings;
+- this.ignoreWarnings = ignoreWarnings;
+- this.warningsAsErrors = warningsAsErrors;
+- this.showAll = showAll;
+- this.explainIssues = explainIssues;
+- this.checkReleaseBuilds = checkReleaseBuilds;
+-
+- if (severityOverrides != null) {
+- for (Map.Entry<String,Integer> entry : severityOverrides.entrySet()) {
+- severities.put(entry.getKey(), convert(entry.getValue()));
+- }
+- }
+- }
+-
+- @NonNull
+- public static com.android.builder.model.LintOptions create(@NonNull com.android.builder.model.LintOptions source) {
+- return new LintOptions(
+- source.getDisable(),
+- source.getEnable(),
+- source.getCheck(),
+- source.getLintConfig(),
+- source.getTextReport(),
+- source.getTextOutput(),
+- source.getHtmlReport(),
+- source.getHtmlOutput(),
+- source.getXmlReport(),
+- source.getXmlOutput(),
+- source.isAbortOnError(),
+- source.isAbsolutePaths(),
+- source.isNoLines(),
+- source.isQuiet(),
+- source.isCheckAllWarnings(),
+- source.isIgnoreWarnings(),
+- source.isWarningsAsErrors(),
+- source.isShowAll(),
+- source.isExplainIssues(),
+- source.isCheckReleaseBuilds(),
+- source.getSeverityOverrides()
+- );
+- }
+-
+- /**
+- * Returns the set of issue id's to suppress. Callers are allowed to modify this collection.
+- */
+- @Override
+- @NonNull
+- @Input
+- public Set<String> getDisable() {
+- return disable;
+- }
+-
+- /**
+- * Sets the set of issue id's to suppress. Callers are allowed to modify this collection.
+- * Note that these ids add to rather than replace the given set of ids.
+- */
+- public void setDisable(@Nullable Set<String> ids) {
+- disable.addAll(ids);
+- }
+-
+- /**
+- * Returns the set of issue id's to enable. Callers are allowed to modify this collection.
+- * To enable a given issue, add the issue ID to the returned set.
+- */
+- @Override
+- @NonNull
+- @Input
+- public Set<String> getEnable() {
+- return enable;
+- }
+-
+- /**
+- * Sets the set of issue id's to enable. Callers are allowed to modify this collection.
+- * Note that these ids add to rather than replace the given set of ids.
+- */
+- public void setEnable(@Nullable Set<String> ids) {
+- enable.addAll(ids);
+- }
+-
+- /**
+- * Returns the exact set of issues to check, or null to run the issues that are enabled
+- * by default plus any issues enabled via {@link #getEnable} and without issues disabled
+- * via {@link #getDisable}. If non-null, callers are allowed to modify this collection.
+- */
+- @Override
+- @Nullable
+- @Optional
+- @Input
+- public Set<String> getCheck() {
+- return check;
+- }
+-
+- /**
+- * Sets the <b>exact</b> set of issues to check.
+- * @param ids the set of issue id's to check
+- */
+- public void setCheck(@NonNull Set<String> ids) {
+- check.addAll(ids);
+- }
+-
+- /** Whether lint should set the exit code of the process if errors are found */
+- @Override
+- @Input
+- public boolean isAbortOnError() {
+- return abortOnError;
+- }
+-
+- /** Sets whether lint should set the exit code of the process if errors are found */
+- public void setAbortOnError(boolean abortOnError) {
+- this.abortOnError = abortOnError;
+- }
+-
+- /**
+- * Whether lint should display full paths in the error output. By default the paths
+- * are relative to the path lint was invoked from.
+- */
+- @Override
+- @Input
+- public boolean isAbsolutePaths() {
+- return absolutePaths;
+- }
+-
+- /**
+- * Sets whether lint should display full paths in the error output. By default the paths
+- * are relative to the path lint was invoked from.
+- */
+- public void setAbsolutePaths(boolean absolutePaths) {
+- this.absolutePaths = absolutePaths;
+- }
+-
+- /**
+- * Whether lint should include the source lines in the output where errors occurred
+- * (true by default)
+- */
+- @Override
+- @Input
+- public boolean isNoLines() {
+- return this.noLines;
+- }
+-
+- /**
+- * Sets whether lint should include the source lines in the output where errors occurred
+- * (true by default)
+- */
+- public void setNoLines(boolean noLines) {
+- this.noLines = noLines;
+- }
+-
+- /**
+- * Returns whether lint should be quiet (for example, not write informational messages
+- * such as paths to report files written)
+- */
+- @Override
+- @Input
+- public boolean isQuiet() {
+- return quiet;
+- }
+-
+- /**
+- * Sets whether lint should be quiet (for example, not write informational messages
+- * such as paths to report files written)
+- */
+- public void setQuiet(boolean quiet) {
+- this.quiet = quiet;
+- }
+-
+- /** Returns whether lint should check all warnings, including those off by default */
+- @Override
+- @Input
+- public boolean isCheckAllWarnings() {
+- return checkAllWarnings;
+- }
+-
+- /** Sets whether lint should check all warnings, including those off by default */
+- public void setCheckAllWarnings(boolean warnAll) {
+- this.checkAllWarnings = warnAll;
+- }
+-
+- /** Returns whether lint will only check for errors (ignoring warnings) */
+- @Override
+- @Input
+- public boolean isIgnoreWarnings() {
+- return ignoreWarnings;
+- }
+-
+- /** Sets whether lint will only check for errors (ignoring warnings) */
+- public void setIgnoreWarnings(boolean noWarnings) {
+- this.ignoreWarnings = noWarnings;
+- }
+-
+- /** Returns whether lint should treat all warnings as errors */
+- @Override
+- @Input
+- public boolean isWarningsAsErrors() {
+- return warningsAsErrors;
+- }
+-
+- /** Sets whether lint should treat all warnings as errors */
+- public void setWarningsAsErrors(boolean allErrors) {
+- this.warningsAsErrors = allErrors;
+- }
+-
+- /** Returns whether lint should include explanations for issue errors. (Note that
+- * HTML and XML reports intentionally do this unconditionally, ignoring this setting.) */
+- @Override
+- @Input
+- public boolean isExplainIssues() {
+- return explainIssues;
+- }
+-
+- public void setExplainIssues(boolean explainIssues) {
+- this.explainIssues = explainIssues;
+- }
+-
+- /**
+- * Returns whether lint should include all output (e.g. include all alternate
+- * locations, not truncating long messages, etc.)
+- */
+- @Override
+- @Input
+- public boolean isShowAll() {
+- return showAll;
+- }
+-
+- /**
+- * Sets whether lint should include all output (e.g. include all alternate
+- * locations, not truncating long messages, etc.)
+- */
+- public void setShowAll(boolean showAll) {
+- this.showAll = showAll;
+- }
+-
+- /**
+- * Returns whether lint should check for fatal errors during release builds. Default is true.
+- * If issues with severity "fatal" are found, the release build is aborted.
+- */
+- @Override
+- @Input
+- public boolean isCheckReleaseBuilds() {
+- return checkReleaseBuilds;
+- }
+-
+- public void setCheckReleaseBuilds(boolean checkReleaseBuilds) {
+- this.checkReleaseBuilds = checkReleaseBuilds;
+- }
+-
+- /**
+- * Returns the default configuration file to use as a fallback
+- */
+- @Override
+- @Optional @InputFile
+- public File getLintConfig() {
+- return lintConfig;
+- }
+-
+- /** Whether we should write an text report. Default false. The location can be
+- * controlled by {@link #getTextOutput()}. */
+- @Override
+- @Input
+- public boolean getTextReport() {
+- return textReport;
+- }
+-
+- public void setTextReport(boolean textReport) {
+- this.textReport = textReport;
+- }
+-
+- public void setHtmlReport(boolean htmlReport) {
+- this.htmlReport = htmlReport;
+- }
+-
+- public void setHtmlOutput(@NonNull File htmlOutput) {
+- this.htmlOutput = htmlOutput;
+- }
+-
+- public void setXmlReport(boolean xmlReport) {
+- this.xmlReport = xmlReport;
+- }
+-
+- public void setXmlOutput(@NonNull File xmlOutput) {
+- this.xmlOutput = xmlOutput;
+- }
+-
+- /**
+- * The optional path to where a text report should be written. The special value
+- * "stdout" can be used to point to standard output.
+- */
+- @Override
+- @Nullable
+- @Optional
+- @Input
+- public File getTextOutput() {
+- return textOutput;
+- }
+-
+- /** Whether we should write an HTML report. Default true. The location can be
+- * controlled by {@link #getHtmlOutput()}. */
+- @Override
+- @Input
+- public boolean getHtmlReport() {
+- return htmlReport;
+- }
+-
+- /** The optional path to where an HTML report should be written */
+- @Override
+- @Nullable
+- @Optional
+- @OutputFile
+- public File getHtmlOutput() {
+- return htmlOutput;
+- }
+-
+- /** Whether we should write an XML report. Default true. The location can be
+- * controlled by {@link #getXmlOutput()}. */
+- @Override
+- @Input
+- public boolean getXmlReport() {
+- return xmlReport;
+- }
+-
+- /** The optional path to where an XML report should be written */
+- @Override
+- @Nullable
+- @Optional
+- @OutputFile
+- public File getXmlOutput() {
+- return xmlOutput;
+- }
+-
+- /**
+- * Sets the default config file to use as a fallback. This corresponds to a {@code lint.xml}
+- * file with severities etc to use when a project does not have more specific information.
+- */
+- public void setLintConfig(@NonNull File lintConfig) {
+- this.lintConfig = lintConfig;
+- }
+-
+- public void syncTo(
+- @NonNull LintCliClient client,
+- @NonNull LintCliFlags flags,
+- @Nullable String variantName,
+- @Nullable org.gradle.api.Project project,
+- boolean report) {
+- if (disable != null) {
+- flags.getSuppressedIds().addAll(disable);
+- }
+- if (enable != null) {
+- flags.getEnabledIds().addAll(enable);
+- }
+- if (check != null && !check.isEmpty()) {
+- flags.setExactCheckedIds(check);
+- }
+- flags.setSetExitCode(this.abortOnError);
+- flags.setFullPath(absolutePaths);
+- flags.setShowSourceLines(!noLines);
+- flags.setQuiet(quiet);
+- flags.setCheckAllWarnings(checkAllWarnings);
+- flags.setIgnoreWarnings(ignoreWarnings);
+- flags.setWarningsAsErrors(warningsAsErrors);
+- flags.setShowEverything(showAll);
+- flags.setDefaultConfiguration(lintConfig);
+- flags.setSeverityOverrides(severities);
+- flags.setExplainIssues(explainIssues);
+-
+- if (report || flags.isFatalOnly() && this.abortOnError) {
+- if (textReport || flags.isFatalOnly()) {
+- File output = textOutput;
+- if (output == null) {
+- output = new File(flags.isFatalOnly() ? STDERR: STDOUT);
+- } else if (!output.isAbsolute() && !isStdOut(output) && !isStdErr(output)) {
+- output = project.file(output.getPath());
+- }
+- output = validateOutputFile(output);
+-
+- Writer writer;
+- File file = null;
+- boolean closeWriter;
+- if (isStdOut(output)) {
+- writer = new PrintWriter(System.out, true);
+- closeWriter = false;
+- } else if (isStdErr(output)) {
+- writer = new PrintWriter(System.err, true);
+- closeWriter = false;
+- } else {
+- file = output;
+- try {
+- writer = new BufferedWriter(new FileWriter(output));
+- } catch (IOException e) {
+- throw new org.gradle.api.GradleException("Text invalid argument.", e);
+- }
+- closeWriter = true;
+- }
+- flags.getReporters().add(new TextReporter(client, flags, file, writer,
+- closeWriter));
+- }
+- if (htmlReport) {
+- File output = htmlOutput;
+- if (output == null || flags.isFatalOnly()) {
+- output = createOutputPath(project, variantName, ".html", flags.isFatalOnly());
+- } else if (!output.isAbsolute()) {
+- output = project.file(output.getPath());
+- }
+- output = validateOutputFile(output);
+- try {
+- flags.getReporters().add(new HtmlReporter(client, output));
+- } catch (IOException e) {
+- throw new GradleException("HTML invalid argument.", e);
+- }
+- }
+- if (xmlReport) {
+- File output = xmlOutput;
+- if (output == null || flags.isFatalOnly()) {
+- output = createOutputPath(project, variantName, DOT_XML, flags.isFatalOnly());
+- } else if (!output.isAbsolute()) {
+- output = project.file(output.getPath());
+- }
+- output = validateOutputFile(output);
+- try {
+- flags.getReporters().add(new XmlReporter(client, output));
+- } catch (IOException e) {
+- throw new org.gradle.api.GradleException("XML invalid argument.", e);
+- }
+- }
+- }
+- }
+-
+- private static boolean isStdOut(@NonNull File output) {
+- return STDOUT.equals(output.getPath());
+- }
+-
+- private static boolean isStdErr(@NonNull File output) {
+- return STDERR.equals(output.getPath());
+- }
+-
+- @NonNull
+- private static File validateOutputFile(@NonNull File output) {
+- if (isStdOut(output) || isStdErr(output)) {
+- return output;
+- }
+-
+- File parent = output.getParentFile();
+- if (!parent.exists()) {
+- parent.mkdirs();
+- }
+-
+- output = output.getAbsoluteFile();
+- if (output.exists()) {
+- boolean delete = output.delete();
+- if (!delete) {
+- throw new org.gradle.api.GradleException("Could not delete old " + output);
+- }
+- }
+- if (output.getParentFile() != null && !output.getParentFile().canWrite()) {
+- throw new org.gradle.api.GradleException("Cannot write output file " + output);
+- }
+-
+- return output;
+- }
+-
+- private static File createOutputPath(
+- @NonNull org.gradle.api.Project project,
+- @NonNull String variantName,
+- @NonNull String extension,
+- boolean fatalOnly) {
+- StringBuilder base = new StringBuilder();
+- base.append(FD_OUTPUTS);
+- base.append(File.separator);
+- base.append("lint-results");
+- if (variantName != null) {
+- base.append("-");
+- base.append(variantName);
+- }
+- if (fatalOnly) {
+- base.append("-fatal");
+- }
+- base.append(extension);
+- return new File(project.getBuildDir(), base.toString());
+- }
+-
+- /**
+- * An optional map of severity overrides. The map maps from issue id's to the corresponding
+- * severity to use, which must be "fatal", "error", "warning", or "ignore".
+- *
+- * @return a map of severity overrides, or null. The severities are one of the constants
+- * {@link #SEVERITY_FATAL}, {@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING},
+- * {@link #SEVERITY_INFORMATIONAL}, {@link #SEVERITY_IGNORE}
+- */
+- @Override
+- @Nullable
+- public Map<String, Integer> getSeverityOverrides() {
+- if (severities == null || severities.isEmpty()) {
+- return null;
+- }
+-
+- Map<String, Integer> map =
+- Maps.newHashMapWithExpectedSize(severities.size());
+- for (Map.Entry<String,Severity> entry : severities.entrySet()) {
+- map.put(entry.getKey(), convert(entry.getValue()));
+- }
+-
+- return map;
+- }
+-
+- // -- DSL Methods.
+-
+- /**
+- * Adds the id to the set of issues to check.
+- */
+- public void check(String id) {
+- check.add(id);
+- }
+-
+- /**
+- * Adds the ids to the set of issues to check.
+- */
+- public void check(String... ids) {
+- for (String id : ids) {
+- check(id);
+- }
+- }
+-
+- /**
+- * Adds the id to the set of issues to enable.
+- */
+- public void enable(String id) {
+- enable.add(id);
+- Issue issue = new BuiltinIssueRegistry().getIssue(id);
+- severities.put(id, issue != null ? issue.getDefaultSeverity() : WARNING);
+- }
+-
+- /**
+- * Adds the ids to the set of issues to enable.
+- */
+- public void enable(String... ids) {
+- for (String id : ids) {
+- enable(id);
+- }
+- }
+-
+- /**
+- * Adds the id to the set of issues to enable.
+- */
+- public void disable(String id) {
+- disable.add(id);
+- severities.put(id, IGNORE);
+- }
+-
+- /**
+- * Adds the ids to the set of issues to enable.
+- */
+- public void disable(String... ids) {
+- for (String id : ids) {
+- disable(id);
+- }
+- }
+-
+- // For textOutput 'stdout' or 'stderr' (normally a file)
+- public void textOutput(String textOutput) {
+- this.textOutput = new File(textOutput);
+- }
+-
+- // For textOutput file()
+- public void textOutput(File textOutput) {
+- this.textOutput = textOutput;
+- }
+-
+- /**
+- * Adds a severity override for the given issues.
+- */
+- public void fatal(String id) {
+- severities.put(id, FATAL);
+- }
+-
+- /**
+- * Adds a severity override for the given issues.
+- */
+- public void fatal(String... ids) {
+- for (String id : ids) {
+- fatal(id);
+- }
+- }
+-
+- /**
+- * Adds a severity override for the given issues.
+- */
+- public void error(String id) {
+- severities.put(id, ERROR);
+- }
+-
+- /**
+- * Adds a severity override for the given issues.
+- */
+- public void error(String... ids) {
+- for (String id : ids) {
+- error(id);
+- }
+- }
+-
+- /**
+- * Adds a severity override for the given issues.
+- */
+- public void warning(String id) {
+- severities.put(id, WARNING);
+- }
+-
+- /**
+- * Adds a severity override for the given issues.
+- */
+- public void warning(String... ids) {
+- for (String id : ids) {
+- warning(id);
+- }
+- }
+-
+- /**
+- * Adds a severity override for the given issues.
+- */
+- public void ignore(String id) {
+- severities.put(id, IGNORE);
+- }
+-
+- /**
+- * Adds a severity override for the given issues.
+- */
+- public void ignore(String... ids) {
+- for (String id : ids) {
+- ignore(id);
+- }
+- }
+-
+- // Without these qualifiers, Groovy compilation will fail with "Apparent variable
+- // 'SEVERITY_FATAL' was found in a static scope but doesn't refer to a local variable,
+- // static field or class"
+- //@SuppressWarnings("UnnecessaryQualifiedReference")
+- private static int convert(Severity s) {
+- switch (s) {
+- case FATAL:
+- return com.android.builder.model.LintOptions.SEVERITY_FATAL;
+- case ERROR:
+- return com.android.builder.model.LintOptions.SEVERITY_ERROR;
+- case WARNING:
+- return com.android.builder.model.LintOptions.SEVERITY_WARNING;
+- case INFORMATIONAL:
+- return com.android.builder.model.LintOptions.SEVERITY_INFORMATIONAL;
+- case IGNORE:
+- default:
+- return com.android.builder.model.LintOptions.SEVERITY_IGNORE;
+- }
+- }
+-
+- //@SuppressWarnings("UnnecessaryQualifiedReference")
+- private static Severity convert(int s) {
+- switch (s) {
+- case com.android.builder.model.LintOptions.SEVERITY_FATAL:
+- return FATAL;
+- case com.android.builder.model.LintOptions.SEVERITY_ERROR:
+- return ERROR;
+- case com.android.builder.model.LintOptions.SEVERITY_WARNING:
+- return WARNING;
+- case com.android.builder.model.LintOptions.SEVERITY_INFORMATIONAL:
+- return INFORMATIONAL;
+- case com.android.builder.model.LintOptions.SEVERITY_IGNORE:
+- default:
+- return IGNORE;
+- }
+- }
+-}
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
+index 4528821..6224833 100644
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
++++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
+@@ -24,7 +24,6 @@ import com.android.builder.model.AndroidProject;
+ import com.android.builder.model.ArtifactMetaData;
+ import com.android.builder.model.BuildTypeContainer;
+ import com.android.builder.model.JavaCompileOptions;
+-import com.android.builder.model.LintOptions;
+ import com.android.builder.model.ProductFlavorContainer;
+ import com.android.builder.model.SigningConfig;
+ import com.android.builder.model.SyncIssue;
+@@ -65,8 +64,6 @@ class DefaultAndroidProject implements AndroidProject, Serializable {
+ @NonNull
+ private final JavaCompileOptions javaCompileOptions;
+ @NonNull
+- private final LintOptions lintOptions;
+- @NonNull
+ private final File buildFolder;
+ @Nullable
+ private final String resourcePrefix;
+@@ -97,7 +94,6 @@ class DefaultAndroidProject implements AndroidProject, Serializable {
+ @NonNull Collection<String> unresolvedDependencies,
+ @NonNull Collection<SyncIssue> syncIssues,
+ @NonNull CompileOptions compileOptions,
+- @NonNull LintOptions lintOptions,
+ @NonNull File buildFolder,
+ @Nullable String resourcePrefix,
+ @NonNull Collection<NativeToolchain> nativeToolchains,
+@@ -115,7 +111,6 @@ class DefaultAndroidProject implements AndroidProject, Serializable {
+ this.unresolvedDependencies = unresolvedDependencies;
+ this.syncIssues = syncIssues;
+ javaCompileOptions = new DefaultJavaCompileOptions(compileOptions);
+- this.lintOptions = lintOptions;
+ this.buildFolder = buildFolder;
+ this.resourcePrefix = resourcePrefix;
+ this.isLibrary = isLibrary;
+@@ -238,12 +233,6 @@ class DefaultAndroidProject implements AndroidProject, Serializable {
+
+ @Override
+ @NonNull
+- public LintOptions getLintOptions() {
+- return lintOptions;
+- }
+-
+- @Override
+- @NonNull
+ public Collection<String> getUnresolvedDependencies() {
+ return unresolvedDependencies;
+ }
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
+index b6e18ee..4cb5424 100644
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
++++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
+@@ -49,7 +49,6 @@ import com.android.builder.model.AndroidProject;
+ import com.android.builder.model.ApiVersion;
+ import com.android.builder.model.ArtifactMetaData;
+ import com.android.builder.model.JavaArtifact;
+-import com.android.builder.model.LintOptions;
+ import com.android.builder.model.NativeLibrary;
+ import com.android.builder.model.NativeToolchain;
+ import com.android.builder.model.ProductFlavor;
+@@ -149,9 +148,6 @@ public class ModelBuilder implements ToolingModelBuilder {
+ variantType.getArtifactType()));
+ }
+
+- LintOptions lintOptions = com.android.build.gradle.internal.dsl.LintOptions.create(
+- config.getLintOptions());
+-
+ AaptOptions aaptOptions = AaptOptionsImpl.create(config.getAaptOptions());
+
+ List<SyncIssue> syncIssues = Lists.newArrayList(extraModelInfo.getSyncIssues().values());
+@@ -174,7 +170,6 @@ public class ModelBuilder implements ToolingModelBuilder {
+ findUnresolvedDependencies(syncIssues),
+ syncIssues,
+ config.getCompileOptions(),
+- lintOptions,
+ project.getBuildDir(),
+ config.getResourcePrefix(),
+ ImmutableList.copyOf(toolchains.values()),
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
+index 2aeb1d6..3138cbc 100644
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
++++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
+@@ -22,7 +22,7 @@ import com.android.build.OutputFile;
+ import com.android.build.gradle.AndroidConfig;
+ import com.android.build.gradle.internal.TaskManager;
+ import com.android.build.gradle.internal.core.GradleVariantConfiguration;
+-import com.android.build.gradle.tasks.ExtractAnnotations;
++//import com.android.build.gradle.tasks.ExtractAnnotations;
+ import com.android.builder.core.VariantType;
+ import com.google.common.collect.Maps;
+
+@@ -40,9 +40,6 @@ public class LibraryVariantData extends BaseVariantData<LibVariantOutputData> im
+
+ private final Map<VariantType, TestVariantData> testVariants;
+
+- @Nullable
+- public ExtractAnnotations generateAnnotationsTask = null;
+-
+ public LibraryVariantData(
+ @NonNull AndroidConfig androidConfig,
+ @NonNull TaskManager taskManager,
+@@ -93,11 +90,6 @@ public class LibraryVariantData extends BaseVariantData<LibVariantOutputData> im
+ public void registerJavaGeneratingTask(
+ @NonNull Task task, @NonNull File... generatedSourceFolders) {
+ super.registerJavaGeneratingTask(task, generatedSourceFolders);
+- if (generateAnnotationsTask != null) {
+- for (File f : generatedSourceFolders) {
+- generateAnnotationsTask.source(f);
+- }
+- }
+ }
+
+ // Overridden to add source folders to a generateAnnotationsTask, if it exists.
+@@ -105,10 +97,5 @@ public class LibraryVariantData extends BaseVariantData<LibVariantOutputData> im
+ public void registerJavaGeneratingTask(
+ @NonNull Task task, @NonNull Collection<File> generatedSourceFolders) {
+ super.registerJavaGeneratingTask(task, generatedSourceFolders);
+- if (generateAnnotationsTask != null) {
+- for (File f : generatedSourceFolders) {
+- generateAnnotationsTask.source(f);
+- }
+- }
+ }
+ }
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
+deleted file mode 100644
+index 4867946..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
++++ /dev/null
+@@ -1,265 +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.
+- */
+-
+-package com.android.build.gradle.tasks
+-
+-import com.android.annotations.NonNull
+-import com.android.build.gradle.internal.tasks.AbstractAndroidCompile
+-import com.android.build.gradle.internal.variant.BaseVariantData
+-import com.android.build.gradle.tasks.annotations.ApiDatabase
+-import com.android.build.gradle.tasks.annotations.Extractor
+-import com.android.tools.lint.EcjParser
+-import com.android.utils.Pair
+-import com.google.common.collect.Lists
+-import com.google.common.collect.Maps
+-import org.eclipse.jdt.core.compiler.IProblem
+-import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration
+-import org.eclipse.jdt.internal.compiler.batch.CompilationUnit
+-import org.eclipse.jdt.internal.compiler.env.ICompilationUnit
+-import org.eclipse.jdt.internal.compiler.env.INameEnvironment
+-import org.eclipse.jdt.internal.compiler.impl.CompilerOptions
+-import org.eclipse.jdt.internal.compiler.util.Util
+-import org.gradle.api.file.EmptyFileVisitor
+-import org.gradle.api.file.FileVisitDetails
+-import org.gradle.api.logging.LogLevel
+-import org.gradle.api.tasks.Input
+-import org.gradle.api.tasks.InputFile
+-import org.gradle.api.tasks.Optional
+-import org.gradle.api.tasks.OutputFile
+-import org.gradle.api.tasks.TaskAction
+-import org.gradle.tooling.BuildException
+-
+-import static com.android.SdkConstants.DOT_JAVA
+-import static com.android.SdkConstants.UTF_8
+-
+-/**
+- * Task which extracts annotations from the source files, and writes them to one of
+- * two possible destinations:
+- * <ul>
+- * <li> A "external annotations" file (pointed to by {@link ExtractAnnotations#output})
+- * which records the annotations in a zipped XML format for use by the IDE and by
+- * lint to associate the (source retention) annotations back with the compiled code</li>
+- * <li> For any {@code Keep} annotated elements, a Proguard keep file (pointed to by
+- * {@link ExtractAnnotations#proguard}, which lists APIs (classes, methods and fields)
+- * that should not be removed even if no references in code are found to those APIs.</li>
+- * <p>
+- * We typically only extract external annotations when building libraries; ProGuard annotations
+- * are extracted when building libraries (to record in the AAR), <b>or</b> when building an
+- * app module where ProGuarding is enabled.
+- * </ul>
+- */
+-class ExtractAnnotations extends AbstractAndroidCompile {
+- public BaseVariantData variant
+-
+- /** Boot classpath: typically android.jar */
+- @Input
+- public List<String> bootClasspath
+-
+- /** The output .zip file to write the annotations database to, if any */
+- @Optional
+- @OutputFile
+- public File output
+-
+- /** The output proguard file to write any @Keep rules into, if any */
+- @Optional
+- @OutputFile
+- public File proguard
+-
+- /**
+- * An optional pointer to an API file to filter the annotations by (any annotations
+- * not found in the API file are considered hidden/not exposed.) This is in the same
+- * format as the api-versions.xml file found in the SDK.
+- */
+- @Optional
+- @InputFile
+- public File apiFilter
+-
+- /**
+- * A list of existing annotation zip files (or dirs) to merge in. This can be used to merge in
+- * a hardcoded set of annotations that are not present in the source code, such as
+- * {@code @Contract} annotations we'd like to record without actually having a dependency
+- * on the IDEA annotations library.
+- */
+- @Optional
+- @InputFile
+- public List<File> mergeJars
+-
+- /**
+- * The encoding to use when reading source files. The output file will ignore this and
+- * will always be a UTF-8 encoded .xml file inside the annotations zip file.
+- */
+- @Optional
+- @Input
+- public String encoding
+-
+- /**
+- * Location of class files. If set, any non-public typedef source retention annotations
+- * will be removed prior to .jar packaging.
+- */
+- @Optional
+- @InputFile
+- public File classDir
+-
+- /** Whether we allow extraction even in the presence of symbol resolution errors */
+- @InputFile
+- public boolean allowErrors = true
+-
+- @Override
+- @TaskAction
+- protected void compile() {
+- if (!hasAndroidAnnotations()) {
+- return
+- }
+-
+- if (encoding == null) {
+- encoding = UTF_8
+- }
+-
+- Pair<Collection<CompilationUnitDeclaration>, INameEnvironment> result = parseSources()
+- def parsedUnits = result.first
+- def environment = result.second
+-
+- try {
+- if (!allowErrors) {
+- for (CompilationUnitDeclaration unit : parsedUnits) {
+- // so maybe I don't need my map!!
+- def problems = unit.compilationResult().allProblems
+- for (IProblem problem : problems) {
+- if (problem.error) {
+- println "Not extracting annotations (compilation problems encountered)";
+- println "Error: " + problem.getOriginatingFileName() + ":" +
+- problem.getSourceLineNumber() + ": " + problem.getMessage()
+- // TODO: Consider whether we abort the build at this point!
+- return
+- }
+- }
+- }
+- }
+-
+- // API definition file
+- ApiDatabase database = null;
+- if (apiFilter != null && apiFilter.exists()) {
+- try {
+- database = new ApiDatabase(apiFilter);
+- } catch (IOException e) {
+- throw new BuildException("Could not open API database " + apiFilter, e)
+- }
+- }
+-
+-
+- def displayInfo = project.logger.isEnabled(LogLevel.INFO)
+- def includeClassRetentionAnnotations = false
+- def sortAnnotations = false
+-
+- Extractor extractor = new Extractor(database, classDir, displayInfo,
+- includeClassRetentionAnnotations, sortAnnotations);
+- extractor.extractFromProjectSource(parsedUnits)
+- if (mergeJars != null) {
+- for (File jar : mergeJars) {
+- extractor.mergeExisting(jar);
+- }
+- }
+- extractor.export(output, proguard)
+- extractor.removeTypedefClasses();
+- } finally {
+- if (environment != null) {
+- environment.cleanup()
+- }
+- }
+- }
+-
+- @Input
+- public boolean hasAndroidAnnotations() {
+- return variant.variantDependency.annotationsPresent
+- }
+-
+- @NonNull
+- private Pair<Collection<CompilationUnitDeclaration>,INameEnvironment> parseSources() {
+- List<ICompilationUnit> sourceUnits = Lists.newArrayListWithExpectedSize(100);
+-
+- source.visit(new EmptyFileVisitor() {
+- @Override
+- void visitFile(FileVisitDetails fileVisitDetails) {
+- def file = fileVisitDetails.file;
+- def path = file.getPath()
+- if (path.endsWith(DOT_JAVA) && file.isFile()) {
+- char[] contents = Util.getFileCharContent(file, encoding);
+- ICompilationUnit unit = new CompilationUnit(contents, path, encoding);
+- sourceUnits.add(unit);
+- }
+- }
+- })
+-
+- Map<ICompilationUnit, CompilationUnitDeclaration> outputMap = Maps.
+- newHashMapWithExpectedSize(sourceUnits.size())
+- List<String> jars = Lists.newArrayList();
+- if (bootClasspath != null) {
+- jars.addAll(bootClasspath)
+- }
+- if (classpath != null) {
+- for (File jar : classpath) {
+- jars.add(jar.getPath());
+- }
+- }
+-
+- CompilerOptions options = EcjParser.createCompilerOptions();
+- options.docCommentSupport = true; // So I can find @hide
+-
+- // Note: We can *not* set options.ignoreMethodBodies=true because it disables
+- // type attribution!
+-
+- def level = getLanguageLevel(sourceCompatibility)
+- options.sourceLevel = level
+- options.complianceLevel = options.sourceLevel
+- // We don't generate code, but just in case the parser consults this flag
+- // and makes sure that it's not greater than the source level:
+- options.targetJDK = options.sourceLevel
+- options.originalComplianceLevel = options.sourceLevel;
+- options.originalSourceLevel = options.sourceLevel;
+- options.inlineJsrBytecode = true; // >= 1.5
+-
+- def environment = EcjParser.parse(options, sourceUnits, jars, outputMap, null);
+- Collection<CompilationUnitDeclaration> parsedUnits = outputMap.values()
+- Pair.of(parsedUnits, environment);
+- }
+-
+- private static long getLanguageLevel(String version) {
+- if ("1.6".equals(version)) {
+- return EcjParser.getLanguageLevel(1, 6);
+- } else if ("1.7".equals(version)) {
+- return EcjParser.getLanguageLevel(1, 7);
+- } else if ("1.5") {
+- return EcjParser.getLanguageLevel(1, 5);
+- } else {
+- return EcjParser.getLanguageLevel(1, 7);
+- }
+- }
+-
+- private def addSources(List<ICompilationUnit> sourceUnits, File file) {
+- if (file.isDirectory()) {
+- def files = file.listFiles();
+- if (files != null) {
+- for (File sub : files) {
+- addSources(sourceUnits, sub);
+- }
+- }
+- } else if (file.getPath().endsWith(DOT_JAVA) && file.isFile()) {
+- char[] contents = Util.getFileCharContent(file, encoding);
+- ICompilationUnit unit = new CompilationUnit(contents, file.getPath(), encoding);
+- sourceUnits.add(unit);
+- }
+- }
+-}
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GroovyGradleDetector.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GroovyGradleDetector.java
+deleted file mode 100644
+index 16ff7d5..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GroovyGradleDetector.java
++++ /dev/null
+@@ -1,242 +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.
+- */
+-
+-package com.android.build.gradle.tasks;
+-
+-import com.android.annotations.NonNull;
+-import com.android.tools.lint.checks.GradleDetector;
+-import com.android.tools.lint.detector.api.Context;
+-import com.android.tools.lint.detector.api.DefaultPosition;
+-import com.android.tools.lint.detector.api.Implementation;
+-import com.android.tools.lint.detector.api.Location;
+-import com.android.tools.lint.detector.api.Scope;
+-import com.android.utils.Pair;
+-import com.google.common.collect.Lists;
+-import com.google.common.collect.Maps;
+-
+-import org.codehaus.groovy.ast.ASTNode;
+-import org.codehaus.groovy.ast.CodeVisitorSupport;
+-import org.codehaus.groovy.ast.GroovyCodeVisitor;
+-import org.codehaus.groovy.ast.builder.AstBuilder;
+-import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+-import org.codehaus.groovy.ast.expr.ClosureExpression;
+-import org.codehaus.groovy.ast.expr.Expression;
+-import org.codehaus.groovy.ast.expr.MapEntryExpression;
+-import org.codehaus.groovy.ast.expr.MethodCallExpression;
+-import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
+-import org.codehaus.groovy.ast.expr.TupleExpression;
+-import org.codehaus.groovy.ast.stmt.BlockStatement;
+-import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+-import org.codehaus.groovy.ast.stmt.ReturnStatement;
+-import org.codehaus.groovy.ast.stmt.Statement;
+-
+-import java.util.List;
+-import java.util.Map;
+-
+-/**
+- * Implementation of the {@link GradleDetector} using a real Groovy AST,
+- * which the Gradle plugin has access to
+- */
+-public class GroovyGradleDetector extends GradleDetector {
+- static final Implementation IMPLEMENTATION = new Implementation(
+- GroovyGradleDetector.class,
+- Scope.GRADLE_SCOPE);
+-
+- @Override
+- public void visitBuildScript(@NonNull final Context context, Map<String, Object> sharedData) {
+- try {
+- visitQuietly(context, sharedData);
+- } catch (Throwable t) {
+- // ignore
+- // Parsing the build script can involve class loading that we sometimes can't
+- // handle. This happens for example when running lint in build-system/tests/api/.
+- // This is a lint limitation rather than a user error, so don't complain
+- // about these. Consider reporting a Issue#LINT_ERROR.
+- }
+- }
+-
+- private void visitQuietly(@NonNull final Context context, Map<String, Object> sharedData) {
+- String source = context.getContents();
+- if (source == null) {
+- return;
+- }
+-
+- List<ASTNode> astNodes = new AstBuilder().buildFromString(source);
+- GroovyCodeVisitor visitor = new CodeVisitorSupport() {
+- private List<MethodCallExpression> mMethodCallStack = Lists.newArrayList();
+- @Override
+- public void visitMethodCallExpression(MethodCallExpression expression) {
+- mMethodCallStack.add(expression);
+- super.visitMethodCallExpression(expression);
+- Expression arguments = expression.getArguments();
+- String parent = expression.getMethodAsString();
+- String parentParent = getParentParent();
+- if (arguments instanceof ArgumentListExpression) {
+- ArgumentListExpression ale = (ArgumentListExpression)arguments;
+- List<Expression> expressions = ale.getExpressions();
+- if (expressions.size() == 1 &&
+- expressions.get(0) instanceof ClosureExpression) {
+- if (isInterestingBlock(parent, parentParent)) {
+- ClosureExpression closureExpression =
+- (ClosureExpression)expressions.get(0);
+- Statement block = closureExpression.getCode();
+- if (block instanceof BlockStatement) {
+- BlockStatement bs = (BlockStatement)block;
+- for (Statement statement : bs.getStatements()) {
+- if (statement instanceof ExpressionStatement) {
+- ExpressionStatement e = (ExpressionStatement)statement;
+- if (e.getExpression() instanceof MethodCallExpression) {
+- checkDslProperty(parent,
+- (MethodCallExpression)e.getExpression(),
+- parentParent);
+- }
+- } else if (statement instanceof ReturnStatement) {
+- // Single item in block
+- ReturnStatement e = (ReturnStatement)statement;
+- if (e.getExpression() instanceof MethodCallExpression) {
+- checkDslProperty(parent,
+- (MethodCallExpression)e.getExpression(),
+- parentParent);
+- }
+- }
+- }
+- }
+- }
+- }
+- } else if (arguments instanceof TupleExpression) {
+- if (isInterestingStatement(parent, parentParent)) {
+- TupleExpression te = (TupleExpression) arguments;
+- Map<String, String> namedArguments = Maps.newHashMap();
+- List<String> unnamedArguments = Lists.newArrayList();
+- for (Expression subExpr : te.getExpressions()) {
+- if (subExpr instanceof NamedArgumentListExpression) {
+- NamedArgumentListExpression nale = (NamedArgumentListExpression) subExpr;
+- for (MapEntryExpression mae : nale.getMapEntryExpressions()) {
+- namedArguments.put(mae.getKeyExpression().getText(),
+- mae.getValueExpression().getText());
+- }
+- }
+- }
+- checkMethodCall(context, parent, parentParent, namedArguments, unnamedArguments, expression);
+- }
+- }
+- assert !mMethodCallStack.isEmpty();
+- assert mMethodCallStack.get(mMethodCallStack.size() - 1) == expression;
+- mMethodCallStack.remove(mMethodCallStack.size() - 1);
+- }
+-
+- private String getParentParent() {
+- for (int i = mMethodCallStack.size() - 2; i >= 0; i--) {
+- MethodCallExpression expression = mMethodCallStack.get(i);
+- Expression arguments = expression.getArguments();
+- if (arguments instanceof ArgumentListExpression) {
+- ArgumentListExpression ale = (ArgumentListExpression)arguments;
+- List<Expression> expressions = ale.getExpressions();
+- if (expressions.size() == 1 &&
+- expressions.get(0) instanceof ClosureExpression) {
+- return expression.getMethodAsString();
+- }
+- }
+- }
+-
+- return null;
+- }
+-
+- private void checkDslProperty(String parent, MethodCallExpression c,
+- String parentParent) {
+- String property = c.getMethodAsString();
+- if (isInterestingProperty(property, parent, getParentParent())) {
+- String value = getText(c.getArguments());
+- checkDslPropertyAssignment(context, property, value, parent, parentParent, c, c);
+- }
+- }
+-
+- private String getText(ASTNode node) {
+- String source = context.getContents();
+- Pair<Integer, Integer> offsets = getOffsets(node, context);
+- return source.substring(offsets.getFirst(), offsets.getSecond());
+- }
+- };
+-
+- for (ASTNode node : astNodes) {
+- node.visit(visitor);
+- }
+- }
+-
+- @NonNull
+- private static Pair<Integer, Integer> getOffsets(ASTNode node, Context context) {
+- if (node.getLastLineNumber() == -1 && node instanceof TupleExpression) {
+- // Workaround: TupleExpressions yield bogus offsets, so use its
+- // children instead
+- TupleExpression exp = (TupleExpression) node;
+- List<Expression> expressions = exp.getExpressions();
+- if (!expressions.isEmpty()) {
+- return Pair.of(
+- getOffsets(expressions.get(0), context).getFirst(),
+- getOffsets(expressions.get(expressions.size() - 1), context).getSecond());
+- }
+- }
+- String source = context.getContents();
+- assert source != null; // because we successfully parsed
+- int start = 0;
+- int end = source.length();
+- int line = 1;
+- int startLine = node.getLineNumber();
+- int startColumn = node.getColumnNumber();
+- int endLine = node.getLastLineNumber();
+- int endColumn = node.getLastColumnNumber();
+- int column = 1;
+- for (int index = 0, len = end; index < len; index++) {
+- if (line == startLine && column == startColumn) {
+- start = index;
+- }
+- if (line == endLine && column == endColumn) {
+- end = index;
+- break;
+- }
+-
+- char c = source.charAt(index);
+- if (c == '\n') {
+- line++;
+- column = 1;
+- } else {
+- column++;
+- }
+- }
+-
+- return Pair.of(start, end);
+- }
+-
+- @Override
+- protected int getStartOffset(@NonNull Context context, @NonNull Object cookie) {
+- ASTNode node = (ASTNode) cookie;
+- Pair<Integer, Integer> offsets = getOffsets(node, context);
+- return offsets.getFirst();
+- }
+-
+- @Override
+- protected Location createLocation(@NonNull Context context, @NonNull Object cookie) {
+- ASTNode node = (ASTNode) cookie;
+- Pair<Integer, Integer> offsets = getOffsets(node, context);
+- int fromLine = node.getLineNumber() - 1;
+- int fromColumn = node.getColumnNumber() - 1;
+- int toLine = node.getLastLineNumber() - 1;
+- int toColumn = node.getLastColumnNumber() - 1;
+- return Location.create(context.file,
+- new DefaultPosition(fromLine, fromColumn, offsets.getFirst()),
+- new DefaultPosition(toLine, toColumn, offsets.getSecond()));
+- }
+-}
+\ No newline at end of file
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
+deleted file mode 100644
+index d3b4e42..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
++++ /dev/null
+@@ -1,295 +0,0 @@
+-/*
+- * Copyright (C) 2013 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.build.gradle.tasks
+-import com.android.annotations.NonNull
+-import com.android.annotations.Nullable
+-import com.android.build.gradle.internal.LintGradleClient
+-import com.android.build.gradle.internal.dsl.LintOptions
+-import com.android.build.gradle.internal.scope.TaskConfigAction
+-import com.android.build.gradle.internal.scope.VariantScope
+-import com.android.build.gradle.internal.tasks.DefaultAndroidTask
+-import com.android.builder.model.AndroidProject
+-import com.android.builder.model.Variant
+-import com.android.tools.lint.LintCliFlags
+-import com.android.tools.lint.Reporter
+-import com.android.tools.lint.Warning
+-import com.android.tools.lint.checks.BuiltinIssueRegistry
+-import com.android.tools.lint.checks.GradleDetector
+-import com.android.tools.lint.client.api.IssueRegistry
+-import com.android.tools.lint.detector.api.Issue
+-import com.android.tools.lint.detector.api.Severity
+-import com.google.common.collect.Maps
+-import org.gradle.api.GradleException
+-import org.gradle.api.Project
+-import org.gradle.api.plugins.JavaBasePlugin
+-import org.gradle.api.tasks.ParallelizableTask
+-import org.gradle.api.tasks.TaskAction
+-import org.gradle.tooling.provider.model.ToolingModelBuilder
+-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
+-
+- at ParallelizableTask
+-public class Lint extends DefaultAndroidTask {
+- @NonNull private LintOptions mLintOptions
+- @Nullable private File mSdkHome
+- private boolean mFatalOnly
+- private ToolingModelBuilderRegistry mToolingRegistry
+-
+- public void setLintOptions(@NonNull LintOptions lintOptions) {
+- mLintOptions = lintOptions
+- }
+-
+- public void setSdkHome(@NonNull File sdkHome) {
+- mSdkHome = sdkHome
+- }
+-
+- void setToolingRegistry(ToolingModelBuilderRegistry toolingRegistry) {
+- mToolingRegistry = toolingRegistry
+- }
+-
+- public void setFatalOnly(boolean fatalOnly) {
+- mFatalOnly = fatalOnly
+- }
+-
+- @SuppressWarnings("GroovyUnusedDeclaration")
+- @TaskAction
+- public void lint() {
+- def modelProject = createAndroidProject(project)
+- if (getVariantName() != null) {
+- lintSingleVariant(modelProject, getVariantName())
+- } else {
+- lintAllVariants(modelProject)
+- }
+- }
+-
+- /**
+- * Runs lint individually on all the variants, and then compares the results
+- * across variants and reports these
+- */
+- public void lintAllVariants(@NonNull AndroidProject modelProject) {
+- Map<Variant,List<Warning>> warningMap = Maps.newHashMap()
+- for (Variant variant : modelProject.getVariants()) {
+- try {
+- List<Warning> warnings = runLint(modelProject, variant.getName(), false)
+- warningMap.put(variant, warnings)
+- } catch (IOException e) {
+- throw new GradleException("Invalid arguments.", e)
+- }
+- }
+-
+- // Compute error matrix
+- def quiet = mLintOptions.quiet
+-
+-
+- for (Map.Entry<Variant,List<Warning>> entry : warningMap.entrySet()) {
+- def variant = entry.getKey()
+- def warnings = entry.getValue()
+- if (!mFatalOnly && !quiet) {
+- println "Ran lint on variant " + variant.getName() + ": " + warnings.size() +
+- " issues found"
+- }
+- }
+-
+- List<Warning> mergedWarnings = LintGradleClient.merge(warningMap, modelProject)
+- int errorCount = 0
+- int warningCount = 0
+- for (Warning warning : mergedWarnings) {
+- if (warning.severity == Severity.ERROR || warning.severity == Severity.FATAL) {
+- errorCount++
+- } else if (warning.severity == Severity.WARNING) {
+- warningCount++
+- }
+- }
+-
+- IssueRegistry registry = new BuiltinIssueRegistry()
+- LintCliFlags flags = new LintCliFlags()
+- LintGradleClient client = new LintGradleClient(registry, flags, project, modelProject,
+- mSdkHome, null)
+- syncOptions(mLintOptions, client, flags, null, project, true, mFatalOnly)
+-
+- for (Reporter reporter : flags.getReporters()) {
+- reporter.write(errorCount, warningCount, mergedWarnings)
+- }
+-
+- if (flags.isSetExitCode() && errorCount > 0) {
+- abort()
+- }
+- }
+-
+- private void abort() {
+- def message;
+- if (mFatalOnly) {
+- message = "" +
+- "Lint found fatal errors while assembling a release target.\n" +
+- "\n" +
+- "To proceed, either fix the issues identified by lint, or modify your build script as follows:\n" +
+- "...\n" +
+- "android {\n" +
+- " lintOptions {\n" +
+- " checkReleaseBuilds false\n" +
+- " // Or, if you prefer, you can continue to check for errors in release builds,\n" +
+- " // but continue the build even when errors are found:\n" +
+- " abortOnError false\n" +
+- " }\n" +
+- "}\n" +
+- "..."
+- ""
+- } else {
+- message = "" +
+- "Lint found errors in the project; aborting build.\n" +
+- "\n" +
+- "Fix the issues identified by lint, or add the following to your build script to proceed with errors:\n" +
+- "...\n" +
+- "android {\n" +
+- " lintOptions {\n" +
+- " abortOnError false\n" +
+- " }\n" +
+- "}\n" +
+- "..."
+- }
+- throw new GradleException(message);
+- }
+-
+- /**
+- * Runs lint on a single specified variant
+- */
+- public void lintSingleVariant(@NonNull AndroidProject modelProject, String variantName) {
+- runLint(modelProject, variantName, true)
+- }
+-
+- /** Runs lint on the given variant and returns the set of warnings */
+- private List<Warning> runLint(
+- @NonNull AndroidProject modelProject,
+- @NonNull String variantName,
+- boolean report) {
+- IssueRegistry registry = createIssueRegistry()
+- LintCliFlags flags = new LintCliFlags()
+- LintGradleClient client = new LintGradleClient(registry, flags, project, modelProject,
+- mSdkHome, variantName)
+- if (mFatalOnly) {
+- if (!mLintOptions.isCheckReleaseBuilds()) {
+- return
+- }
+- flags.setFatalOnly(true)
+- }
+- syncOptions(mLintOptions, client, flags, variantName, project, report, mFatalOnly)
+- if (!report || mFatalOnly) {
+- flags.setQuiet(true)
+- }
+-
+- List<Warning> warnings;
+- try {
+- warnings = client.run(registry)
+- } catch (IOException e) {
+- throw new GradleException("Invalid arguments.", e)
+- }
+-
+- if (report && client.haveErrors() && flags.isSetExitCode()) {
+- abort()
+- }
+-
+- return warnings;
+- }
+-
+- private static syncOptions(
+- @NonNull LintOptions options,
+- @NonNull LintGradleClient client,
+- @NonNull LintCliFlags flags,
+- @NonNull String variantName,
+- @NonNull Project project,
+- boolean report,
+- boolean fatalOnly) {
+- options.syncTo(client, flags, variantName, project, report)
+-
+- if (fatalOnly || flags.quiet) {
+- for (Reporter reporter : flags.getReporters()) {
+- reporter.setDisplayEmpty(false)
+- }
+- }
+- }
+-
+- private AndroidProject createAndroidProject(@NonNull Project gradleProject) {
+- String modelName = AndroidProject.class.getName()
+- ToolingModelBuilder modelBuilder = mToolingRegistry.getBuilder(modelName)
+- assert modelBuilder != null
+- return (AndroidProject) modelBuilder.buildAll(modelName, gradleProject)
+- }
+-
+- private static BuiltinIssueRegistry createIssueRegistry() {
+- return new LintGradleIssueRegistry()
+- }
+-
+- // Issue registry when Lint is run inside Gradle: we replace the Gradle
+- // detector with a local implementation which directly references Groovy
+- // for parsing. In Studio on the other hand, the implementation is replaced
+- // by a PSI-based check. (This is necessary for now since we don't have a
+- // tool-agnostic API for the Groovy AST and we don't want to add a 6.3MB dependency
+- // on Groovy itself quite yet.
+- public static class LintGradleIssueRegistry extends BuiltinIssueRegistry {
+- private boolean mInitialized;
+-
+- public LintGradleIssueRegistry() {
+- }
+-
+- @NonNull
+- @Override
+- public List<Issue> getIssues() {
+- List<Issue> issues = super.getIssues();
+- if (!mInitialized) {
+- mInitialized = true;
+- for (Issue issue : issues) {
+- if (issue.getImplementation().getDetectorClass() == GradleDetector.class) {
+- issue.setImplementation(GroovyGradleDetector.IMPLEMENTATION);
+- }
+- }
+- }
+-
+- return issues;
+- }
+- }
+-
+- public static class ConfigAction implements TaskConfigAction<Lint> {
+-
+- @NonNull
+- VariantScope scope
+-
+- ConfigAction(@NonNull VariantScope scope) {
+- this.scope = scope
+- }
+-
+- @Override
+- @NonNull
+- String getName() {
+- return scope.getTaskName("lint")
+- }
+-
+- @Override
+- @NonNull
+- Class<Lint> getType() {
+- return Lint
+- }
+-
+- @Override
+- void execute(Lint lint) {
+- lint.setLintOptions(scope.globalScope.getExtension().lintOptions)
+- lint.setSdkHome(scope.globalScope.sdkHandler.getSdkFolder())
+- lint.setVariantName(scope.variantConfiguration.fullName)
+- lint.setToolingRegistry(scope.globalScope.toolingRegistry)
+- lint.description = "Runs lint on the " + scope.variantConfiguration.fullName.capitalize() + " build."
+- lint.group = JavaBasePlugin.VERIFICATION_GROUP
+- }
+- }
+-}
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
+index 16ab90c..52f085b 100644
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
++++ b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
+@@ -286,22 +286,6 @@ public class PackageApplication extends IncrementalTask implements FileSupplier
+ packageApp.setVariantName(
+ scope.getVariantScope().getVariantConfiguration().getFullName());
+
+- if (config.isMinifyEnabled() && config.getBuildType().isShrinkResources() && !config
+- .getUseJack()) {
+- ConventionMappingHelper.map(packageApp, "resourceFile", new Callable<File>() {
+- @Override
+- public File call() {
+- return scope.getCompressedResourceFile();
+- }
+- });
+- } else {
+- ConventionMappingHelper.map(packageApp, "resourceFile", new Callable<File>() {
+- @Override
+- public File call() {
+- return variantOutputData.processResourcesTask.getPackageOutputFile();
+- }
+- });
+- }
+
+ ConventionMappingHelper.map(packageApp, "dexFolder", new Callable<File>() {
+ @Override
+@@ -437,36 +421,6 @@ public class PackageApplication extends IncrementalTask implements FileSupplier
+ });
+ }
+
+- private ShrinkResources createShrinkResourcesTask(
+- final ApkVariantOutputData variantOutputData) {
+- BaseVariantData<?> variantData = (BaseVariantData<?>) variantOutputData.variantData;
+- ShrinkResources task = scope.getGlobalScope().getProject().getTasks()
+- .create("shrink" + StringGroovyMethods
+- .capitalize(variantOutputData.getFullName())
+- + "Resources", ShrinkResources.class);
+- task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+- task.setVariantName(scope.getVariantScope().getVariantConfiguration().getFullName());
+- task.variantOutputData = variantOutputData;
+-
+- final String outputBaseName = variantOutputData.getBaseName();
+- task.setCompressedResources(new File(
+- scope.getGlobalScope().getBuildDir() + "/" + FD_INTERMEDIATES + "/res/" +
+- "resources-" + outputBaseName + "-stripped.ap_"));
+-
+- ConventionMappingHelper.map(task, "uncompressedResources", new Callable<File>() {
+- @Override
+- public File call() {
+- return variantOutputData.processResourcesTask.getPackageOutputFile();
+- }
+- });
+-
+- task.dependsOn(
+- scope.getVariantScope().getObfuscationTask().getName(),
+- scope.getManifestProcessorTask().getName(),
+- variantOutputData.processResourcesTask);
+-
+- return task;
+- }
+
+ private static File getOptionalDir(File dir) {
+ if (dir.isDirectory()) {
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
+deleted file mode 100644
+index 649a8d0..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
++++ /dev/null
+@@ -1,2568 +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.
+- */
+-
+-package com.android.build.gradle.tasks;
+-
+-import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX;
+-import static com.android.SdkConstants.ANDROID_URI;
+-import static com.android.SdkConstants.ATTR_NAME;
+-import static com.android.SdkConstants.ATTR_PARENT;
+-import static com.android.SdkConstants.ATTR_TYPE;
+-import static com.android.SdkConstants.DOT_CLASS;
+-import static com.android.SdkConstants.DOT_GIF;
+-import static com.android.SdkConstants.DOT_JPEG;
+-import static com.android.SdkConstants.DOT_JPG;
+-import static com.android.SdkConstants.DOT_PNG;
+-import static com.android.SdkConstants.DOT_XML;
+-import static com.android.SdkConstants.FD_RES_VALUES;
+-import static com.android.SdkConstants.PREFIX_ANDROID;
+-import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX;
+-import static com.android.SdkConstants.TAG_ITEM;
+-import static com.android.SdkConstants.TAG_RESOURCES;
+-import static com.android.SdkConstants.TAG_STYLE;
+-import static com.android.SdkConstants.TOOLS_URI;
+-import static com.android.utils.SdkUtils.endsWith;
+-import static com.android.utils.SdkUtils.endsWithIgnoreCase;
+-import static com.google.common.base.Charsets.UTF_8;
+-import static org.objectweb.asm.ClassReader.SKIP_DEBUG;
+-import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
+-
+-import com.android.SdkConstants;
+-import com.android.annotations.NonNull;
+-import com.android.annotations.Nullable;
+-import com.android.annotations.VisibleForTesting;
+-import com.android.ide.common.resources.ResourceUrl;
+-import com.android.ide.common.resources.configuration.DensityQualifier;
+-import com.android.ide.common.resources.configuration.FolderConfiguration;
+-import com.android.ide.common.resources.configuration.ResourceQualifier;
+-import com.android.ide.common.xml.XmlPrettyPrinter;
+-import com.android.resources.FolderTypeRelationship;
+-import com.android.resources.ResourceFolderType;
+-import com.android.resources.ResourceType;
+-import com.android.tools.lint.checks.StringFormatDetector;
+-import com.android.tools.lint.client.api.DefaultConfiguration;
+-import com.android.tools.lint.detector.api.LintUtils;
+-import com.android.utils.XmlUtils;
+-import com.google.common.base.Joiner;
+-import com.google.common.base.Splitter;
+-import com.google.common.collect.Lists;
+-import com.google.common.collect.Maps;
+-import com.google.common.collect.Sets;
+-import com.google.common.io.ByteStreams;
+-import com.google.common.io.Closeables;
+-import com.google.common.io.Files;
+-
+-import org.objectweb.asm.AnnotationVisitor;
+-import org.objectweb.asm.ClassReader;
+-import org.objectweb.asm.ClassVisitor;
+-import org.objectweb.asm.FieldVisitor;
+-import org.objectweb.asm.MethodVisitor;
+-import org.objectweb.asm.Opcodes;
+-import org.w3c.dom.Attr;
+-import org.w3c.dom.Document;
+-import org.w3c.dom.Element;
+-import org.w3c.dom.NamedNodeMap;
+-import org.w3c.dom.Node;
+-import org.w3c.dom.NodeList;
+-import org.xml.sax.SAXException;
+-
+-import java.io.File;
+-import java.io.FileInputStream;
+-import java.io.FileOutputStream;
+-import java.io.IOException;
+-import java.util.ArrayList;
+-import java.util.Collections;
+-import java.util.Comparator;
+-import java.util.IdentityHashMap;
+-import java.util.List;
+-import java.util.Map;
+-import java.util.Set;
+-import java.util.jar.JarEntry;
+-import java.util.jar.JarInputStream;
+-import java.util.jar.JarOutputStream;
+-import java.util.regex.Matcher;
+-import java.util.regex.Pattern;
+-import java.util.regex.PatternSyntaxException;
+-import java.util.zip.ZipEntry;
+-import java.util.zip.ZipInputStream;
+-
+-import javax.xml.parsers.ParserConfigurationException;
+-
+-/**
+- * Class responsible for searching through a Gradle built tree (after resource merging,
+- * compilation and ProGuarding has been completed, but before final .apk assembly), which
+- * figures out which resources if any are unused, and removes them.
+- * <p>
+- * It does this by examining
+- * <ul>
+- * <li>The merged manifest, to find root resource references (such as drawables
+- * used for activity icons)</li>
+- * <li>The merged R class (to find the actual integer constants assigned to resources)</li>
+- * <li>The ProGuard log files (to find the mapping from original symbol names to
+- * short names)</li>*
+- * <li>The merged resources (to find which resources reference other resources, e.g.
+- * drawable state lists including other drawables, or layouts including other
+- * layouts, or styles referencing other drawables, or menus items including action
+- * layouts, etc.)</li>
+- * <li>The ProGuard output classes (to find resource references in code that are
+- * actually reachable)</li>
+- * </ul>
+- * From all this, it builds up a reference graph, and based on the root references (e.g.
+- * from the manifest and from the remaining code) it computes which resources are actually
+- * reachable in the app, and anything that is not reachable is then marked for deletion.
+- * <p>
+- * A resource is referenced in code if either the field R.type.name is referenced (which
+- * is the case for non-final resource references, e.g. in libraries), or if the corresponding
+- * int value is referenced (for final resource values). We check this by looking at the
+- * ProGuard output classes with an ASM visitor. One complication is that code can also
+- * call {@code Resources#getIdentifier(String,String,String)} where they can pass in the names
+- * of resources to look up. To handle this scenario, we use the ClassVisitor to see if
+- * there are any calls to the specific {@code Resources#getIdentifier} method. If not,
+- * great, the usage analysis is completely accurate. If we <b>do</b> find one, we check
+- * <b>all</b> the string constants found anywhere in the app, and look to see if any look
+- * relevant. For example, if we find the string "string/foo" or "my.pkg:string/foo", we
+- * will then mark the string resource named foo (if any) as potentially used. Similarly,
+- * if we find just "foo" or "/foo", we will mark <b>all</b> resources named "foo" as
+- * potentially used. However, if the string is "bar/foo" or " foo " these strings are
+- * ignored. This means we can potentially miss resources usages where the resource name
+- * is completed computed (e.g. by concatenating individual characters or taking substrings
+- * of strings that do not look like resource names), but that seems extremely unlikely
+- * to be a real-world scenario.
+- * <p>
+- * For now, for reasons detailed in the code, this only applies to file-based resources
+- * like layouts, menus and drawables, not value-based resources like strings and dimensions.
+- */
+-public class ResourceUsageAnalyzer {
+- private static final String ANDROID_RES = "android_res/";
+-
+- /**
+- Whether we support running aapt twice, to regenerate the resources.arsc file
+- such that we can strip out value resources as well. We don't do this yet, for
+- reasons detailed in the ShrinkResources task
+-
+- We have two options:
+- (1) Copy the resource files over to a new destination directory, filtering out
+- removed file resources and rewriting value resource files by stripping out
+- the declarations for removed value resources. We then re-run aapt on this
+- new destination directory.
+-
+- The problem with this approach is that when we re-run aapt it will assign new
+- id's to all the resources, so we have to create dummy placeholders for all the
+- removed resources. (The alternative would be to then run compilation one more
+- time -- regenerating classes.jar, regenerating .dex) -- this would really slow
+- down builds.)
+-
+- A cleaner solution than this is to get aapt to support using a predefined set
+- of id's. It can emit R.txt symbol files now; if we can get it to read R.txt
+- and use those numbers in its assignment, we can solve this cleanly. This request
+- is tracked in https://code.google.com/p/android/issues/detail?id=70869
+-
+- (2) Just rewrite the .ap_ file directly. It's just a .zip file which contains
+- (a) binary files for bitmaps and XML file resources such as layouts and menus
+- (b) a binary file, resources.arsc, containing all the values.
+- The resources.arsc format is opaque to us. However, MOST of the resource bulk
+- comes from the bitmap and other resource files.
+-
+- So here we don't even need to run aapt a second time; we simply rewrite the
+- .ap_ zip file directly, filtering out res/ files we know to be unused.
+-
+- Approach #2 gives us most of the space savings without the risk of #1 (running aapt
+- a second time introduces the possibility of aapt compilation errors if we haven't
+- been careful enough to insert resource aliases for all necessary items (such as
+- inline @+id declarations), or if we haven't carefully not created aliases for items
+- already defined in other value files as aliases, and perhaps most importantly,
+- introduces risk that aapt will pick a different resource order anyway, which we can
+- only guard against by doing a full compilation over again.
+-
+- Therefore, for now the below code uses #2, but since we can solve #1 with support
+- from aapt), we're preserving all the code to rewrite resource files since that will
+- give additional space savings, particularly for apps with a lot of strings or a lot
+- of translations.
+- */
+- @SuppressWarnings("SpellCheckingInspection") // arsc
+- public static final boolean TWO_PASS_AAPT = false;
+- public static final int TYPICAL_RESOURCE_COUNT = 200;
+-
+- /** Name of keep attribute in XML */
+- private static final String ATTR_KEEP = "keep";
+- /** Name of discard attribute in XML (to mark resources as not referenced, despite guesses) */
+- private static final String ATTR_DISCARD = "discard";
+- /** Name of attribute in XML to control whether we should guess resources to keep */
+- private static final String ATTR_SHRINK_MODE = "shrinkMode";
+- /** @{linkplain #ATTR_SHRINK_MODE} value to only shrink explicitly encountered resources */
+- private static final String VALUE_STRICT = "strict";
+- /** @{linkplain #ATTR_SHRINK_MODE} value to keep possibly referenced resources */
+- private static final String VALUE_SAFE = "safe";
+-
+- /** Special marker regexp which does not match a resource name */
+- static final String NO_MATCH = "-nomatch-";
+-
+- private final File mResourceClassDir;
+- private final File mProguardMapping;
+- private final File mClassesJar;
+- private final File mMergedManifest;
+- private final File mMergedResourceDir;
+-
+- private boolean mVerbose;
+- private boolean mDebug;
+- private boolean mDryRun;
+-
+- /** The computed set of unused resources */
+- private List<Resource> mUnused;
+-
+- /** List of all known resources (parsed from R.java) */
+- private List<Resource> mResources = Lists.newArrayListWithExpectedSize(TYPICAL_RESOURCE_COUNT);
+- /** Map from R field value to corresponding resource */
+- private Map<Integer, Resource> mValueToResource =
+- Maps.newHashMapWithExpectedSize(TYPICAL_RESOURCE_COUNT);
+- /** Map from resource type to map from resource name to resource object */
+- private Map<ResourceType, Map<String, Resource>> mTypeToName =
+- Maps.newEnumMap(ResourceType.class);
+- /** Map from resource class owners (VM format class) to corresponding resource types.
+- * This will typically be the fully qualified names of the R classes, as well as
+- * any renamed versions of those discovered in the mapping.txt file from ProGuard */
+- private Map<String, ResourceType> mResourceClassOwners = Maps.newHashMapWithExpectedSize(20);
+-
+- /**
+- * Whether we should attempt to guess resources that should be kept based on looking
+- * at the string pool and assuming some of the strings can be used to dynamically construct
+- * the resource names. Can be turned off via {@code tools:guessKeep="false"}.
+- */
+- private boolean mGuessKeep = true;
+-
+- public ResourceUsageAnalyzer(
+- @NonNull File rDir,
+- @NonNull File classesJar,
+- @NonNull File manifest,
+- @Nullable File mapping,
+- @NonNull File resources) {
+- mResourceClassDir = rDir;
+- mProguardMapping = mapping;
+- mClassesJar = classesJar;
+- mMergedManifest = manifest;
+- mMergedResourceDir = resources;
+- }
+-
+- public void analyze() throws IOException, ParserConfigurationException, SAXException {
+- gatherResourceValues(mResourceClassDir);
+- recordMapping(mProguardMapping);
+- recordUsages(mClassesJar);
+- recordManifestUsages(mMergedManifest);
+- recordResources(mMergedResourceDir);
+- keepPossiblyReferencedResources();
+- dumpReferences();
+- findUnused();
+- }
+-
+- public boolean isDryRun() {
+- return mDryRun;
+- }
+-
+- public void setDryRun(boolean dryRun) {
+- mDryRun = dryRun;
+- }
+-
+- public boolean isVerbose() {
+- return mVerbose;
+- }
+-
+- public void setVerbose(boolean verbose) {
+- mVerbose = verbose;
+- }
+-
+-
+- public boolean isDebug() {
+- return mDebug;
+- }
+-
+- public void setDebug(boolean verbose) {
+- mDebug = verbose;
+- }
+-
+- /**
+- * "Removes" resources from an .ap_ file by writing it out while filtering out
+- * unused resources. This won't touch the values XML data (resources.arsc) but
+- * will remove the individual file-based resources, which is where most of
+- * the data is anyway (usually in drawable bitmaps)
+- *
+- * @param source the .ap_ file created by aapt
+- * @param dest a new .ap_ file with unused file-based resources removed
+- */
+- public void rewriteResourceZip(@NonNull File source, @NonNull File dest)
+- throws IOException {
+- if (dest.exists()) {
+- boolean deleted = dest.delete();
+- if (!deleted) {
+- throw new IOException("Could not delete " + dest);
+- }
+- }
+-
+- JarInputStream zis = null;
+- try {
+- FileInputStream fis = new FileInputStream(source);
+- try {
+- FileOutputStream fos = new FileOutputStream(dest);
+- zis = new JarInputStream(fis);
+- JarOutputStream zos = new JarOutputStream(fos);
+- try {
+- // Rather than using Deflater.DEFAULT_COMPRESSION we use 9 here,
+- // since that seems to match the compressed sizes we observe in source
+- // .ap_ files encountered by the resource shrinker:
+- zos.setLevel(9);
+-
+- ZipEntry entry = zis.getNextEntry();
+- while (entry != null) {
+- String name = entry.getName();
+- boolean directory = entry.isDirectory();
+- Resource resource = getResourceByJarPath(name);
+- if (resource == null || resource.reachable) {
+- // We can't just compress all files; files that are not
+- // compressed in the source .ap_ file must be left uncompressed
+- // here, since for example RAW files need to remain uncompressed in
+- // the APK such that they can be mmap'ed at runtime.
+- // Preserve the STORED method of the input entry.
+- JarEntry outEntry;
+- if (entry.getMethod() == JarEntry.STORED) {
+- outEntry = new JarEntry(entry);
+- } else {
+- // Create a new entry so that the compressed len is recomputed.
+- outEntry = new JarEntry(name);
+- if (entry.getTime() != -1L) {
+- outEntry.setTime(entry.getTime());
+- }
+- }
+-
+- zos.putNextEntry(outEntry);
+-
+- if (!directory) {
+- byte[] bytes = ByteStreams.toByteArray(zis);
+- if (bytes != null) {
+- zos.write(bytes);
+- }
+- }
+-
+- zos.closeEntry();
+- } else if (isVerbose()) {
+- System.out.println("Skipped unused resource " + name + ": "
+- + entry.getSize() + " bytes");
+- }
+- entry = zis.getNextEntry();
+- }
+- zos.flush();
+- } finally {
+- Closeables.close(zos, false);
+- }
+- } finally {
+- Closeables.close(fis, true);
+- }
+- } finally {
+- Closeables.close(zis, false);
+- }
+- }
+-
+- /**
+- * Remove resources (already identified by {@link #analyze()}).
+- *
+- * This task will copy all remaining used resources over from the full resource
+- * directory to a new reduced resource directory. However, it can't just
+- * delete the resources, because it has no way to tell aapt to continue to use
+- * the same id's for the resources. When we re-run aapt on the stripped resource
+- * directory, it will assign new id's to some of the resources (to fill the gaps)
+- * which means the resource id's no longer match the constants compiled into the
+- * dex files, and as a result, the app crashes at runtime.
+- * <p>
+- * Therefore, it needs to preserve all id's by actually keeping all the resource
+- * names. It can still save a lot of space by making these resources tiny; e.g.
+- * all strings are set to empty, all styles, arrays and plurals are set to not contain
+- * any children, and most importantly, all file based resources like bitmaps and
+- * layouts are replaced by simple resource aliases which just point to @null.
+- *
+- * @param destination directory to copy resources into; if null, delete resources in place
+- * @throws IOException
+- * @throws ParserConfigurationException
+- * @throws SAXException
+- */
+- public void removeUnused(@Nullable File destination) throws IOException,
+- ParserConfigurationException, SAXException {
+- if (TWO_PASS_AAPT) {
+- assert mUnused != null; // should always call analyze() first
+-
+- int resourceCount = mUnused.size()
+- * 4; // *4: account for some resource folder repetition
+- boolean inPlace = destination == null;
+- Set<File> skip = inPlace ? null : Sets.<File>newHashSetWithExpectedSize(resourceCount);
+- Set<File> rewrite = Sets.newHashSetWithExpectedSize(resourceCount);
+- for (Resource resource : mUnused) {
+- if (resource.declarations != null) {
+- for (File file : resource.declarations) {
+- String folder = file.getParentFile().getName();
+- ResourceFolderType folderType = ResourceFolderType.getFolderType(folder);
+- if (folderType != null && folderType != ResourceFolderType.VALUES) {
+- if (isVerbose()) {
+- System.out.println("Deleted unused resource " + file);
+- }
+- if (inPlace) {
+- if (!isDryRun()) {
+- boolean delete = file.delete();
+- if (!delete) {
+- System.err.println("Could not delete " + file);
+- }
+- }
+- } else {
+- assert skip != null;
+- skip.add(file);
+- }
+- } else {
+- // Can't delete values immediately; there can be many resources
+- // in this file, so we have to process them all
+- rewrite.add(file);
+- }
+- }
+- }
+- }
+-
+- // Special case the base values.xml folder
+- File values = new File(mMergedResourceDir,
+- FD_RES_VALUES + File.separatorChar + "values.xml");
+- boolean valuesExists = values.exists();
+- if (valuesExists) {
+- rewrite.add(values);
+- }
+-
+- Map<File, String> rewritten = Maps.newHashMapWithExpectedSize(rewrite.size());
+-
+- // Delete value resources: Must rewrite the XML files
+- for (File file : rewrite) {
+- String xml = Files.toString(file, UTF_8);
+- Document document = XmlUtils.parseDocument(xml, true);
+- Element root = document.getDocumentElement();
+- if (root != null && TAG_RESOURCES.equals(root.getTagName())) {
+- List<String> removed = Lists.newArrayList();
+- stripUnused(root, removed);
+- if (isVerbose()) {
+- System.out.println("Removed " + removed.size() +
+- " unused resources from " + file + ":\n " +
+- Joiner.on(", ").join(removed));
+- }
+-
+- String formatted = XmlPrettyPrinter.prettyPrint(document, xml.endsWith("\n"));
+- rewritten.put(file, formatted);
+- }
+- }
+-
+- if (isDryRun()) {
+- return;
+- }
+-
+- if (valuesExists) {
+- String xml = rewritten.get(values);
+- if (xml == null) {
+- xml = Files.toString(values, UTF_8);
+- }
+- Document document = XmlUtils.parseDocument(xml, true);
+- Element root = document.getDocumentElement();
+-
+- for (Resource resource : mResources) {
+- if (resource.type == ResourceType.ID && !resource.hasDefault) {
+- Element item = document.createElement(TAG_ITEM);
+- item.setAttribute(ATTR_TYPE, resource.type.getName());
+- item.setAttribute(ATTR_NAME, resource.name);
+- root.appendChild(item);
+- } else if (!resource.reachable
+- && !resource.hasDefault
+- && resource.type != ResourceType.DECLARE_STYLEABLE
+- && resource.type != ResourceType.STYLE
+- && resource.type != ResourceType.PLURALS
+- && resource.type != ResourceType.ARRAY
+- && resource.isRelevantType()) {
+- Element item = document.createElement(TAG_ITEM);
+- item.setAttribute(ATTR_TYPE, resource.type.getName());
+- item.setAttribute(ATTR_NAME, resource.name);
+- root.appendChild(item);
+- String s = "@null";
+- item.appendChild(document.createTextNode(s));
+- }
+- }
+-
+- String formatted = XmlPrettyPrinter.prettyPrint(document, xml.endsWith("\n"));
+- rewritten.put(values, formatted);
+- }
+-
+- if (inPlace) {
+- for (Map.Entry<File, String> entry : rewritten.entrySet()) {
+- File file = entry.getKey();
+- String formatted = entry.getValue();
+- Files.write(formatted, file, UTF_8);
+- }
+- } else {
+- filteredCopy(mMergedResourceDir, destination, skip, rewritten);
+- }
+- } else {
+- assert false;
+- }
+- }
+-
+- /**
+- * Copies one resource directory tree into another; skipping some files, replacing
+- * the contents of some, and passing everything else through unmodified
+- */
+- private static void filteredCopy(File source, File destination, Set<File> skip,
+- Map<File, String> replace) throws IOException {
+- if (TWO_PASS_AAPT) {
+- if (source.isDirectory()) {
+- File[] children = source.listFiles();
+- if (children != null) {
+- if (!destination.exists()) {
+- boolean success = destination.mkdirs();
+- if (!success) {
+- throw new IOException("Could not create " + destination);
+- }
+- }
+- for (File child : children) {
+- filteredCopy(child, new File(destination, child.getName()), skip, replace);
+- }
+- }
+- } else if (!skip.contains(source) && source.isFile()) {
+- String contents = replace.get(source);
+- if (contents != null) {
+- Files.write(contents, destination, UTF_8);
+- } else {
+- Files.copy(source, destination);
+- }
+- }
+- } else {
+- assert false;
+- }
+- }
+-
+- private void stripUnused(Element element, List<String> removed) {
+- if (TWO_PASS_AAPT) {
+- ResourceType type = getResourceType(element);
+- if (type == ResourceType.ATTR) {
+- // Not yet properly handled
+- return;
+- }
+-
+- Resource resource = getResource(element);
+- if (resource != null) {
+- if (resource.type == ResourceType.DECLARE_STYLEABLE ||
+- resource.type == ResourceType.ATTR) {
+- // Don't strip children of declare-styleable; we're not correctly
+- // tracking field references of the R_styleable_attr fields yet
+- return;
+- }
+-
+- if (!resource.reachable &&
+- (resource.type == ResourceType.STYLE ||
+- resource.type == ResourceType.PLURALS ||
+- resource.type == ResourceType.ARRAY)) {
+- NodeList children = element.getChildNodes();
+- for (int i = children.getLength() - 1; i >= 0; i--) {
+- Node child = children.item(i);
+- element.removeChild(child);
+- }
+- return;
+- }
+- }
+-
+- NodeList children = element.getChildNodes();
+- for (int i = children.getLength() - 1; i >= 0; i--) {
+- Node child = children.item(i);
+- if (child.getNodeType() == Node.ELEMENT_NODE) {
+- stripUnused((Element) child, removed);
+- }
+- }
+-
+- if (resource != null && !resource.reachable) {
+- if (mVerbose) {
+- removed.add(resource.getUrl());
+- }
+- // for themes etc where .'s have been replaced by _'s
+- String name = element.getAttribute(ATTR_NAME);
+- if (name.isEmpty()) {
+- name = resource.name;
+- }
+- Node nextSibling = element.getNextSibling();
+- Node parent = element.getParentNode();
+- NodeList oldChildren = element.getChildNodes();
+- parent.removeChild(element);
+- Document document = element.getOwnerDocument();
+- element = document.createElement("item");
+- for (int i = 0; i < oldChildren.getLength(); i++) {
+- element.appendChild(oldChildren.item(i));
+- }
+-
+- element.setAttribute(ATTR_NAME, name);
+- element.setAttribute(ATTR_TYPE, resource.type.getName());
+- String text = null;
+- switch (resource.type) {
+- case BOOL:
+- text = "true";
+- break;
+- case DIMEN:
+- text = "0dp";
+- break;
+- case INTEGER:
+- text = "0";
+- break;
+- }
+- element.setTextContent(text);
+- parent.insertBefore(element, nextSibling);
+- }
+- } else {
+- assert false;
+- }
+- }
+-
+- private static String getFieldName(Element element) {
+- return getFieldName(element.getAttribute(ATTR_NAME));
+- }
+-
+- @Nullable
+- private Resource getResource(Element element) {
+- ResourceType type = getResourceType(element);
+- if (type != null) {
+- String name = getFieldName(element);
+- return getResource(type, name);
+- }
+-
+- return null;
+- }
+-
+- @Nullable
+- private Resource getResourceByJarPath(String path) {
+- // Jars use forward slash paths, not File.separator
+- if (path.startsWith("res/")) {
+- int folderStart = 4; // "res/".length
+- int folderEnd = path.indexOf('/', folderStart);
+- if (folderEnd != -1) {
+- String folderName = path.substring(folderStart, folderEnd);
+- ResourceFolderType folderType = ResourceFolderType.getFolderType(folderName);
+- if (folderType != null) {
+- int nameStart = folderEnd + 1;
+- int nameEnd = path.indexOf('.', nameStart);
+- if (nameEnd != -1) {
+- String name = path.substring(nameStart, nameEnd);
+- List<ResourceType> types =
+- FolderTypeRelationship.getRelatedResourceTypes(folderType);
+- for (ResourceType type : types) {
+- if (type != ResourceType.ID) {
+- Resource resource = getResource(type, name);
+- if (resource != null) {
+- return resource;
+- }
+- }
+- }
+- }
+- }
+- }
+- }
+-
+- return null;
+- }
+-
+- private static ResourceType getResourceType(Element element) {
+- String tagName = element.getTagName();
+- if (tagName.equals(TAG_ITEM)) {
+- String typeName = element.getAttribute(ATTR_TYPE);
+- if (!typeName.isEmpty()) {
+- return ResourceType.getEnum(typeName);
+- }
+- } else if ("string-array".equals(tagName) || "integer-array".equals(tagName)) {
+- return ResourceType.ARRAY;
+- } else {
+- return ResourceType.getEnum(tagName);
+- }
+- return null;
+- }
+-
+- private void findUnused() {
+- List<Resource> roots = Lists.newArrayList();
+-
+- for (Resource resource : mResources) {
+- if (resource.reachable && resource.type != ResourceType.ID
+- && resource.type != ResourceType.ATTR) {
+- roots.add(resource);
+- }
+- }
+-
+- if (mDebug) {
+- System.out.println("The root reachable resources are:\n" +
+- Joiner.on(",\n ").join(roots));
+- }
+-
+- Map<Resource,Boolean> seen = new IdentityHashMap<Resource,Boolean>(mResources.size());
+- for (Resource root : roots) {
+- visit(root, seen);
+- }
+-
+- List<Resource> unused = Lists.newArrayListWithExpectedSize(mResources.size());
+- for (Resource resource : mResources) {
+- if (!resource.reachable && resource.isRelevantType()) {
+- unused.add(resource);
+- }
+- }
+-
+- mUnused = unused;
+-
+- if (mDebug) {
+- System.out.println(dumpResourceModel());
+- }
+- }
+-
+- private static void visit(Resource root, Map<Resource, Boolean> seen) {
+- if (seen.containsKey(root)) {
+- return;
+- }
+- seen.put(root, Boolean.TRUE);
+- root.reachable = true;
+- if (root.references != null) {
+- for (Resource referenced : root.references) {
+- visit(referenced, seen);
+- }
+- }
+- }
+-
+- private void dumpReferences() {
+- if (mDebug) {
+- System.out.println("Resource Reference Graph:");
+- for (Resource resource : mResources) {
+- if (resource.references != null) {
+- System.out.println(resource + " => " + resource.references);
+- }
+- }
+- }
+- }
+-
+- private void keepPossiblyReferencedResources() {
+- if ((!mFoundGetIdentifier && !mFoundWebContent) || mStrings == null) {
+- // No calls to android.content.res.Resources#getIdentifier; no need
+- // to worry about string references to resources
+- return;
+- }
+-
+- if (!mGuessKeep) {
+- // User specifically asked for us not to guess resources to keep; they will
+- // explicitly mark them as kept if necessary instead
+- return;
+- }
+-
+- if (mDebug) {
+- List<String> strings = new ArrayList<String>(mStrings);
+- Collections.sort(strings);
+- System.out.println("android.content.res.Resources#getIdentifier present: "
+- + mFoundGetIdentifier);
+- System.out.println("Web content present: " + mFoundWebContent);
+- System.out.println("Referenced Strings:");
+- for (String s : strings) {
+- s = s.trim().replace("\n", "\\n");
+- if (s.length() > 40) {
+- s = s.substring(0, 37) + "...";
+- } else if (s.isEmpty()) {
+- continue;
+- }
+- System.out.println(" " + s);
+- }
+- }
+-
+- int shortest = Integer.MAX_VALUE;
+- Set<String> names = Sets.newHashSetWithExpectedSize(50);
+- for (Map<String, Resource> map : mTypeToName.values()) {
+- for (String name : map.keySet()) {
+- names.add(name);
+- int length = name.length();
+- if (length < shortest) {
+- shortest = length;
+- }
+- }
+- }
+-
+- for (String string : mStrings) {
+- if (string.length() < shortest) {
+- continue;
+- }
+-
+- // Check whether the string looks relevant
+- // We consider four types of strings:
+- // (1) simple resource names, e.g. "foo" from @layout/foo
+- // These might be the parameter to a getIdentifier() call, or could
+- // be composed into a fully qualified resource name for the getIdentifier()
+- // method. We match these for *all* resource types.
+- // (2) Relative source names, e.g. layout/foo, from @layout/foo
+- // These might be composed into a fully qualified resource name for
+- // getIdentifier().
+- // (3) Fully qualified resource names of the form package:type/name.
+- // (4) If mFoundWebContent is true, look for android_res/ URL strings as well
+-
+- if (mFoundWebContent) {
+- Resource resource = getResourceFromFilePath(string);
+- if (resource != null) {
+- markReachable(resource);
+- continue;
+- } else {
+- int start = 0;
+- int slash = string.lastIndexOf('/');
+- if (slash != -1) {
+- start = slash + 1;
+- }
+- int dot = string.indexOf('.', start);
+- String name = string.substring(start, dot != -1 ? dot : string.length());
+- if (names.contains(name)) {
+- for (Map<String, Resource> map : mTypeToName.values()) {
+- resource = map.get(name);
+- if (mDebug && resource != null) {
+- System.out.println("Marking " + resource + " used because it "
+- + "matches string pool constant " + string);
+- }
+- markReachable(resource);
+- }
+- }
+- }
+- }
+-
+- // Look for normal getIdentifier resource URLs
+- int n = string.length();
+- boolean justName = true;
+- boolean formatting = false;
+- boolean haveSlash = false;
+- for (int i = 0; i < n; i++) {
+- char c = string.charAt(i);
+- if (c == '/') {
+- haveSlash = true;
+- justName = false;
+- } else if (c == '.' || c == ':' || c == '%') {
+- justName = false;
+- if (c == '%') {
+- formatting = true;
+- }
+- } else if (!Character.isJavaIdentifierPart(c)) {
+- // This shouldn't happen; we've filtered out these strings in
+- // the {@link #referencedString} method
+- assert false : string;
+- break;
+- }
+- }
+-
+- String name;
+- if (justName) {
+- // Check name (below)
+- name = string;
+-
+- // Check for a simple prefix match, e.g. as in
+- // getResources().getIdentifier("ic_video_codec_" + codecName, "drawable", ...)
+- for (Map<String, Resource> map : mTypeToName.values()) {
+- for (Resource resource : map.values()) {
+- if (resource.name.startsWith(name)) {
+- if (mDebug) {
+- System.out.println("Marking " + resource + " used because its "
+- + "prefix matches string pool constant " + string);
+- }
+- markReachable(resource);
+- }
+- }
+- }
+- } else if (!haveSlash) {
+- if (formatting) {
+- // Possibly a formatting string, e.g.
+- // String name = String.format("my_prefix_%1d", index);
+- // int res = getContext().getResources().getIdentifier(name, "drawable", ...)
+-
+- try {
+- Pattern pattern = Pattern.compile(convertFormatStringToRegexp(string));
+- for (Map<String, Resource> map : mTypeToName.values()) {
+- for (Resource resource : map.values()) {
+- if (pattern.matcher(resource.name).matches()) {
+- if (mDebug) {
+- System.out.println("Marking " + resource + " used because "
+- + "it format-string matches string pool constant "
+- + string);
+- }
+- markReachable(resource);
+- }
+- }
+- }
+- } catch (PatternSyntaxException ignored) {
+- // Might not have been a formatting string after all!
+- }
+- }
+-
+- // If we have more than just a symbol name, we expect to also see a slash
+- //noinspection UnnecessaryContinue
+- continue;
+- } else {
+- // Try to pick out the resource name pieces; if we can find the
+- // resource type unambiguously; if not, just match on names
+- int slash = string.indexOf('/');
+- assert slash != -1; // checked with haveSlash above
+- name = string.substring(slash + 1);
+- if (name.isEmpty() || !names.contains(name)) {
+- continue;
+- }
+- // See if have a known specific resource type
+- if (slash > 0) {
+- int colon = string.indexOf(':');
+- String typeName = string.substring(colon != -1 ? colon + 1 : 0, slash);
+- ResourceType type = ResourceType.getEnum(typeName);
+- if (type == null) {
+- continue;
+- }
+- Resource resource = getResource(type, name);
+- if (mDebug && resource != null) {
+- System.out.println("Marking " + resource + " used because it "
+- + "matches string pool constant " + string);
+- }
+- markReachable(resource);
+- continue;
+- }
+-
+- // fall through and check the name
+- }
+-
+- if (names.contains(name)) {
+- for (Map<String, Resource> map : mTypeToName.values()) {
+- Resource resource = map.get(name);
+- if (mDebug && resource != null) {
+- System.out.println("Marking " + resource + " used because it "
+- + "matches string pool constant " + string);
+- }
+- markReachable(resource);
+- }
+- } else if (Character.isDigit(name.charAt(0))) {
+- // Just a number? There are cases where it calls getIdentifier by
+- // a String number; see for example SuggestionsAdapter in the support
+- // library which reports supporting a string like "2130837524" and
+- // "android.resource://com.android.alarmclock/2130837524".
+- try {
+- int id = Integer.parseInt(name);
+- if (id != 0) {
+- markReachable(mValueToResource.get(id));
+- }
+- } catch (NumberFormatException e) {
+- // pass
+- }
+- }
+- }
+- }
+-
+- @VisibleForTesting
+- static String convertFormatStringToRegexp(String formatString) {
+- StringBuilder regexp = new StringBuilder();
+- int from = 0;
+- boolean hasEscapedLetters = false;
+- Matcher matcher = StringFormatDetector.FORMAT.matcher(formatString);
+- int length = formatString.length();
+- while (matcher.find(from)) {
+- int start = matcher.start();
+- int end = matcher.end();
+- if (start == 0 && end == length) {
+- // Don't match if the entire string literal starts with % and ends with
+- // the a formatting character, such as just "%d": this just matches absolutely
+- // everything and is unlikely to be used in a resource lookup
+- return NO_MATCH;
+- }
+- if (start > from) {
+- hasEscapedLetters |= appendEscapedPattern(formatString, regexp, from, start);
+- }
+- // If the wildcard follows a previous wildcard, just skip it
+- // (e.g. don't convert %s%s into .*.*; .* is enough.
+- int regexLength = regexp.length();
+- if (regexLength < 2
+- || regexp.charAt(regexLength - 1) != '*'
+- || regexp.charAt(regexLength - 2) != '.') {
+- regexp.append(".*");
+- }
+- from = end;
+- }
+-
+- if (from < length) {
+- hasEscapedLetters |= appendEscapedPattern(formatString, regexp, from, length);
+- }
+-
+- if (!hasEscapedLetters) {
+- // If the regexp contains *only* formatting characters, e.g. "%.0f%d", or
+- // if it contains only formatting characters and punctuation, e.g. "%s_%d",
+- // don't treat this as a possible resource name pattern string: it is unlikely
+- // to be intended for actual resource names, and has the side effect of matching
+- // most names.
+- return NO_MATCH;
+- }
+-
+- return regexp.toString();
+- }
+-
+- /**
+- * Appends the characters in the range [from,to> from formatString as escaped
+- * regexp characters into the given string builder. Returns true if there were
+- * any letters in the appended text.
+- */
+- private static boolean appendEscapedPattern(@NonNull String formatString,
+- @NonNull StringBuilder regexp, int from, int to) {
+- regexp.append(Pattern.quote(formatString.substring(from, to)));
+-
+- for (int i = from; i < to; i++) {
+- if (Character.isLetter(formatString.charAt(i))) {
+- return true;
+- }
+- }
+-
+- return false;
+- }
+-
+- private void recordResources(File resDir)
+- throws IOException, SAXException, ParserConfigurationException {
+- File[] resourceFolders = resDir.listFiles();
+- if (resourceFolders != null) {
+- for (File folder : resourceFolders) {
+- ResourceFolderType folderType = ResourceFolderType.getFolderType(folder.getName());
+- if (folderType != null) {
+- recordResources(folderType, folder);
+- }
+- }
+- }
+- }
+-
+- private void recordResources(@NonNull ResourceFolderType folderType, File folder)
+- throws ParserConfigurationException, SAXException, IOException {
+- File[] files = folder.listFiles();
+- FolderConfiguration config = FolderConfiguration.getConfigForFolder(folder.getName());
+- boolean isDefaultFolder = false;
+- if (config != null) {
+- isDefaultFolder = true;
+- for (int i = 0, n = FolderConfiguration.getQualifierCount(); i < n; i++) {
+- ResourceQualifier qualifier = config.getQualifier(i);
+- // Densities are special: even if they're present in just (say) drawable-hdpi
+- // we'll match it on any other density
+- if (qualifier != null && !(qualifier instanceof DensityQualifier)) {
+- isDefaultFolder = false;
+- break;
+- }
+- }
+- }
+-
+- if (files != null) {
+- for (File file : files) {
+- String path = file.getPath();
+- boolean isXml = endsWithIgnoreCase(path, DOT_XML);
+-
+- Resource from = null;
+- // Record resource for the whole file
+- if (folderType != ResourceFolderType.VALUES
+- && (isXml
+- || endsWith(path, DOT_PNG) //also true for endsWith(name, DOT_9PNG)
+- || endsWith(path, DOT_JPG)
+- || endsWith(path, DOT_GIF)
+- || endsWith(path, DOT_JPEG))) {
+- List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(
+- folderType);
+- ResourceType type = types.get(0);
+- assert type != ResourceType.ID : folderType;
+- String name = file.getName();
+- name = name.substring(0, name.indexOf('.'));
+- Resource resource = getResource(type, name);
+- if (resource != null) {
+- resource.addLocation(file);
+- if (isDefaultFolder) {
+- resource.hasDefault = true;
+- }
+- from = resource;
+- }
+- }
+-
+- if (isXml) {
+- // For value files, and drawables and colors etc also pull in resource
+- // references inside the file
+- recordXmlResourcesUsages(file, isDefaultFolder, from);
+- if (folderType == ResourceFolderType.XML) {
+- tokenizeUnknownText(Files.toString(file, UTF_8));
+- }
+- } else if (folderType == ResourceFolderType.RAW) {
+- // Is this an HTML, CSS or JavaScript document bundled with the app?
+- // If so tokenize and look for resource references.
+- if (endsWithIgnoreCase(path, ".html") || endsWithIgnoreCase(path, ".htm")) {
+- tokenizeHtml(from, Files.toString(file, UTF_8));
+- } else if (endsWithIgnoreCase(path, ".css")) {
+- tokenizeCss(from, Files.toString(file, UTF_8));
+- } else if (endsWithIgnoreCase(path, ".js")) {
+- tokenizeJs(from, Files.toString(file, UTF_8));
+- } else if (file.isFile() && !LintUtils.isBitmapFile(file)) {
+- tokenizeUnknownBinary(file);
+- }
+- }
+- }
+- }
+- }
+-
+- private void recordMapping(@Nullable File mapping) throws IOException {
+- if (mapping == null || !mapping.exists()) {
+- return;
+- }
+- final String ARROW = " -> ";
+- final String RESOURCE = ".R$";
+- for (String line : Files.readLines(mapping, UTF_8)) {
+- if (line.startsWith(" ") || line.startsWith("\t")) {
+- continue;
+- }
+- int index = line.indexOf(RESOURCE);
+- if (index == -1) {
+- continue;
+- }
+- int arrow = line.indexOf(ARROW, index + 3);
+- if (arrow == -1) {
+- continue;
+- }
+- String typeName = line.substring(index + RESOURCE.length(), arrow);
+- ResourceType type = ResourceType.getEnum(typeName);
+- if (type == null) {
+- continue;
+- }
+- int end = line.indexOf(':', arrow + ARROW.length());
+- if (end == -1) {
+- end = line.length();
+- }
+- String target = line.substring(arrow + ARROW.length(), end).trim();
+- String ownerName = target.replace('.', '/');
+- mResourceClassOwners.put(ownerName, type);
+- }
+- }
+-
+- private void recordManifestUsages(File manifest)
+- throws IOException, ParserConfigurationException, SAXException {
+- String xml = Files.toString(manifest, UTF_8);
+- Document document = XmlUtils.parseDocument(xml, true);
+- recordManifestUsages(document.getDocumentElement());
+- }
+-
+- private void recordXmlResourcesUsages(@NonNull File file, boolean isDefaultFolder,
+- @Nullable Resource from)
+- throws IOException, ParserConfigurationException, SAXException {
+- String xml = Files.toString(file, UTF_8);
+- Document document = XmlUtils.parseDocument(xml, true);
+- recordResourceReferences(file, isDefaultFolder, document.getDocumentElement(), from);
+- }
+-
+- private void tokenizeHtml(@Nullable Resource from, @NonNull String html) {
+- // Look for
+- // (1) URLs of the form /android_res/drawable/foo.ext
+- // which we will use to keep R.drawable.foo
+- // and
+- // (2) Filenames. If the web content is loaded with something like
+- // WebView.loadDataWithBaseURL("file:///android_res/drawable/", ...)
+- // this is similar to Resources#getIdentifier handling where all
+- // *potentially* aliased filenames are kept to play it safe.
+-
+- // Simple HTML tokenizer
+- int length = html.length();
+- final int STATE_TEXT = 1;
+- final int STATE_SLASH = 2;
+- final int STATE_ATTRIBUTE_NAME = 3;
+- final int STATE_BEFORE_TAG = 4;
+- final int STATE_IN_TAG = 5;
+- final int STATE_BEFORE_ATTRIBUTE = 6;
+- final int STATE_ATTRIBUTE_BEFORE_EQUALS = 7;
+- final int STATE_ATTRIBUTE_AFTER_EQUALS = 8;
+- final int STATE_ATTRIBUTE_VALUE_NONE = 9;
+- final int STATE_ATTRIBUTE_VALUE_SINGLE = 10;
+- final int STATE_ATTRIBUTE_VALUE_DOUBLE = 11;
+- final int STATE_CLOSE_TAG = 12;
+-
+- int state = STATE_TEXT;
+- int offset = 0;
+- int valueStart = 0;
+- int tagStart = 0;
+- String tag = null;
+- String attribute = null;
+- int attributeStart = 0;
+- int prev = -1;
+- while (offset < length) {
+- if (offset == prev) {
+- // Purely here to prevent potential bugs in the state machine from looping
+- // infinitely
+- offset++;
+- }
+- prev = offset;
+-
+-
+- char c = html.charAt(offset);
+-
+- // MAke sure I handle doctypes properly.
+- // Make sure I handle cdata properly.
+- // Oh and what about <style> tags? tokenize everything inside as CSS!
+- // ANd <script> tag content as js!
+- switch (state) {
+- case STATE_TEXT: {
+- if (c == '<') {
+- state = STATE_SLASH;
+- offset++;
+- continue;
+- }
+-
+- // Other text is just ignored
+- offset++;
+- break;
+- }
+-
+- case STATE_SLASH: {
+- if (c == '!') {
+- if (html.startsWith("!--", offset)) {
+- // Comment
+- int end = html.indexOf("-->", offset + 3);
+- if (end == -1) {
+- offset = length;
+- break;
+- }
+- offset = end + 3;
+- continue;
+- } else if (html.startsWith("![CDATA[", offset)) {
+- // Skip CDATA text content; HTML text is irrelevant to this tokenizer
+- // anyway
+- int end = html.indexOf("]]>", offset + 8);
+- if (end == -1) {
+- offset = length;
+- break;
+- }
+- offset = end + 3;
+- continue;
+- }
+- } else if (c == '/') {
+- state = STATE_CLOSE_TAG;
+- offset++;
+- continue;
+- } else if (c == '?') {
+- // XML Prologue
+- int end = html.indexOf('>', offset + 2);
+- if (end == -1) {
+- offset = length;
+- break;
+- }
+- offset = end + 1;
+- continue;
+- }
+- state = STATE_IN_TAG;
+- tagStart = offset;
+- break;
+- }
+-
+- case STATE_CLOSE_TAG: {
+- if (c == '>') {
+- state = STATE_TEXT;
+- }
+- offset++;
+- break;
+- }
+-
+- case STATE_BEFORE_TAG: {
+- if (!Character.isWhitespace(c)) {
+- state = STATE_IN_TAG;
+- tagStart = offset;
+- }
+- // (For an end tag we'll include / in the tag name here)
+- offset++;
+- break;
+- }
+- case STATE_IN_TAG: {
+- if (Character.isWhitespace(c)) {
+- state = STATE_BEFORE_ATTRIBUTE;
+- tag = html.substring(tagStart, offset).trim();
+- } else if (c == '>') {
+- tag = html.substring(tagStart, offset).trim();
+- endHtmlTag(from, html, offset, tag);
+- state = STATE_TEXT;
+- }
+- offset++;
+- break;
+- }
+- case STATE_BEFORE_ATTRIBUTE: {
+- if (c == '>') {
+- endHtmlTag(from, html, offset, tag);
+- state = STATE_TEXT;
+- } else //noinspection StatementWithEmptyBody
+- if (c == '/') {
+- // we expect an '>' next to close the tag
+- } else if (!Character.isWhitespace(c)) {
+- state = STATE_ATTRIBUTE_NAME;
+- attributeStart = offset;
+- }
+- offset++;
+- break;
+- }
+- case STATE_ATTRIBUTE_NAME: {
+- if (c == '>') {
+- endHtmlTag(from, html, offset, tag);
+- state = STATE_TEXT;
+- } else if (c == '=') {
+- attribute = html.substring(attributeStart, offset);
+- state = STATE_ATTRIBUTE_AFTER_EQUALS;
+- } else if (Character.isWhitespace(c)) {
+- attribute = html.substring(attributeStart, offset);
+- state = STATE_ATTRIBUTE_BEFORE_EQUALS;
+- }
+- offset++;
+- break;
+- }
+- case STATE_ATTRIBUTE_BEFORE_EQUALS: {
+- if (c == '=') {
+- state = STATE_ATTRIBUTE_AFTER_EQUALS;
+- } else if (c == '>') {
+- endHtmlTag(from, html, offset, tag);
+- state = STATE_TEXT;
+- } else if (!Character.isWhitespace(c)) {
+- // Attribute value not specified (used for some boolean attributes)
+- state = STATE_ATTRIBUTE_NAME;
+- attributeStart = offset;
+- }
+- offset++;
+- break;
+- }
+-
+- case STATE_ATTRIBUTE_AFTER_EQUALS: {
+- if (c == '\'') {
+- // a='b'
+- state = STATE_ATTRIBUTE_VALUE_SINGLE;
+- valueStart = offset + 1;
+- } else if (c == '"') {
+- // a="b"
+- state = STATE_ATTRIBUTE_VALUE_DOUBLE;
+- valueStart = offset + 1;
+- } else if (!Character.isWhitespace(c)) {
+- // a=b
+- state = STATE_ATTRIBUTE_VALUE_NONE;
+- valueStart = offset + 1;
+- }
+- offset++;
+- break;
+- }
+-
+- case STATE_ATTRIBUTE_VALUE_SINGLE: {
+- if (c == '\'') {
+- state = STATE_BEFORE_ATTRIBUTE;
+- recordHtmlAttributeValue(from, tag, attribute,
+- html.substring(valueStart, offset));
+- }
+- offset++;
+- break;
+- }
+- case STATE_ATTRIBUTE_VALUE_DOUBLE: {
+- if (c == '"') {
+- state = STATE_BEFORE_ATTRIBUTE;
+- recordHtmlAttributeValue(from, tag, attribute,
+- html.substring(valueStart, offset));
+- }
+- offset++;
+- break;
+- }
+- case STATE_ATTRIBUTE_VALUE_NONE: {
+- if (c == '>') {
+- recordHtmlAttributeValue(from, tag, attribute,
+- html.substring(valueStart, offset));
+- endHtmlTag(from, html, offset, tag);
+- state = STATE_TEXT;
+- } else if (Character.isWhitespace(c)) {
+- state = STATE_BEFORE_ATTRIBUTE;
+- recordHtmlAttributeValue(from, tag, attribute,
+- html.substring(valueStart, offset));
+- }
+- offset++;
+- break;
+- }
+- default:
+- assert false : state;
+- }
+- }
+- }
+-
+- private void endHtmlTag(@Nullable Resource from, @NonNull String html, int offset,
+- @Nullable String tag) {
+- if ("script".equals(tag)) {
+- int end = html.indexOf("</script>", offset + 1);
+- if (end != -1) {
+- // Attempt to tokenize the text as JavaScript
+- String js = html.substring(offset + 1, end);
+- tokenizeJs(from, js);
+- }
+- } else if ("style".equals(tag)) {
+- int end = html.indexOf("</style>", offset + 1);
+- if (end != -1) {
+- // Attempt to tokenize the text as CSS
+- String css = html.substring(offset + 1, end);
+- tokenizeCss(from, css);
+- }
+- }
+- }
+-
+- private void tokenizeJs(@Nullable Resource from, @NonNull String js) {
+- // Simple JavaScript tokenizer: only looks for literal strings,
+- // and records those as string references
+- int length = js.length();
+- final int STATE_INIT = 1;
+- final int STATE_SLASH = 2;
+- final int STATE_STRING_DOUBLE = 3;
+- final int STATE_STRING_DOUBLE_QUOTED = 4;
+- final int STATE_STRING_SINGLE = 5;
+- final int STATE_STRING_SINGLE_QUOTED = 6;
+-
+- int state = STATE_INIT;
+- int offset = 0;
+- int stringStart = 0;
+- int prev = -1;
+- while (offset < length) {
+- if (offset == prev) {
+- // Purely here to prevent potential bugs in the state machine from looping
+- // infinitely
+- offset++;
+- }
+- prev = offset;
+-
+- char c = js.charAt(offset);
+- switch (state) {
+- case STATE_INIT: {
+- if (c == '/') {
+- state = STATE_SLASH;
+- } else if (c == '"') {
+- stringStart = offset + 1;
+- state = STATE_STRING_DOUBLE;
+- } else if (c == '\'') {
+- stringStart = offset + 1;
+- state = STATE_STRING_SINGLE;
+- }
+- offset++;
+- break;
+- }
+- case STATE_SLASH: {
+- if (c == '*') {
+- // Comment block
+- state = STATE_INIT;
+- int end = js.indexOf("*/", offset + 1);
+- if (end == -1) {
+- offset = length; // unterminated
+- break;
+- }
+- offset = end + 2;
+- continue;
+- } else if (c == '/') {
+- // Line comment
+- state = STATE_INIT;
+- int end = js.indexOf('\n', offset + 1);
+- if (end == -1) {
+- offset = length;
+- break;
+- }
+- offset = end + 1;
+- continue;
+- } else {
+- // division - just continue
+- state = STATE_INIT;
+- offset++;
+- break;
+- }
+- }
+- case STATE_STRING_DOUBLE: {
+- if (c == '"') {
+- recordJsString(js.substring(stringStart, offset));
+- state = STATE_INIT;
+- } else if (c == '\\') {
+- state = STATE_STRING_DOUBLE_QUOTED;
+- }
+- offset++;
+- break;
+- }
+- case STATE_STRING_DOUBLE_QUOTED: {
+- state = STATE_STRING_DOUBLE;
+- offset++;
+- break;
+- }
+- case STATE_STRING_SINGLE: {
+- if (c == '\'') {
+- recordJsString(js.substring(stringStart, offset));
+- state = STATE_INIT;
+- } else if (c == '\\') {
+- state = STATE_STRING_SINGLE_QUOTED;
+- }
+- offset++;
+- break;
+- }
+- case STATE_STRING_SINGLE_QUOTED: {
+- state = STATE_STRING_SINGLE;
+- offset++;
+- break;
+- }
+- default:
+- assert false : state;
+- }
+- }
+- }
+-
+- private void tokenizeCss(@Nullable Resource from, @NonNull String css) {
+- // Simple CSS tokenizer: Only looks for URL references, and records those
+- // filenames. Skips everything else (unrelated to images).
+- int length = css.length();
+- final int STATE_INIT = 1;
+- final int STATE_SLASH = 2;
+- int state = STATE_INIT;
+- int offset = 0;
+- int prev = -1;
+- while (offset < length) {
+- if (offset == prev) {
+- // Purely here to prevent potential bugs in the state machine from looping
+- // infinitely
+- offset++;
+- }
+- prev = offset;
+-
+- char c = css.charAt(offset);
+- switch (state) {
+- case STATE_INIT: {
+- if (c == '/') {
+- state = STATE_SLASH;
+- } else if (c == 'u' && css.startsWith("url(", offset) && offset > 0) {
+- char prevChar = css.charAt(offset-1);
+- if (Character.isWhitespace(prevChar) || prevChar == ':') {
+- int end = css.indexOf(')', offset);
+- offset += 4; // skip url(
+- while (offset < length && Character.isWhitespace(css.charAt(offset))) {
+- offset++;
+- }
+- if (end != -1 && end > offset + 1) {
+- while (end > offset
+- && Character.isWhitespace(css.charAt(end - 1))) {
+- end--;
+- }
+- if ((css.charAt(offset) == '"'
+- && css.charAt(end - 1) == '"')
+- || (css.charAt(offset) == '\''
+- && css.charAt(end - 1) == '\'')) {
+- // Strip " or '
+- offset++;
+- end--;
+- }
+- recordCssUrl(from, css.substring(offset, end).trim());
+- }
+- offset = end + 1;
+- continue;
+- }
+-
+- }
+- offset++;
+- break;
+- }
+- case STATE_SLASH: {
+- if (c == '*') {
+- // CSS comment? Skip the whole block rather than staying within the
+- // character tokenizer.
+- int end = css.indexOf("*/", offset + 1);
+- if (end == -1) {
+- offset = length;
+- break;
+- }
+- offset = end + 2;
+- continue;
+- }
+- state = STATE_INIT;
+- offset++;
+- break;
+- }
+- default:
+- assert false : state;
+- }
+- }
+- }
+-
+- private static byte[] sAndroidResBytes;
+-
+- /** Look through binary/unknown files looking for resource URLs */
+- private void tokenizeUnknownBinary(@NonNull File file) {
+- try {
+- if (sAndroidResBytes == null) {
+- sAndroidResBytes = ANDROID_RES.getBytes(SdkConstants.UTF_8);
+- }
+- byte[] bytes = Files.toByteArray(file);
+- int index = 0;
+- while (index != -1) {
+- index = indexOf(bytes, sAndroidResBytes, index);
+- if (index != -1) {
+- index += sAndroidResBytes.length;
+-
+- // Find the end of the URL
+- int begin = index;
+- int end = begin;
+- for (; end < bytes.length; end++) {
+- byte c = bytes[end];
+- if (c != '/' && !Character.isJavaIdentifierPart((char)c)) {
+- // android_res/raw/my_drawable.png => @raw/my_drawable
+- String url = "@" + new String(bytes, begin, end - begin, UTF_8);
+- markReachable(getResourceFromUrl(url));
+- break;
+- }
+- }
+- }
+- }
+- } catch (IOException e) {
+- // Ignore
+- }
+- }
+-
+- /**
+- * Returns the index of the given target array in the first array, looking from the given
+- * index
+- */
+- private static int indexOf(byte[] array, byte[] target, int fromIndex) {
+- outer:
+- for (int i = fromIndex; i < array.length - target.length + 1; i++) {
+- for (int j = 0; j < target.length; j++) {
+- if (array[i + j] != target[j]) {
+- continue outer;
+- }
+- }
+- return i;
+- }
+- return -1;
+- }
+-
+- /** Look through text files of unknown structure looking for resource URLs */
+- private void tokenizeUnknownText(@NonNull String text) {
+- int index = 0;
+- while (index != -1) {
+- index = text.indexOf(ANDROID_RES, index);
+- if (index != -1) {
+- index += ANDROID_RES.length();
+-
+- // Find the end of the URL
+- int begin = index;
+- int end = begin;
+- int length = text.length();
+- for (; end < length; end++) {
+- char c = text.charAt(end);
+- if (c != '/' && !Character.isJavaIdentifierPart(c)) {
+- // android_res/raw/my_drawable.png => @raw/my_drawable
+- markReachable(getResourceFromUrl("@" + text.substring(begin, end)));
+- break;
+- }
+- }
+- }
+- }
+- }
+-
+- private void recordCssUrl(@Nullable Resource from, @NonNull String value) {
+- if (!referencedUrl(from, value)) {
+- referencedString(value);
+- mFoundWebContent = true;
+- }
+- }
+-
+- /**
+- * See if the given URL is a URL that we can resolve to a specific resource; if so,
+- * record it and return true, otherwise returns false.
+- */
+- private boolean referencedUrl(@Nullable Resource from, @NonNull String url) {
+- Resource resource = getResourceFromFilePath(url);
+- if (resource != null) {
+- if (from != null) {
+- from.addReference(resource);
+- } else {
+- // We don't have an inclusion context, so just assume this resource is reachable
+- markReachable(resource);
+- }
+- return true;
+- }
+-
+- return false;
+- }
+-
+- private void recordHtmlAttributeValue(@Nullable Resource from, @Nullable String tagName,
+- @Nullable String attribute, @NonNull String value) {
+- if ("href".equals(attribute) || "src".equals(attribute)) {
+- // In general we'd need to unescape the HTML here (e.g. remove entities) but
+- // those wouldn't be valid characters in the resource name anyway
+- if (!referencedUrl(from, value)) {
+- referencedString(value);
+- mFoundWebContent = true;
+- }
+-
+- // If this document includes another, record the reachability of that script/resource
+- if (from != null) {
+- from.addReference(getResourceFromFilePath(attribute));
+- }
+- }
+- }
+-
+- private void recordJsString(@NonNull String string) {
+- referencedString(string);
+- }
+-
+- @Nullable
+- private Resource getResource(@NonNull ResourceType type, @NonNull String name) {
+- Map<String, Resource> nameMap = mTypeToName.get(type);
+- if (nameMap != null) {
+- return nameMap.get(getFieldName(name));
+- }
+- return null;
+- }
+-
+- @Nullable
+- private Resource getResourceFromUrl(@NonNull String possibleUrlReference) {
+- ResourceUrl url = ResourceUrl.parse(possibleUrlReference);
+- if (url != null && !url.framework) {
+- return getResource(url.type, url.name);
+- }
+-
+- return null;
+- }
+-
+- @Nullable
+- private Resource getResourceFromFilePath(@NonNull String url) {
+- int nameSlash = url.lastIndexOf('/');
+- if (nameSlash == -1) {
+- return null;
+- }
+-
+- // Look for
+- // (1) a full resource URL: /android_res/type/name.ext
+- // (2) a partial URL that uniquely identifies a given resource: drawable/name.ext
+- // e.g. file:///android_res/drawable/bar.png
+- int androidRes = url.indexOf(ANDROID_RES);
+- if (androidRes != -1) {
+- androidRes += ANDROID_RES.length();
+- int slash = url.indexOf('/', androidRes);
+- if (slash != -1) {
+- String folderName = url.substring(androidRes, slash);
+- ResourceFolderType folderType = ResourceFolderType.getFolderType(folderName);
+- if (folderType != null) {
+- List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(
+- folderType);
+- if (!types.isEmpty()) {
+- ResourceType type = types.get(0);
+- int nameBegin = slash + 1;
+- int dot = url.indexOf('.', nameBegin);
+- String name = url.substring(nameBegin, dot != -1 ? dot : url.length());
+- return getResource(type, name);
+- }
+- }
+- }
+- }
+-
+- // Some other relative path. Just look from the end:
+- int typeSlash = url.lastIndexOf('/', nameSlash - 1);
+- ResourceType type = ResourceType.getEnum(url.substring(typeSlash + 1, nameSlash));
+- if (type != null) {
+- int nameBegin = nameSlash + 1;
+- int dot = url.indexOf('.', nameBegin);
+- String name = url.substring(nameBegin, dot != -1 ? dot : url.length());
+- return getResource(type, name);
+- }
+-
+- return null;
+- }
+-
+- private void recordManifestUsages(Node node) {
+- short nodeType = node.getNodeType();
+- if (nodeType == Node.ELEMENT_NODE) {
+- Element element = (Element) node;
+- NamedNodeMap attributes = element.getAttributes();
+- for (int i = 0, n = attributes.getLength(); i < n; i++) {
+- Attr attr = (Attr) attributes.item(i);
+- markReachable(getResourceFromUrl(attr.getValue()));
+- }
+- } else if (nodeType == Node.TEXT_NODE) {
+- // Does this apply to any manifests??
+- String text = node.getNodeValue().trim();
+- markReachable(getResourceFromUrl(text));
+- }
+-
+- NodeList children = node.getChildNodes();
+- for (int i = 0, n = children.getLength(); i < n; i++) {
+- Node child = children.item(i);
+- recordManifestUsages(child);
+- }
+- }
+-
+-
+- private void recordResourceReferences(@NonNull File file, boolean isDefaultFolder,
+- @NonNull Node node, @Nullable Resource from) {
+- short nodeType = node.getNodeType();
+- if (nodeType == Node.ELEMENT_NODE) {
+- Element element = (Element) node;
+- if (from != null) {
+- NamedNodeMap attributes = element.getAttributes();
+- for (int i = 0, n = attributes.getLength(); i < n; i++) {
+- Attr attr = (Attr) attributes.item(i);
+-
+- // Ignore tools: namespace attributes, unless it's
+- // a keep attribute
+- if (TOOLS_URI.equals(attr.getNamespaceURI())) {
+- handleToolsAttribute(attr);
+- // Skip all other tools: attributes
+- continue;
+- }
+-
+- Resource resource = getResourceFromUrl(attr.getValue());
+- if (resource != null) {
+- from.addReference(resource);
+- }
+- }
+-
+- // Android Wear. We *could* limit ourselves to only doing this in files
+- // referenced from a manifest meta-data element, e.g.
+- // <meta-data android:name="com.google.android.wearable.beta.app"
+- // android:resource="@xml/wearable_app_desc"/>
+- // but given that that property has "beta" in the name, it seems likely
+- // to change and therefore hardcoding it for that key risks breakage
+- // in the future.
+- if ("rawPathResId".equals(element.getTagName())) {
+- StringBuilder sb = new StringBuilder();
+- NodeList children = node.getChildNodes();
+- for (int i = 0, n = children.getLength(); i < n; i++) {
+- Node child = children.item(i);
+- if (child.getNodeType() == Element.TEXT_NODE
+- || child.getNodeType() == Element.CDATA_SECTION_NODE) {
+- sb.append(child.getNodeValue());
+- }
+- }
+- if (sb.length() > 0) {
+- Resource resource = getResource(ResourceType.RAW, sb.toString().trim());
+- from.addReference(resource);
+- }
+- }
+- } else {
+- // Look for keep attributes everywhere else since they don't require a source
+- handleToolsAttribute(element.getAttributeNodeNS(TOOLS_URI, ATTR_KEEP));
+- handleToolsAttribute(element.getAttributeNodeNS(TOOLS_URI, ATTR_DISCARD));
+- handleToolsAttribute(element.getAttributeNodeNS(TOOLS_URI, ATTR_SHRINK_MODE));
+- }
+-
+- Resource definition = getResource(element);
+- if (definition != null) {
+- from = definition;
+- definition.addLocation(file);
+- if (isDefaultFolder) {
+- definition.hasDefault = true;
+- }
+- }
+-
+- String tagName = element.getTagName();
+- if (TAG_STYLE.equals(tagName)) {
+- if (element.hasAttribute(ATTR_PARENT)) {
+- String parent = element.getAttribute(ATTR_PARENT);
+- if (!parent.isEmpty() && !parent.startsWith(ANDROID_STYLE_RESOURCE_PREFIX) &&
+- !parent.startsWith(PREFIX_ANDROID)) {
+- String parentStyle = parent;
+- if (!parentStyle.startsWith(STYLE_RESOURCE_PREFIX)) {
+- parentStyle = STYLE_RESOURCE_PREFIX + parentStyle;
+- }
+- Resource ps = getResourceFromUrl(getFieldName(parentStyle));
+- if (ps != null && definition != null) {
+- definition.addReference(ps);
+- }
+- }
+- } else {
+- // Implicit parent styles by name
+- String name = getFieldName(element);
+- while (true) {
+- int index = name.lastIndexOf('_');
+- if (index != -1) {
+- name = name.substring(0, index);
+- Resource ps = getResourceFromUrl(
+- STYLE_RESOURCE_PREFIX + getFieldName(name));
+- if (ps != null && definition != null) {
+- definition.addReference(ps);
+- }
+- } else {
+- break;
+- }
+- }
+- }
+- }
+-
+- if (TAG_ITEM.equals(tagName)) {
+- // In style? If so the name: attribute can be a reference
+- if (element.getParentNode() != null
+- && element.getParentNode().getNodeName().equals(TAG_STYLE)) {
+- String name = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
+- if (!name.isEmpty() && !name.startsWith("android:")) {
+- Resource resource = getResource(ResourceType.ATTR, name);
+- if (definition == null) {
+- Element style = (Element) element.getParentNode();
+- definition = getResource(style);
+- if (definition != null) {
+- from = definition;
+- definition.addReference(resource);
+- }
+- }
+- }
+- }
+- }
+- } else if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) {
+- String text = node.getNodeValue().trim();
+- Resource textResource = getResourceFromUrl(getFieldName(text));
+- if (textResource != null && from != null) {
+- from.addReference(textResource);
+- }
+- }
+-
+- NodeList children = node.getChildNodes();
+- for (int i = 0, n = children.getLength(); i < n; i++) {
+- Node child = children.item(i);
+- recordResourceReferences(file, isDefaultFolder, child, from);
+- }
+- }
+-
+- private void handleToolsAttribute(@Nullable Attr attr) {
+- if (attr == null) {
+- return;
+- }
+- String localName = attr.getLocalName();
+- String value = attr.getValue();
+- if (ATTR_KEEP.equals(localName)) {
+- handleKeepAttribute(value);
+- } else if (ATTR_DISCARD.equals(localName)) {
+- handleRemoveAttribute(value);
+- } else if (ATTR_SHRINK_MODE.equals(localName)) {
+- if (VALUE_STRICT.equals(value)) {
+- mGuessKeep = false;
+- } else if (VALUE_SAFE.equals(value)) {
+- mGuessKeep = true;
+- } else if (mDebug) {
+- System.out.println("Ignoring unknown " + ATTR_SHRINK_MODE + " " + value);
+- }
+- if (mDebug) {
+- System.out.println("Setting shrink mode to " + value);
+- }
+- }
+- }
+-
+- public static String getFieldName(@NonNull String styleName) {
+- return styleName.replace('.', '_').replace('-', '_').replace(':', '_');
+- }
+-
+- /**
+- * Marks the given resource (if non-null) as reachable, and returns true if
+- * this is the first time the resource is marked reachable
+- */
+- private static boolean markReachable(@Nullable Resource resource) {
+- if (resource != null) {
+- boolean wasReachable = resource.reachable;
+- resource.reachable = true;
+- return !wasReachable;
+- }
+-
+- return false;
+- }
+-
+- private static void markUnreachable(@Nullable Resource resource) {
+- if (resource != null) {
+- resource.reachable = false;
+- }
+- }
+-
+- /**
+- * Called for a tools:keep attribute containing a resource URL where that resource name
+- * is not referencing a known resource
+- *
+- * @param value The keep value
+- */
+- private void handleKeepAttribute(@NonNull String value) {
+- // Handle comma separated lists of URLs and globs
+- if (value.indexOf(',') != -1) {
+- for (String portion : Splitter.on(',').omitEmptyStrings().trimResults().split(value)) {
+- handleKeepAttribute(portion);
+- }
+- return;
+- }
+-
+- ResourceUrl url = ResourceUrl.parse(value);
+- if (url == null || url.framework) {
+- return;
+- }
+-
+- Resource resource = getResource(url.type, url.name);
+- if (resource != null) {
+- if (mDebug) {
+- System.out.println("Marking " + resource + " used because it "
+- + "matches keep attribute " + value);
+- }
+- markReachable(resource);
+- } else if (url.name.contains("*") || url.name.contains("?")) {
+- // Look for globbing patterns
+- String regexp = DefaultConfiguration.globToRegexp(getFieldName(url.name));
+- try {
+- Pattern pattern = Pattern.compile(regexp);
+- Map<String, Resource> nameMap = mTypeToName.get(url.type);
+- if (nameMap != null) {
+- for (Resource r : nameMap.values()) {
+- if (pattern.matcher(r.name).matches()) {
+- if (mDebug) {
+- System.out.println("Marking " + r + " used because it "
+- + "matches keep globbing pattern " + url.name);
+- }
+-
+- markReachable(r);
+- }
+- }
+- }
+- } catch (PatternSyntaxException ignored) {
+- if (mDebug) {
+- System.out.println("Could not compute keep globbing pattern for " +
+- url.name + ": tried regexp " + regexp + "(" + ignored + ")");
+- }
+- }
+- }
+- }
+-
+- private void handleRemoveAttribute(@NonNull String value) {
+- // Handle comma separated lists of URLs and globs
+- if (value.indexOf(',') != -1) {
+- for (String portion : Splitter.on(',').omitEmptyStrings().trimResults().split(value)) {
+- handleRemoveAttribute(portion);
+- }
+- return;
+- }
+-
+- ResourceUrl url = ResourceUrl.parse(value);
+- if (url == null || url.framework) {
+- return;
+- }
+-
+- Resource resource = getResource(url.type, url.name);
+- if (resource != null) {
+- if (mDebug) {
+- System.out.println("Marking " + resource + " used because it "
+- + "matches remove attribute " + value);
+- }
+- markUnreachable(resource);
+- } else if (url.name.contains("*") || url.name.contains("?")) {
+- // Look for globbing patterns
+- String regexp = DefaultConfiguration.globToRegexp(getFieldName(url.name));
+- try {
+- Pattern pattern = Pattern.compile(regexp);
+- Map<String, Resource> nameMap = mTypeToName.get(url.type);
+- if (nameMap != null) {
+- for (Resource r : nameMap.values()) {
+- if (pattern.matcher(r.name).matches()) {
+- if (mDebug) {
+- System.out.println("Marking " + r + " used because it "
+- + "matches remove globbing pattern " + url.name);
+- }
+-
+- markUnreachable(r);
+- }
+- }
+- }
+- } catch (PatternSyntaxException ignored) {
+- if (mDebug) {
+- System.out.println("Could not compute remove globbing pattern for " +
+- url.name + ": tried regexp " + regexp + "(" + ignored + ")");
+- }
+- }
+- }
+- }
+-
+- private Set<String> mStrings;
+- private boolean mFoundGetIdentifier;
+- private boolean mFoundWebContent;
+-
+- private void referencedString(@NonNull String string) {
+- // See if the string is at all eligible; ignore strings that aren't
+- // identifiers (has java identifier chars and nothing but .:/), or are empty or too long
+- // We also allow "%", used for formatting strings.
+- if (string.isEmpty() || string.length() > 80) {
+- return;
+- }
+- boolean haveIdentifierChar = false;
+- for (int i = 0, n = string.length(); i < n; i++) {
+- char c = string.charAt(i);
+- boolean identifierChar = Character.isJavaIdentifierPart(c);
+- if (!identifierChar && c != '.' && c != ':' && c != '/' && c != '%') {
+- // .:/ are for the fully qualified resource names, or for resource URLs or
+- // relative file names
+- return;
+- } else if (identifierChar) {
+- haveIdentifierChar = true;
+- }
+- }
+- if (!haveIdentifierChar) {
+- return;
+- }
+-
+- if (mStrings == null) {
+- mStrings = Sets.newHashSetWithExpectedSize(300);
+- }
+- mStrings.add(string);
+-
+- if (!mFoundWebContent && string.contains(ANDROID_RES)) {
+- mFoundWebContent = true;
+- }
+- }
+-
+- private void recordUsages(File jarFile) throws IOException {
+- if (!jarFile.exists()) {
+- return;
+- }
+- ZipInputStream zis = null;
+- try {
+- FileInputStream fis = new FileInputStream(jarFile);
+- try {
+- zis = new ZipInputStream(fis);
+- ZipEntry entry = zis.getNextEntry();
+- while (entry != null) {
+- String name = entry.getName();
+- if (name.endsWith(DOT_CLASS) &&
+- // Skip resource type classes like R$drawable; they will
+- // reference the integer id's we're looking for, but these aren't
+- // actual usages we need to track; if somebody references the
+- // field elsewhere, we'll catch that
+- !isResourceClass(name)) {
+- byte[] bytes = ByteStreams.toByteArray(zis);
+- if (bytes != null) {
+- ClassReader classReader = new ClassReader(bytes);
+- classReader.accept(new UsageVisitor(jarFile, name),
+- SKIP_DEBUG | SKIP_FRAMES);
+- }
+- }
+-
+- entry = zis.getNextEntry();
+- }
+- } finally {
+- Closeables.close(fis, true);
+- }
+- } finally {
+- Closeables.close(zis, true);
+- }
+- }
+-
+- /** Returns whether the given class path points to an aapt-generated compiled R class */
+- @VisibleForTesting
+- static boolean isResourceClass(@NonNull String name) {
+- assert name.endsWith(DOT_CLASS) : name;
+- int index = name.lastIndexOf('/');
+- if (index != -1 && name.startsWith("R$", index + 1)) {
+- String typeName = name.substring(index + 3, name.length() - DOT_CLASS.length());
+- return ResourceType.getEnum(typeName) != null;
+- }
+-
+- return false;
+- }
+-
+- private void gatherResourceValues(File file) throws IOException {
+- if (file.isDirectory()) {
+- File[] children = file.listFiles();
+- if (children != null) {
+- for (File child : children) {
+- gatherResourceValues(child);
+- }
+- }
+- } else if (file.isFile() && file.getName().equals(SdkConstants.FN_RESOURCE_CLASS)) {
+- parseResourceClass(file);
+- }
+- }
+-
+- // TODO: Use Lombok/ECJ here
+- private void parseResourceClass(File file) throws IOException {
+- String s = Files.toString(file, UTF_8);
+- // Simple parser which handles only aapt's special R output
+- String pkg = null;
+- int index = s.indexOf("package ");
+- if (index != -1) {
+- int end = s.indexOf(';', index);
+- pkg = s.substring(index + "package ".length(), end).trim().replace('.', '/');
+- }
+- index = 0;
+- int length = s.length();
+- String classDeclaration = "public static final class ";
+- while (true) {
+- index = s.indexOf(classDeclaration, index);
+- if (index == -1) {
+- break;
+- }
+- int start = index + classDeclaration.length();
+- int end = s.indexOf(' ', start);
+- if (end == -1) {
+- break;
+- }
+- String typeName = s.substring(start, end);
+- ResourceType type = ResourceType.getEnum(typeName);
+- if (type == null) {
+- break;
+- }
+-
+- if (pkg != null) {
+- mResourceClassOwners.put(pkg + "/R$" + type.getName(), type);
+- }
+-
+- index = end;
+-
+- // Find next declaration
+- for (; index < length - 1; index++) {
+- char c = s.charAt(index);
+- if (Character.isWhitespace(c)) {
+- //noinspection UnnecessaryContinue
+- continue;
+- } else if (c == '/') {
+- char next = s.charAt(index + 1);
+- if (next == '*') {
+- // Scan forward to comment end
+- end = index + 2;
+- while (end < length -2) {
+- c = s.charAt(end);
+- if (c == '*' && s.charAt(end + 1) == '/') {
+- end++;
+- break;
+- } else {
+- end++;
+- }
+- }
+- index = end;
+- } else if (next == '/') {
+- // Scan forward to next newline
+- assert false : s.substring(index - 1, index + 50); // we don't put line comments in R files
+- } else {
+- assert false : s.substring(index - 1, index + 50); // unexpected division
+- }
+- } else if (c == 'p' && s.startsWith("public ", index)) {
+- if (type == ResourceType.STYLEABLE) {
+- start = s.indexOf(" int", index);
+- if (s.startsWith(" int[] ", start)) {
+- end = s.indexOf('=', start);
+- assert end != -1;
+- String styleable = s.substring(start, end).trim();
+- addResource(ResourceType.DECLARE_STYLEABLE, styleable, null);
+-
+- // TODO: Read in all the action bar ints!
+- // For now, we're simply treating all R.attr fields as used
+- } else if (s.startsWith(" int ")) {
+- // Read these fields in and correlate with the attr R's. Actually
+- // we don't need this for anything; the local attributes are
+- // found by the R attr thing. I just need to record the class
+- // (style).
+- // public static final int ActionBar_background = 10;
+- // ignore - jump to end
+- index = s.indexOf(';', index);
+- if (index == -1) {
+- break;
+- }
+- // For now, we're simply treating all R.attr fields as used
+- }
+- } else {
+- start = s.indexOf(" int ", index);
+- if (start != -1) {
+- start += " int ".length();
+- // e.g. abc_fade_in=0x7f040000;
+- end = s.indexOf('=', start);
+- assert end != -1;
+- String name = s.substring(start, end).trim();
+- start = end + 1;
+- end = s.indexOf(';', start);
+- assert end != -1;
+- String value = s.substring(start, end).trim();
+- addResource(type, name, value);
+- }
+- }
+- } else if (c == '}') {
+- // Done with resource class
+- break;
+- }
+- }
+- }
+- }
+-
+- private void addResource(@NonNull ResourceType type, @NonNull String name,
+- @Nullable String value) {
+- int realValue = value != null ? Integer.decode(value) : -1;
+- Resource resource = getResource(type, name);
+- if (resource != null) {
+- //noinspection VariableNotUsedInsideIf
+- if (value != null) {
+- if (resource.value == -1) {
+- resource.value = realValue;
+- } else {
+- assert realValue == resource.value;
+- }
+- }
+- return;
+- }
+-
+- resource = new Resource(type, name, realValue);
+- mResources.add(resource);
+- if (realValue != -1) {
+- mValueToResource.put(realValue, resource);
+- }
+- Map<String, Resource> nameMap = mTypeToName.get(type);
+- if (nameMap == null) {
+- nameMap = Maps.newHashMapWithExpectedSize(30);
+- mTypeToName.put(type, nameMap);
+- }
+- nameMap.put(name, resource);
+-
+- // TODO: Assert that we don't set the same resource multiple times to different values.
+- // Could happen if you pass in stale data!
+- }
+-
+- public int getUnusedResourceCount() {
+- return mUnused.size();
+- }
+-
+- @VisibleForTesting
+- List<Resource> getAllResources() {
+- return mResources;
+- }
+-
+- public static class Resource {
+- /** Type of resource */
+- public ResourceType type;
+- /** Name of resource */
+- public String name;
+- /** Integer id location */
+- public int value;
+- /** Whether this resource can be reached from one of the roots (manifest, code) */
+- public boolean reachable;
+- /** Whether this resource has a default definition (e.g. present in a resource folder
+- * with no qualifiers). For id references, an inline definition (@+id) does not count as
+- * a default definition.*/
+- public boolean hasDefault;
+- /** Resources this resource references. For example, a layout can reference another via
+- * an include; a style reference in a layout references that layout style, and so on. */
+- public List<Resource> references;
+- public final List<File> declarations = Lists.newArrayList();
+-
+- private Resource(ResourceType type, String name, int value) {
+- this.type = type;
+- this.name = name;
+- this.value = value;
+- }
+-
+- @Override
+- public String toString() {
+- return type + ":" + name + ":" + value;
+- }
+-
+- @SuppressWarnings("RedundantIfStatement") // Generated by IDE
+- @Override
+- public boolean equals(Object o) {
+- if (this == o) {
+- return true;
+- }
+- if (o == null || getClass() != o.getClass()) {
+- return false;
+- }
+-
+- Resource resource = (Resource) o;
+-
+- if (name != null ? !name.equals(resource.name) : resource.name != null) {
+- return false;
+- }
+- if (type != resource.type) {
+- return false;
+- }
+-
+- return true;
+- }
+-
+- @Override
+- public int hashCode() {
+- int result = type != null ? type.hashCode() : 0;
+- result = 31 * result + (name != null ? name.hashCode() : 0);
+- return result;
+- }
+-
+- public void addLocation(@NonNull File file) {
+- declarations.add(file);
+- }
+-
+- public void addReference(@Nullable Resource resource) {
+- if (resource != null) {
+- if (references == null) {
+- references = Lists.newArrayList();
+- } else if (references.contains(resource)) {
+- return;
+- }
+- references.add(resource);
+- }
+- }
+-
+- public String getUrl() {
+- return '@' + type.getName() + '/' + name;
+- }
+-
+- public boolean isRelevantType() {
+- return type != ResourceType.ID; // && getFolderType() != ResourceFolderType.VALUES;
+- }
+- }
+-
+- /**
+- * Class visitor responsible for looking for resource references in code.
+- * It looks for R.type.name references (as well as inlined constants for these,
+- * in the case of non-library code), as well as looking both for Resources#getIdentifier
+- * calls and recording string literals, used to handle dynamic lookup of resources.
+- */
+- private class UsageVisitor extends ClassVisitor {
+- private final File mJarFile;
+- private final String mCurrentClass;
+-
+- public UsageVisitor(File jarFile, String name) {
+- super(Opcodes.ASM5);
+- mJarFile = jarFile;
+- mCurrentClass = name;
+- }
+-
+- @Override
+- public MethodVisitor visitMethod(int access, final String name,
+- String desc, String signature, String[] exceptions) {
+- return new MethodVisitor(Opcodes.ASM5) {
+- @Override
+- public void visitLdcInsn(Object cst) {
+- handleCodeConstant(cst, "ldc");
+- }
+-
+- @Override
+- public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+- if (opcode == Opcodes.GETSTATIC) {
+- ResourceType type = mResourceClassOwners.get(owner);
+- if (type != null) {
+- Resource resource = getResource(type, name);
+- if (resource != null) {
+- markReachable(resource);
+- }
+- }
+- }
+- }
+-
+- @Override
+- public void visitMethodInsn(int opcode, String owner, String name,
+- String desc, boolean itf) {
+- super.visitMethodInsn(opcode, owner, name, desc, itf);
+- if (owner.equals("android/content/res/Resources")
+- && name.equals("getIdentifier")
+- && desc.equals(
+- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I")) {
+- mFoundGetIdentifier = true;
+- // TODO: Check previous instruction and see if we can find a literal
+- // String; if so, we can more accurately dispatch the resource here
+- // rather than having to check the whole string pool!
+- }
+- if (owner.equals("android/webkit/WebView") && name.startsWith("load")) {
+- mFoundWebContent = true;
+- }
+- }
+-
+- @Override
+- public AnnotationVisitor visitAnnotationDefault() {
+- return new AnnotationUsageVisitor();
+- }
+-
+- @Override
+- public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+- return new AnnotationUsageVisitor();
+- }
+-
+- @Override
+- public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+- boolean visible) {
+- return new AnnotationUsageVisitor();
+- }
+- };
+- }
+-
+- @Override
+- public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+- return new AnnotationUsageVisitor();
+- }
+-
+- @Override
+- public FieldVisitor visitField(int access, String name, String desc, String signature,
+- Object value) {
+- handleCodeConstant(value, "field");
+- return new FieldVisitor(Opcodes.ASM5) {
+- @Override
+- public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+- return new AnnotationUsageVisitor();
+- }
+- };
+- }
+-
+- private class AnnotationUsageVisitor extends AnnotationVisitor {
+- public AnnotationUsageVisitor() {
+- super(Opcodes.ASM5);
+- }
+-
+- @Override
+- public AnnotationVisitor visitAnnotation(String name, String desc) {
+- return new AnnotationUsageVisitor();
+- }
+-
+- @Override
+- public AnnotationVisitor visitArray(String name) {
+- return new AnnotationUsageVisitor();
+- }
+-
+- @Override
+- public void visit(String name, Object value) {
+- handleCodeConstant(value, "annotation");
+- super.visit(name, value);
+- }
+- }
+-
+- /** Invoked when an ASM visitor encounters a constant: record corresponding reference */
+- private void handleCodeConstant(@Nullable Object cst, @NonNull String context) {
+- if (cst instanceof Integer) {
+- Integer value = (Integer) cst;
+- Resource resource = mValueToResource.get(value);
+- if (markReachable(resource) && mDebug) {
+- System.out.println("Marking " + resource + " reachable: referenced from " +
+- context + " in " + mJarFile + ":" + mCurrentClass);
+- }
+- } else if (cst instanceof int[]) {
+- int[] values = (int[]) cst;
+- for (int value : values) {
+- Resource resource = mValueToResource.get(value);
+- if (markReachable(resource) && mDebug) {
+- System.out.println("Marking " + resource + " reachable: referenced from " +
+- context + " in " + mJarFile + ":" + mCurrentClass);
+- }
+- }
+- } else if (cst instanceof String) {
+- String string = (String) cst;
+- referencedString(string);
+- }
+- }
+- }
+-
+- @VisibleForTesting
+- String dumpResourceModel() {
+- StringBuilder sb = new StringBuilder(1000);
+- Collections.sort(mResources, new Comparator<Resource>() {
+- @Override
+- public int compare(Resource resource1,
+- Resource resource2) {
+- int delta = resource1.type.compareTo(resource2.type);
+- if (delta != 0) {
+- return delta;
+- }
+- return resource1.name.compareTo(resource2.name);
+- }
+- });
+-
+- for (Resource resource : mResources) {
+- sb.append(resource.getUrl()).append(" : reachable=").append(resource.reachable);
+- sb.append("\n");
+- if (resource.references != null) {
+- for (Resource referenced : resource.references) {
+- sb.append(" ");
+- sb.append(referenced.getUrl());
+- sb.append("\n");
+- }
+- }
+- }
+-
+- return sb.toString();
+- }
+-}
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
+deleted file mode 100644
+index 4031e6c..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
++++ /dev/null
+@@ -1,220 +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.
+- */
+-
+-package com.android.build.gradle.tasks
+-
+-import com.android.build.gradle.internal.scope.ConventionMappingHelper
+-import com.android.build.gradle.internal.scope.TaskConfigAction
+-import com.android.build.gradle.internal.scope.VariantOutputScope
+-import com.android.build.gradle.internal.tasks.BaseTask
+-import com.android.build.gradle.internal.variant.BaseVariantData
+-import com.android.build.gradle.internal.variant.BaseVariantOutputData
+-import com.android.builder.core.AaptPackageProcessBuilder
+-import com.android.ide.common.process.LoggedProcessOutputHandler
+-import org.gradle.api.logging.LogLevel
+-import org.gradle.api.tasks.InputFile
+-import org.gradle.api.tasks.OutputFile
+-import org.gradle.api.tasks.ParallelizableTask
+-import org.gradle.api.tasks.TaskAction
+-
+-import java.util.concurrent.Callable
+-
+-/**
+- * Task which strips out unused resources
+- * <p>
+- * The process works as follows:
+- * <ul>
+- * <li> Collect R id <b>values</b> from the final merged R class, which incorporates
+- * the final id's of all the libraries (if ProGuard hasn't inlined these,
+- * we don't need to do this; we can look for actual R.id's instead!)
+- * <li> Collect <b>used<b> R values from all the .class files, and R.x.y references too!
+- * <li> Compute the set of remaining/used id’s
+- * <li> Add in any found in the manifest
+- * <li> Look through all resources and produce a graph of reachable resources
+- * <li> Compute unused resources by visiting all resources and ignoring those that
+- * were reachable
+- * <li> In addition, if we find a call to Resources#getIdentifier(), we collect all
+- * strings in the class files, and also mark as used any resources that match
+- * potential string lookups
+- * </ul>
+- */
+- at ParallelizableTask
+-public class ShrinkResources extends BaseTask {
+- /**
+- * Associated variant data that the strip task will be run against. Used to locate
+- * not only locations the task needs (e.g. for resources and generated R classes)
+- * but also to obtain the resource merging task, since we will run it a second time
+- * here to generate a new .ap_ file with fewer resources
+- */
+- public BaseVariantOutputData variantOutputData
+-
+- @InputFile
+- File uncompressedResources
+-
+- @OutputFile
+- File compressedResources
+-
+- /** Whether we've already warned about how to turn off shrinking. Used to avoid
+- * repeating the same multi-line message for every repeated abi split. */
+- private static ourWarned;
+-
+- @SuppressWarnings("GroovyUnusedDeclaration")
+- @TaskAction
+- void shrink() {
+- def variantData = variantOutputData.variantData
+- try {
+- def processResourcesTask = variantData.generateRClassTask
+- File sourceDir = processResourcesTask.sourceOutputDir
+- File resourceDir = variantData.getScope().getFinalResourcesDir()
+- File mergedManifest = variantOutputData.manifestProcessorTask.manifestOutputFile
+-
+- // Analyze resources and usages and strip out unused
+- def analyzer = new ResourceUsageAnalyzer(
+- sourceDir,
+- variantData.getScope().getProguardOutputFile(),
+- mergedManifest,
+- variantData.getMappingFile(),
+- resourceDir)
+- analyzer.verbose = project.logger.isEnabled(LogLevel.INFO)
+- analyzer.debug = project.logger.isEnabled(LogLevel.DEBUG)
+- analyzer.analyze();
+-
+- //noinspection GroovyConstantIfStatement
+- if (ResourceUsageAnalyzer.TWO_PASS_AAPT) {
+- // This is currently not working; we need support from aapt to be able
+- // to assign a stable set of resources that it should use.
+- def destination = new File(resourceDir.parentFile, resourceDir.name + "-stripped")
+- analyzer.removeUnused(destination)
+-
+- File sourceOutputs = processResourcesTask.getSourceOutputDir();
+- sourceOutputs = new File(sourceOutputs.getParentFile(),
+- sourceOutputs.getName() + "-stripped")
+- sourceOutputs.mkdirs()
+-
+- // We don't need to emit R files again, but we can do this here such that
+- // we can *verify* that the R classes generated in the second aapt pass
+- // matches those we saw the first time around.
+- //String sourceOutputPath = sourceOutputs?.getAbsolutePath();
+- String sourceOutputPath = null
+-
+- // Repackage the resources:
+- AaptPackageProcessBuilder aaptPackageCommandBuilder =
+- new AaptPackageProcessBuilder(processResourcesTask.getManifestFile(),
+- processResourcesTask.getAaptOptions())
+- .setAssetsFolder(processResourcesTask.getAssetsDir())
+- .setResFolder(destination)
+- .setLibraries(processResourcesTask.getLibraries())
+- .setPackageForR(processResourcesTask.getPackageForR())
+- .setSourceOutputDir(sourceOutputPath)
+- .setResPackageOutput(getCompressedResources().absolutePath)
+- .setType(processResourcesTask.getType())
+- .setDebuggable(processResourcesTask.getDebuggable())
+- .setResourceConfigs(processResourcesTask.getResourceConfigs())
+- .setSplits(processResourcesTask.getSplits())
+-
+- getBuilder().processResources(
+- aaptPackageCommandBuilder,
+- processResourcesTask.getEnforceUniquePackageName(),
+- new LoggedProcessOutputHandler(getBuilder().getLogger())
+- )
+- } else {
+- // Just rewrite the .ap_ file to strip out the res/ files for unused resources
+- analyzer.rewriteResourceZip(getUncompressedResources(), getCompressedResources())
+- }
+-
+- // Dump some stats
+- int unused = analyzer.getUnusedResourceCount()
+- if (unused > 0) {
+- StringBuilder sb = new StringBuilder(200);
+- sb.append("Removed unused resources")
+-
+- // This is a bit misleading until we can strip out all resource types:
+- //int total = analyzer.getTotalResourceCount()
+- //sb.append("(" + unused + "/" + total + ")")
+-
+- int before = getUncompressedResources().length()
+- int after = getCompressedResources().length()
+- int percent = (before - after) * 100 / before
+- sb.append(": Binary resource data reduced from ").
+- append(toKbString(before)).
+- append("KB to ").
+- append(toKbString(after)).
+- append("KB: Removed " + percent + "%");
+- if (!ourWarned) {
+- ourWarned = true;
+- sb.append(
+- "\nNote: If necessary, you can disable resource shrinking by adding\n" +
+- "android {\n" +
+- " buildTypes {\n" +
+- " " + variantData.variantConfiguration.buildType.name + " {\n" +
+- " shrinkResources false\n" +
+- " }\n" +
+- " }\n" +
+- "}")
+- }
+-
+- println sb.toString();
+- }
+-
+- } catch (Exception e) {
+- println 'Failed to shrink resources: ' + e.toString() + '; ignoring'
+- logger.quiet("Failed to shrink resources: ignoring", e)
+- }
+- }
+-
+- private static String toKbString(long size) {
+- return Integer.toString((int)size/1024);
+- }
+-
+- public static class ConfigAction implements TaskConfigAction<ShrinkResources> {
+-
+- private VariantOutputScope scope;
+-
+- public ConfigAction(VariantOutputScope scope) {
+- this.scope = scope;
+- }
+-
+- @Override
+- String getName() {
+- return scope.getTaskName("shrink", "Resources");
+- }
+-
+- @Override
+- Class<ShrinkResources> getType() {
+- return ShrinkResources.class
+- }
+-
+- @Override
+- void execute(ShrinkResources task) {
+- BaseVariantData<? extends BaseVariantOutputData> variantData =
+- scope.variantScope.variantData
+- task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+- task.setVariantName(scope.getVariantScope().getVariantConfiguration().getFullName());
+- task.variantOutputData = scope.variantOutputData;
+-
+- final String outputBaseName = scope.variantOutputData.getBaseName();
+- task.setCompressedResources(scope.getCompressedResourceFile());
+-
+- ConventionMappingHelper.map(task, "uncompressedResources", new Callable<File>() {
+- @Override
+- public File call() {
+- return scope.variantOutputData.processResourcesTask.getPackageOutputFile();
+- }
+- });
+-
+- }
+- }
+-}
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ApiDatabase.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ApiDatabase.java
+deleted file mode 100644
+index 67d47e1..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ApiDatabase.java
++++ /dev/null
+@@ -1,356 +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.
+- */
+-
+-package com.android.build.gradle.tasks.annotations;
+-
+-import com.android.annotations.NonNull;
+-import com.android.annotations.VisibleForTesting;
+-import com.google.common.base.Charsets;
+-import com.google.common.base.Splitter;
+-import com.google.common.collect.Lists;
+-import com.google.common.collect.Maps;
+-import com.google.common.collect.Sets;
+-import com.google.common.io.Files;
+-
+-import java.io.File;
+-import java.io.IOException;
+-import java.util.List;
+-import java.util.Map;
+-import java.util.Set;
+-import java.util.regex.Matcher;
+-import java.util.regex.Pattern;
+-
+-/** Reads a signature file in the format of the new API files in frameworks/base/api */
+-public class ApiDatabase {
+- @NonNull
+- private final List<String> lines;
+- /** Map from class name to set of field names */
+- @NonNull private final Map<String,Set<String>> fieldMap =
+- Maps.newHashMapWithExpectedSize(1000);
+- /** Map from class name to map of method names whose values are overloaded signatures */
+- @NonNull private final Map<String,Map<String,List<String>>> methodMap =
+- Maps.newHashMapWithExpectedSize(1000);
+- @NonNull private final Map<String, List<String>> inheritsFrom =
+- Maps.newHashMapWithExpectedSize(1000);
+- @NonNull private final Map<String,Set<String>> intFieldMap =
+- Maps.newHashMapWithExpectedSize(1000);
+- @NonNull private final Set<String> classSet =
+- Sets.newHashSetWithExpectedSize(1000);
+-
+- public ApiDatabase(@NonNull List<String> lines) {
+- this.lines = lines;
+- readApi();
+- }
+-
+- public ApiDatabase(@NonNull File api) throws IOException {
+- this(Files.readLines(api, Charsets.UTF_8));
+- }
+-
+- public boolean hasMethod(String className, String methodName, String arguments) {
+- // Perform raw lookup
+- className = getRawClass(className);
+- methodName = getRawMethod(methodName);
+- arguments = getRawParameterList(arguments);
+-
+- Map<String, List<String>> methods = methodMap.get(className);
+- if (methods != null) {
+- List<String> strings = methods.get(methodName);
+- if (strings != null && strings.contains(arguments)) {
+- return true;
+- }
+- }
+-
+- List<String> inheritsFrom = this.inheritsFrom.get(className);
+- if (inheritsFrom != null) {
+- for (String clz : inheritsFrom) {
+- if (hasMethod(clz, methodName, arguments)) {
+- return true;
+- }
+- }
+- }
+-
+- return false;
+- }
+-
+- public boolean hasField(String className, String fieldName) {
+- Set<String> fields = fieldMap.get(className);
+- if (fields != null && fields.contains(fieldName)) {
+- return true;
+- }
+-
+- List<String> inheritsFrom = this.inheritsFrom.get(className);
+- if (inheritsFrom != null) {
+- for (String clz : inheritsFrom) {
+- if (hasField(clz, fieldName)) {
+- return true;
+- }
+- }
+- }
+-
+- return false;
+- }
+-
+- public boolean hasClass(String className) {
+- return classSet.contains(className);
+- }
+-
+- public Set<String> getDeclaredIntFields(String className) {
+- return intFieldMap.get(className);
+- }
+-
+- private void readApi() {
+- String MODIFIERS =
+- "((deprecated|public|static|private|protected|final|abstract|\\s*)\\s+)*";
+- Pattern PACKAGE = Pattern.compile("package (\\S+) \\{");
+- Pattern CLASS =
+- Pattern.compile(MODIFIERS
+- + "(class|interface|enum)\\s+(\\S+)\\s+(extends (.+))?(implements (.+))?(.*)\\{");
+- Pattern METHOD = Pattern.compile("(method|ctor)\\s+" +
+- MODIFIERS + "(.+)??\\s+(\\S+)\\s*\\((.*)\\)(.*);");
+- Pattern CTOR = Pattern.compile("(method|ctor)\\s+.*\\((.*)\\)(.*);");
+- Pattern FIELD = Pattern.compile("(enum_constant|field)\\s+" +
+- MODIFIERS + "(.+)\\s+(\\S+)\\s*;");
+-
+- String currentPackage = null;
+- String currentClass = null;
+-
+- for (String line : lines) {
+- line = line.trim();
+- if (line.isEmpty() || line.equals("}")) {
+- continue;
+- }
+- if (line.startsWith("method ")) {
+- Matcher matcher = METHOD.matcher(line);
+- if (!matcher.matches()) {
+- Extractor.warning("Warning: Did not match as a member: " + line);
+- } else {
+- assert currentClass != null;
+- Map<String,List<String>> memberMap = methodMap.get(currentClass);
+- if (memberMap == null) {
+- memberMap = Maps.newHashMap();
+- methodMap.put(currentClass, memberMap);
+- methodMap.put(getRawClass(currentClass), memberMap);
+- }
+- String methodName = matcher.group(5);
+- List<String> signatures = memberMap.get(methodName);
+- if (signatures == null) {
+- signatures = Lists.newArrayList();
+- memberMap.put(methodName, signatures);
+- memberMap.put(getRawMethod(methodName), signatures);
+- }
+- String signature = matcher.group(6);
+- signature = signature.trim().replace(" ", "").replace(" ", "");
+- // normalize varargs: allow lookup with both formats
+- signatures.add(signature);
+- if (signature.endsWith("...")) {
+- signatures.add(signature.substring(0, signature.length() - 3) + "[]");
+- } else if (signature.endsWith("[]") && !signature.endsWith("[][]")) {
+- signatures.add(signature.substring(0, signature.length() - 2) + "...");
+- }
+- String raw = getRawParameterList(signature);
+- if (!signatures.contains(raw)) {
+- signatures.add(raw);
+- }
+- }
+- } else if (line.startsWith("ctor ")) {
+- Matcher matcher = CTOR.matcher(line);
+- if (!matcher.matches()) {
+- Extractor.warning("Warning: Did not match as a member: " + line);
+- } else {
+- assert currentClass != null;
+- Map<String,List<String>> memberMap = methodMap.get(currentClass);
+- if (memberMap == null) {
+- memberMap = Maps.newHashMap();
+- methodMap.put(currentClass, memberMap);
+- methodMap.put(getRawClass(currentClass), memberMap);
+- }
+- @SuppressWarnings("UnnecessaryLocalVariable")
+- String methodName = currentClass;
+- List<String> signatures = memberMap.get(methodName);
+- if (signatures == null) {
+- signatures = Lists.newArrayList();
+- memberMap.put(methodName, signatures);
+- String constructor = methodName.substring(methodName.lastIndexOf('.') + 1);
+- memberMap.put(constructor, signatures);
+- memberMap.put(getRawMethod(methodName), signatures);
+- memberMap.put(getRawMethod(constructor), signatures);
+- }
+- String signature = matcher.group(2);
+- signature = signature.trim().replace(" ", "").replace(" ", "");
+- if (signature.endsWith("...")) {
+- signatures.add(signature.substring(0, signature.length() - 3) + "[]");
+- } else if (signature.endsWith("[]") && !signature.endsWith("[][]")) {
+- signatures.add(signature.substring(0, signature.length() - 2) + "...");
+- }
+- signatures.add(signature);
+- String raw = getRawMethod(signature);
+- if (!signatures.contains(raw)) {
+- signatures.add(raw);
+- }
+- }
+- } else if (line.startsWith("enum_constant ") || line.startsWith("field ")) {
+- int equals = line.indexOf('=');
+- if (equals != -1) {
+- line = line.substring(0, equals).trim();
+- int semi = line.indexOf(';');
+- if (semi == -1) {
+- line = line + ';';
+- }
+- } else if (!line.endsWith(";")) {
+- int semi = line.indexOf(';');
+- if (semi != -1) {
+- line = line.substring(0, semi + 1);
+- }
+- }
+- Matcher matcher = FIELD.matcher(line);
+- if (!matcher.matches()) {
+- Extractor.warning("Warning: Did not match as a member: " + line);
+- } else {
+- assert currentClass != null;
+- String fieldName = matcher.group(5);
+- Set<String> fieldSet = fieldMap.get(currentClass);
+- if (fieldSet == null) {
+- fieldSet = Sets.newHashSet();
+- fieldMap.put(currentClass, fieldSet);
+- }
+- fieldSet.add(fieldName);
+- String type = matcher.group(4);
+- if (type.equals("int")) {
+- fieldSet = intFieldMap.get(currentClass);
+- if (fieldSet == null) {
+- fieldSet = Sets.newHashSet();
+- intFieldMap.put(currentClass, fieldSet);
+- }
+- fieldSet.add(fieldName);
+- }
+- }
+- } else if (line.startsWith("package ")) {
+- Matcher matcher = PACKAGE.matcher(line);
+- if (!matcher.matches()) {
+- Extractor.warning("Warning: Did not match as a package: " + line);
+- } else {
+- currentPackage = matcher.group(1);
+- }
+- } else {
+- Matcher matcher = CLASS.matcher(line);
+- if (!matcher.matches()) {
+- Extractor.warning("Warning: Did not match as a class/interface: " + line);
+- } else {
+- currentClass = currentPackage + '.' + matcher.group(4);
+- classSet.add(currentClass);
+-
+- String superClass = matcher.group(6);
+- if (superClass != null) {
+- Splitter splitter = Splitter.on(' ').trimResults().omitEmptyStrings();
+- for (String from : splitter.split(superClass)) {
+- if (from.equals("implements")) { // workaround for broken regexp
+- continue;
+- }
+- addInheritsFrom(currentClass, from);
+- }
+- addInheritsFrom(currentClass, superClass.trim());
+- }
+- String implementsList = matcher.group(8);
+- if (implementsList != null) {
+- Splitter splitter = Splitter.on(' ').trimResults().omitEmptyStrings();
+- for (String from : splitter.split(implementsList)) {
+- addInheritsFrom(currentClass, from);
+- }
+- }
+- }
+- }
+- }
+- }
+-
+- private void addInheritsFrom(String cls, String inheritsFrom) {
+- List<String> list = this.inheritsFrom.get(cls);
+- if (list == null) {
+- list = Lists.newArrayList();
+- this.inheritsFrom.put(cls, list);
+- }
+- list.add(inheritsFrom);
+- }
+-
+- /** Drop generic type variables from a class name */
+- @VisibleForTesting
+- static String getRawClass(@NonNull String name) {
+- int index = name.indexOf('<');
+- if (index != -1) {
+- int end = name.indexOf('>', index + 1);
+- if (end == -1 || end == name.length() - 1) {
+- return name.substring(0, index);
+- } else {
+- // e.g. test.pkg.ArrayAdapter<T>.Inner
+- return name.substring(0, index) + name.substring(end + 1);
+- }
+- }
+- return name;
+- }
+-
+- /** Drop generic type variables from a method or constructor name */
+- @VisibleForTesting
+- static String getRawMethod(@NonNull String name) {
+- int index = name.indexOf('<');
+- if (index != -1) {
+- return name.substring(0, index);
+- }
+- return name;
+- }
+-
+- /** Drop generic type variables and varargs to produce a raw signature */
+- @VisibleForTesting
+- static String getRawParameterList(String signature) {
+- if (signature.indexOf('<') == -1 && !signature.endsWith("...")) {
+- return signature;
+- }
+-
+- int n = signature.length();
+- StringBuilder sb = new StringBuilder(n);
+- int start = 0;
+- while (true) {
+- int index = signature.indexOf('<', start);
+- if (index == -1) {
+- sb.append(signature.substring(start));
+- break;
+- }
+- sb.append(signature.substring(start, index));
+- int balance = 1;
+- for (int i = index + 1; i < n; i++) {
+- char c = signature.charAt(i);
+- if (c == '<') {
+- balance++;
+- } else if (c == '>') {
+- balance--;
+- if (balance == 0) {
+- start = i + 1;
+- break;
+- }
+- }
+- }
+- }
+-
+- // Normalize varargs... to []
+- if (sb.length() > 3
+- && sb.charAt(sb.length() - 1) == '.'
+- && sb.charAt(sb.length() - 2) == '.'
+- && sb.charAt(sb.length() - 3) == '.') {
+- sb.setLength(sb.length() - 3);
+- sb.append('[').append(']');
+- }
+-
+- return sb.toString();
+- }
+-}
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java
+deleted file mode 100644
+index 78ec03d..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java
++++ /dev/null
+@@ -1,400 +0,0 @@
+-/*
+- * Copyright (C) 2015 The Android Open Source Project
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package com.android.build.gradle.tasks.annotations;
+-
+-import static com.android.SdkConstants.DOT_JAVA;
+-import static java.io.File.pathSeparator;
+-import static java.io.File.pathSeparatorChar;
+-
+-import com.android.annotations.NonNull;
+-import com.android.tools.lint.EcjParser;
+-import com.android.utils.Pair;
+-import com.google.common.base.Charsets;
+-import com.google.common.base.Splitter;
+-import com.google.common.collect.Lists;
+-import com.google.common.collect.Maps;
+-import com.google.common.io.Files;
+-
+-import org.eclipse.jdt.core.compiler.IProblem;
+-import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+-import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
+-import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+-import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+-import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+-import org.eclipse.jdt.internal.compiler.util.Util;
+-
+-import java.io.File;
+-import java.io.IOException;
+-import java.io.PrintStream;
+-import java.util.Collection;
+-import java.util.List;
+-import java.util.Map;
+-
+-/**
+- * The extract annotations driver is a command line interface to extracting annotations
+- * from a source tree. It's similar to the gradle
+- * {@link com.android.build.gradle.tasks.ExtractAnnotations} task,
+- * but usable from the command line and outside Gradle, for example
+- * to extract annotations from the Android framework itself (which is not built with
+- * Gradle). It also allows other options only interesting for extracting
+- * platform annotations, such as filtering all APIs and constants through an
+- * API white-list (such that we for example can pull annotations from the master
+- * branch which has the latest metadata, but only expose APIs that are actually in
+- * a released platform), as well as translating android.annotation annotations into
+- * android.support.annotations.
+- */
+-public class ExtractAnnotationsDriver {
+- public static void main(String[] args) {
+- new ExtractAnnotationsDriver().run(args);
+- }
+-
+- private static void usage(PrintStream output) {
+- output.println("Usage: " + ExtractAnnotationsDriver.class.getSimpleName() + " <flags>");
+- output.println(" --sources <paths> : Source directories to extract annotations from. ");
+- output.println(" Separate paths with " + pathSeparator + ", and you can use @ ");
+- output.println(" as a filename prefix to have the filenames fed from a file");
+- output.println("--classpath <paths> : Directories and .jar files to resolve symbols from");
+- output.println("--output <zip path> : The .zip file to write the extracted annotations to, if any");
+- output.println("--proguard <path> : The proguard.cfg file to write the keep rules to, if any");
+- output.println();
+- output.println("Optional flags:");
+- output.println("--merge-zips <paths> : Existing external annotation files to merge in");
+- output.println("--quiet : Don't print summary information");
+- output.println("--rmtypedefs <folder> : Remove typedef classes found in the given folder");
+- output.println("--allow-missing-types : Don't fail even if some types can't be resolved");
+- output.println("--allow-errors : Don't fail even if there are some compiler errors");
+- output.println("--encoding <encoding> : Encoding (defaults to utf-8)");
+- output.println("--language-level <level> : Java source language level, typically 1.6 (default) or 1.7");
+- output.println("--api-filter <api.txt> : A framework API definition to restrict included APIs to");
+- output.println("--hide-filtered : If filtering out non-APIs, supply this flag to hide listing matches");
+- output.println("--skip-class-retention : Don't extract annotations that have class retention");
+- System.exit(-1);
+- }
+-
+- @SuppressWarnings("MethodMayBeStatic")
+- public void run(@NonNull String[] args) {
+- List<String> classpath = Lists.newArrayList();
+- List<File> sources = Lists.newArrayList();
+- List<File> mergePaths = Lists.newArrayList();
+- List<File> apiFilters = null;
+- File rmTypeDefs = null;
+- boolean verbose = true;
+- boolean allowMissingTypes = false;
+- boolean allowErrors = false;
+- boolean listFiltered = true;
+- boolean skipClassRetention = false;
+-
+- String encoding = Charsets.UTF_8.name();
+- File output = null;
+- File proguard = null;
+- long languageLevel = EcjParser.getLanguageLevel(1, 7);
+- if (args.length == 1 && "--help".equals(args[0])) {
+- usage(System.out);
+- }
+- if (args.length < 2) {
+- usage(System.err);
+- }
+- for (int i = 0, n = args.length; i < n; i++) {
+- String flag = args[i];
+-
+- if (flag.equals("--quiet")) {
+- verbose = false;
+- continue;
+- } else if (flag.equals("--allow-missing-types")) {
+- allowMissingTypes = true;
+- continue;
+- } else if (flag.equals("--allow-errors")) {
+- allowErrors = true;
+- continue;
+- } else if (flag.equals("--hide-filtered")) {
+- listFiltered = false;
+- continue;
+- } else if (flag.equals("--skip-class-retention")) {
+- skipClassRetention = true;
+- continue;
+- }
+- if (i == n - 1) {
+- usage(System.err);
+- }
+- String value = args[i + 1];
+- i++;
+-
+- if (flag.equals("--sources")) {
+- sources = getFiles(value);
+- } else if (flag.equals("--classpath")) {
+- classpath = getPaths(value);
+- } else if (flag.equals("--merge-zips")) {
+- mergePaths = getFiles(value);
+- } else if (flag.equals("--output")) {
+- output = new File(value);
+- if (output.exists()) {
+- if (output.isDirectory()) {
+- abort(output + " is a directory");
+- }
+- boolean deleted = output.delete();
+- if (!deleted) {
+- abort("Could not delete previous version of " + output);
+- }
+- } else if (output.getParentFile() != null && !output.getParentFile().exists()) {
+- abort(output.getParentFile() + " does not exist");
+- }
+- } else if (flag.equals("--proguard")) {
+- proguard = new File(value);
+- if (proguard.exists()) {
+- if (proguard.isDirectory()) {
+- abort(proguard + " is a directory");
+- }
+- boolean deleted = proguard.delete();
+- if (!deleted) {
+- abort("Could not delete previous version of " + proguard);
+- }
+- } else if (proguard.getParentFile() != null && !proguard.getParentFile().exists()) {
+- abort(proguard.getParentFile() + " does not exist");
+- }
+- } else if (flag.equals("--encoding")) {
+- encoding = value;
+- } else if (flag.equals("--api-filter")) {
+- if (apiFilters == null) {
+- apiFilters = Lists.newArrayList();
+- }
+- for (String path : Splitter.on(",").omitEmptyStrings().split(value)) {
+- File apiFilter = new File(path);
+- if (!apiFilter.isFile()) {
+- String message = apiFilter + " does not exist or is not a file";
+- abort(message);
+- }
+- apiFilters.add(apiFilter);
+- }
+- } else if (flag.equals("--language-level")) {
+- if ("1.6".equals(value)) {
+- languageLevel = EcjParser.getLanguageLevel(1, 6);
+- } else if ("1.7".equals(value)) {
+- languageLevel = EcjParser.getLanguageLevel(1, 7);
+- } else {
+- abort("Unsupported language level " + value);
+- }
+- } else if (flag.equals("--rmtypedefs")) {
+- rmTypeDefs = new File(value);
+- if (!rmTypeDefs.isDirectory()) {
+- abort(rmTypeDefs + " is not a directory");
+- }
+- } else {
+- System.err.println("Unknown flag " + flag + ": Use --help for usage information");
+- }
+- }
+-
+- if (sources.isEmpty()) {
+- abort("Must specify at least one source path");
+- }
+- if (classpath.isEmpty()) {
+- abort("Must specify classpath pointing to at least android.jar or the framework");
+- }
+- if (output == null && proguard == null) {
+- abort("Must specify output path with --output or a proguard path with --proguard");
+- }
+-
+- // API definition files
+- ApiDatabase database = null;
+- if (apiFilters != null && !apiFilters.isEmpty()) {
+- try {
+- List<String> lines = Lists.newArrayList();
+- for (File file : apiFilters) {
+- lines.addAll(Files.readLines(file, Charsets.UTF_8));
+- }
+- database = new ApiDatabase(lines);
+- } catch (IOException e) {
+- abort("Could not open API database " + apiFilters + ": " + e.getLocalizedMessage());
+- }
+- }
+-
+- Extractor extractor = new Extractor(database, rmTypeDefs, verbose, !skipClassRetention,
+- true);
+- extractor.setListIgnored(listFiltered);
+-
+- try {
+- Pair<Collection<CompilationUnitDeclaration>, INameEnvironment>
+- pair = parseSources(sources, classpath, encoding, languageLevel);
+- Collection<CompilationUnitDeclaration> units = pair.getFirst();
+-
+- boolean abort = false;
+- int errorCount = 0;
+- for (CompilationUnitDeclaration unit : units) {
+- // so maybe I don't need my map!!
+- IProblem[] problems = unit.compilationResult().getAllProblems();
+- if (problems != null) {
+- for (IProblem problem : problems) {
+- if (problem.isError()) {
+- errorCount++;
+- String message = problem.getMessage();
+- if (allowMissingTypes) {
+- if (message.contains("cannot be resolved")) {
+- continue;
+- }
+- }
+-
+- System.out.println("Error: " +
+- new String(problem.getOriginatingFileName()) + ":" +
+- problem.getSourceLineNumber() + ": " + message);
+- abort = !allowErrors;
+- }
+- }
+- }
+- }
+- if (errorCount > 0) {
+- System.err.println("Found " + errorCount + " errors");
+- }
+- if (abort) {
+- abort("Not extracting annotations (compilation problems encountered)");
+- }
+-
+- INameEnvironment environment = pair.getSecond();
+- extractor.extractFromProjectSource(units);
+-
+- if (mergePaths != null) {
+- for (File jar : mergePaths) {
+- extractor.mergeExisting(jar);
+- }
+- }
+-
+- extractor.export(output, proguard);
+-
+- // Remove typedefs?
+- //noinspection VariableNotUsedInsideIf
+- if (rmTypeDefs != null) {
+- extractor.removeTypedefClasses();
+- }
+-
+- environment.cleanup();
+- } catch (IOException e) {
+- e.printStackTrace();
+- }
+- }
+-
+- private static void abort(@NonNull String message) {
+- System.err.println(message);
+- System.exit(-1);
+- }
+-
+- private static List<File> getFiles(String value) {
+- List<File> files = Lists.newArrayList();
+- Splitter splitter = Splitter.on(pathSeparatorChar).omitEmptyStrings().trimResults();
+- for (String path : splitter.split(value)) {
+- if (path.startsWith("@")) {
+- // Special syntax for providing files in a list
+- File sourcePath = new File(path.substring(1));
+- if (!sourcePath.exists()) {
+- abort(sourcePath + " does not exist");
+- }
+- try {
+- for (String line : Files.readLines(sourcePath, Charsets.UTF_8)) {
+- line = line.trim();
+- if (!line.isEmpty()) {
+- File file = new File(line);
+- if (!file.exists()) {
+- System.err.println("Warning: Could not find file " + line +
+- " listed in " + sourcePath);
+- }
+- files.add(file);
+- }
+- }
+- continue;
+- } catch (IOException e) {
+- e.printStackTrace();
+- System.exit(-1);
+- }
+- }
+- File file = new File(path);
+- if (!file.exists()) {
+- abort(file + " does not exist");
+- }
+- files.add(file);
+- }
+-
+- return files;
+- }
+-
+- private static List<String> getPaths(String value) {
+- List<File> files = getFiles(value);
+- List<String> paths = Lists.newArrayListWithExpectedSize(files.size());
+- for (File file : files) {
+- paths.add(file.getPath());
+- }
+- return paths;
+- }
+-
+- private static void addJavaSources(List<File> list, File file) {
+- if (file.isDirectory()) {
+- File[] files = file.listFiles();
+- if (files != null) {
+- for (File child : files) {
+- addJavaSources(list, child);
+- }
+- }
+- } else {
+- if (file.isFile() && file.getName().endsWith(DOT_JAVA)) {
+- list.add(file);
+- }
+- }
+- }
+-
+- private static List<File> gatherJavaSources(List<File> sourcePath) {
+- List<File> sources = Lists.newArrayList();
+- for (File file : sourcePath) {
+- addJavaSources(sources, file);
+- }
+- return sources;
+- }
+-
+- @NonNull
+- private static Pair<Collection<CompilationUnitDeclaration>,INameEnvironment> parseSources(
+- @NonNull List<File> sourcePaths,
+- @NonNull List<String> classpath,
+- @NonNull String encoding,
+- long languageLevel)
+- throws IOException {
+- List<ICompilationUnit> sourceUnits = Lists.newArrayListWithExpectedSize(100);
+-
+- for (File source : gatherJavaSources(sourcePaths)) {
+- char[] contents = Util.getFileCharContent(source, encoding);
+- ICompilationUnit unit = new CompilationUnit(contents, source.getPath(), encoding);
+- sourceUnits.add(unit);
+- }
+-
+- Map<ICompilationUnit, CompilationUnitDeclaration> outputMap = Maps.newHashMapWithExpectedSize(
+- sourceUnits.size());
+-
+- CompilerOptions options = EcjParser.createCompilerOptions();
+- options.docCommentSupport = true; // So I can find @hide
+-
+- // Note: We can *not* set options.ignoreMethodBodies=true because it disables
+- // type attribution!
+-
+- options.sourceLevel = languageLevel;
+- options.complianceLevel = options.sourceLevel;
+- // We don't generate code, but just in case the parser consults this flag
+- // and makes sure that it's not greater than the source level:
+- options.targetJDK = options.sourceLevel;
+- options.originalComplianceLevel = options.sourceLevel;
+- options.originalSourceLevel = options.sourceLevel;
+- options.inlineJsrBytecode = true; // >= 1.5
+-
+- INameEnvironment environment = EcjParser.parse(options, sourceUnits, classpath,
+- outputMap, null);
+- Collection<CompilationUnitDeclaration> parsedUnits = outputMap.values();
+- return Pair.of(parsedUnits, environment);
+- }
+-}
+\ No newline at end of file
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
+deleted file mode 100644
+index 8cc6042..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
++++ /dev/null
+@@ -1,2484 +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.
+- */
+-
+-package com.android.build.gradle.tasks.annotations;
+-
+-import static com.android.SdkConstants.AMP_ENTITY;
+-import static com.android.SdkConstants.APOS_ENTITY;
+-import static com.android.SdkConstants.ATTR_NAME;
+-import static com.android.SdkConstants.ATTR_VALUE;
+-import static com.android.SdkConstants.DOT_CLASS;
+-import static com.android.SdkConstants.DOT_JAR;
+-import static com.android.SdkConstants.DOT_XML;
+-import static com.android.SdkConstants.GT_ENTITY;
+-import static com.android.SdkConstants.INT_DEF_ANNOTATION;
+-import static com.android.SdkConstants.LT_ENTITY;
+-import static com.android.SdkConstants.QUOT_ENTITY;
+-import static com.android.SdkConstants.STRING_DEF_ANNOTATION;
+-import static com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX;
+-import static com.android.SdkConstants.TYPE_DEF_FLAG_ATTRIBUTE;
+-import static com.android.SdkConstants.TYPE_DEF_VALUE_ATTRIBUTE;
+-import static com.android.SdkConstants.VALUE_TRUE;
+-import static com.android.tools.lint.checks.SupportAnnotationDetector.INT_RANGE_ANNOTATION;
+-import static com.android.tools.lint.detector.api.LintUtils.assertionsEnabled;
+-
+-import com.android.annotations.NonNull;
+-import com.android.annotations.Nullable;
+-import com.android.utils.XmlUtils;
+-import com.google.common.base.Charsets;
+-import com.google.common.base.Splitter;
+-import com.google.common.collect.Lists;
+-import com.google.common.collect.Maps;
+-import com.google.common.collect.Sets;
+-import com.google.common.io.ByteStreams;
+-import com.google.common.io.Closeables;
+-import com.google.common.io.Files;
+-import com.google.common.xml.XmlEscapers;
+-
+-import org.eclipse.jdt.internal.compiler.ASTVisitor;
+-import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+-import org.eclipse.jdt.internal.compiler.ast.Annotation;
+-import org.eclipse.jdt.internal.compiler.ast.Argument;
+-import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
+-import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+-import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+-import org.eclipse.jdt.internal.compiler.ast.Expression;
+-import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
+-import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+-import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
+-import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+-import org.eclipse.jdt.internal.compiler.ast.NameReference;
+-import org.eclipse.jdt.internal.compiler.ast.NumberLiteral;
+-import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
+-import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
+-import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
+-import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+-import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
+-import org.eclipse.jdt.internal.compiler.impl.ByteConstant;
+-import org.eclipse.jdt.internal.compiler.impl.CharConstant;
+-import org.eclipse.jdt.internal.compiler.impl.Constant;
+-import org.eclipse.jdt.internal.compiler.impl.DoubleConstant;
+-import org.eclipse.jdt.internal.compiler.impl.FloatConstant;
+-import org.eclipse.jdt.internal.compiler.impl.IntConstant;
+-import org.eclipse.jdt.internal.compiler.impl.LongConstant;
+-import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
+-import org.eclipse.jdt.internal.compiler.impl.ShortConstant;
+-import org.eclipse.jdt.internal.compiler.impl.StringConstant;
+-import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
+-import org.eclipse.jdt.internal.compiler.lookup.Binding;
+-import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+-import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+-import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
+-import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair;
+-import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+-import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
+-import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding;
+-import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+-import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
+-import org.eclipse.jdt.internal.compiler.lookup.Scope;
+-import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
+-import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+-import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+-import org.w3c.dom.Document;
+-import org.w3c.dom.Element;
+-import org.w3c.dom.Node;
+-import org.w3c.dom.NodeList;
+-import org.xml.sax.SAXException;
+-import org.xml.sax.SAXParseException;
+-
+-import java.io.BufferedWriter;
+-import java.io.File;
+-import java.io.FileInputStream;
+-import java.io.FileOutputStream;
+-import java.io.FileWriter;
+-import java.io.IOException;
+-import java.io.PrintWriter;
+-import java.io.StringWriter;
+-import java.io.Writer;
+-import java.lang.reflect.Field;
+-import java.util.ArrayList;
+-import java.util.Arrays;
+-import java.util.Collection;
+-import java.util.Collections;
+-import java.util.Comparator;
+-import java.util.List;
+-import java.util.Map;
+-import java.util.Set;
+-import java.util.jar.JarEntry;
+-import java.util.jar.JarInputStream;
+-import java.util.jar.JarOutputStream;
+-import java.util.regex.Matcher;
+-import java.util.regex.Pattern;
+-import java.util.zip.ZipEntry;
+-
+-/**
+- * Annotation extractor which looks for annotations in parsed compilation units and writes
+- * the annotations into a format suitable for use by IntelliJ and Android Studio etc;
+- * it's basically an XML file, organized by package, which lists the signatures for
+- * fields and methods in classes in the given package, and identifiers method parameters
+- * by index, and lists the annotations annotated on that element.
+- * <p>
+- * This is primarily intended for use in Android libraries such as the support library,
+- * where you want to use the resource int ({@code StringRes}, {@code DrawableRes}, and so on)
+- * annotations to indicate what types of id's are expected, or the {@code IntDef} or
+- * {@code StringDef} annotations to record which specific constants are allowed in int and
+- * String parameters.
+- * <p>
+- * However, the code is also used to extract SDK annotations from the platform, where
+- * the package names of the annotations differ slightly (and where the nullness annotations
+- * do not have class retention for example). Therefore, this code contains some extra
+- * support not needed when extracting annotations in an Android library, such as code
+- * to skip annotations for any method/field not mentioned in the API database, and code
+- * to rewrite the android.jar file to insert annotations in the generated bytecode.
+- * <p>
+- * TODO:
+- * - Warn if the {@code @IntDef} annotation is used on a non-int, and similarly if
+- * {@code @StringDef} is used on a non-string
+- * - Ignore annotations defined on @hide elements
+- */
+-public class Extractor {
+- /** Whether we should include type args like <T*gt; in external annotations signatures */
+- private static final boolean INCLUDE_TYPE_ARGS = false;
+-
+- /** Whether to sort annotation attributes (otherwise their declaration order is used) */
+- private final boolean sortAnnotations;
+-
+- /**
+- * Whether we should include class-retention annotations into the extracted file;
+- * we don't need {@code android.support.annotation.Nullable} to be in the extracted XML
+- * file since it has class retention and will appear in the compiled .jar version of
+- * the library
+- */
+- private final boolean includeClassRetentionAnnotations;
+-
+- /**
+- * Whether we should skip nullable annotations in merged in annotations zip files
+- * (these are typically from infer nullity, which sometimes is a bit aggressive
+- * in assuming something should be marked as nullable; see for example issue #66999
+- * or all the manual removals of findViewById @Nullable return value annotations
+- */
+- private static final boolean INCLUDE_INFERRED_NULLABLE = false;
+-
+- public static final String ANDROID_ANNOTATIONS_PREFIX = "android.annotation.";
+- public static final String ANDROID_NULLABLE = "android.annotation.Nullable";
+- public static final String SUPPORT_NULLABLE = "android.support.annotation.Nullable";
+- public static final String SUPPORT_KEEP = "android.support.annotation.Keep";
+- public static final String RESOURCE_TYPE_ANNOTATIONS_SUFFIX = "Res";
+- public static final String ANDROID_NOTNULL = "android.annotation.NonNull";
+- public static final String SUPPORT_NOTNULL = "android.support.annotation.NonNull";
+- public static final String ANDROID_INT_DEF = "android.annotation.IntDef";
+- public static final String ANDROID_INT_RANGE = "android.annotation.IntRange";
+- public static final String ANDROID_STRING_DEF = "android.annotation.StringDef";
+- public static final String REQUIRES_PERMISSION = "android.support.annotation.RequiresPermission";
+- public static final String ANDROID_REQUIRES_PERMISSION = "android.annotation.RequiresPermission";
+- public static final String IDEA_NULLABLE = "org.jetbrains.annotations.Nullable";
+- public static final String IDEA_NOTNULL = "org.jetbrains.annotations.NotNull";
+- public static final String IDEA_MAGIC = "org.intellij.lang.annotations.MagicConstant";
+- public static final String IDEA_CONTRACT = "org.jetbrains.annotations.Contract";
+- public static final String IDEA_NON_NLS = "org.jetbrains.annotations.NonNls";
+- public static final String ATTR_VAL = "val";
+-
+- @NonNull
+- private final Map<String, List<AnnotationData>> types = Maps.newHashMap();
+-
+- @NonNull
+- private final Set<String> irrelevantAnnotations = Sets.newHashSet();
+-
+- private final File classDir;
+-
+- @NonNull
+- private final Map<String, Map<String, List<Item>>> itemMap = Maps.newHashMap();
+-
+- @Nullable
+- private final ApiDatabase apiFilter;
+-
+- private final boolean displayInfo;
+-
+- private final Map<String,Integer> stats = Maps.newHashMap();
+- private int filteredCount;
+- private int mergedCount;
+- private final Set<CompilationUnitDeclaration> processedFiles = Sets.newHashSetWithExpectedSize(100);
+- private final Set<String> ignoredAnnotations = Sets.newHashSet();
+- private boolean listIgnored;
+- private Map<String,List<Annotation>> typedefs;
+- private List<String> typedefClasses;
+- private Map<String,Boolean> sourceRetention;
+- private final List<Item> keepItems = Lists.newArrayList();
+-
+- public Extractor(@Nullable ApiDatabase apiFilter, @Nullable File classDir, boolean displayInfo,
+- boolean includeClassRetentionAnnotations, boolean sortAnnotations) {
+- this.apiFilter = apiFilter;
+- this.listIgnored = apiFilter != null;
+- this.classDir = classDir;
+- this.displayInfo = displayInfo;
+- this.includeClassRetentionAnnotations = includeClassRetentionAnnotations;
+- this.sortAnnotations = sortAnnotations;
+- }
+-
+- public void extractFromProjectSource(Collection<CompilationUnitDeclaration> units) {
+- TypedefCollector collector = new TypedefCollector(units, false /*requireHide*/,
+- true /*requireSourceRetention*/);
+- typedefs = collector.getTypedefs();
+- typedefClasses = collector.getNonPublicTypedefClasses();
+-
+- for (CompilationUnitDeclaration unit : units) {
+- analyze(unit);
+- }
+- }
+-
+- public void removeTypedefClasses() {
+- if (classDir != null && typedefClasses != null && !typedefClasses.isEmpty()) {
+- boolean quiet = false;
+- boolean verbose = false;
+- boolean dryRun = false;
+- //noinspection ConstantConditions
+- TypedefRemover remover = new TypedefRemover(this, quiet, verbose, dryRun);
+- remover.remove(classDir, typedefClasses);
+- }
+- }
+-
+- public void export(@Nullable File annotationsZip, @Nullable File proguardCfg) {
+- if (proguardCfg != null) {
+- if (keepItems.isEmpty()) {
+- if (proguardCfg.exists()) {
+- //noinspection ResultOfMethodCallIgnored
+- proguardCfg.delete();
+- }
+- } else if (writeKeepRules(proguardCfg)) {
+- info("ProGuard keep rules written to " + proguardCfg);
+- }
+- }
+-
+- if (annotationsZip != null) {
+- if (itemMap.isEmpty()) {
+- if (annotationsZip.exists()) {
+- //noinspection ResultOfMethodCallIgnored
+- annotationsZip.delete();
+- }
+- } else if (writeExternalAnnotations(annotationsZip)) {
+- writeStats();
+- info("Annotations written to " + annotationsZip);
+- }
+- }
+-
+- }
+-
+- public void writeStats() {
+- if (!displayInfo) {
+- return;
+- }
+-
+- if (!stats.isEmpty()) {
+- List<String> annotations = Lists.newArrayList(stats.keySet());
+- Collections.sort(annotations, new Comparator<String>() {
+- @Override
+- public int compare(String s1, String s2) {
+- int frequency1 = stats.get(s1);
+- int frequency2 = stats.get(s2);
+- int delta = frequency2 - frequency1;
+- if (delta != 0) {
+- return delta;
+- }
+- return s1.compareTo(s2);
+- }
+- });
+- Map<String,String> fqnToName = Maps.newHashMap();
+- int max = 0;
+- int count = 0;
+- for (String fqn : annotations) {
+- String name = fqn.substring(fqn.lastIndexOf('.') + 1);
+- fqnToName.put(fqn, name);
+- max = Math.max(max, name.length());
+- count += stats.get(fqn);
+- }
+-
+- StringBuilder sb = new StringBuilder(200);
+- sb.append("Extracted ").append(count).append(" Annotations:");
+- for (String fqn : annotations) {
+- sb.append('\n');
+- String name = fqnToName.get(fqn);
+- for (int i = 0, n = max - name.length() + 1; i < n; i++) {
+- sb.append(' ');
+- }
+- sb.append('@');
+- sb.append(name);
+- sb.append(':').append(' ');
+- sb.append(Integer.toString(stats.get(fqn)));
+- }
+- if (sb.length() > 0) {
+- info(sb.toString());
+- }
+- }
+-
+- if (filteredCount > 0) {
+- info(filteredCount + " of these were filtered out (not in API database file)");
+- }
+- if (mergedCount > 0) {
+- info(mergedCount + " additional annotations were merged in");
+- }
+- }
+-
+- @SuppressWarnings("UseOfSystemOutOrSystemErr")
+- void info(final String message) {
+- if (displayInfo) {
+- System.out.println(message);
+- }
+- }
+-
+- @SuppressWarnings("UseOfSystemOutOrSystemErr")
+- static void error(String message) {
+- System.err.println("Error: " + message);
+- }
+-
+- @SuppressWarnings("UseOfSystemOutOrSystemErr")
+- static void warning(String message) {
+- System.out.println("Warning: " + message);
+- }
+-
+- private void analyze(CompilationUnitDeclaration unit) {
+- if (processedFiles.contains(unit)) {
+- // The code to process all roots seems to hit some of the same classes
+- // repeatedly... so filter these out manually
+- return;
+- }
+- processedFiles.add(unit);
+-
+- AnnotationVisitor visitor = new AnnotationVisitor();
+- unit.traverse(visitor, unit.scope);
+- }
+-
+- @Nullable
+- private static ClassScope findClassScope(Scope scope) {
+- while (scope != null) {
+- if (scope instanceof ClassScope) {
+- return (ClassScope)scope;
+- }
+- scope = scope.parent;
+- }
+-
+- return null;
+- }
+-
+- @Nullable
+- static String getFqn(@NonNull Annotation annotation) {
+- if (annotation.resolvedType != null) {
+- return new String(annotation.resolvedType.readableName());
+- }
+- return null;
+- }
+-
+- @Nullable
+- private static String getFqn(@NonNull ClassScope scope) {
+- TypeDeclaration typeDeclaration = scope.referenceType();
+- if (typeDeclaration != null && typeDeclaration.binding != null) {
+- return new String(typeDeclaration.binding.readableName());
+- }
+- return null;
+- }
+-
+- @Nullable
+- private static String getFqn(@NonNull MethodScope scope) {
+- ClassScope classScope = findClassScope(scope);
+- if (classScope != null) {
+- return getFqn(classScope);
+- }
+-
+- return null;
+- }
+-
+- @Nullable
+- private static String getFqn(@NonNull BlockScope scope) {
+- ClassScope classScope = findClassScope(scope);
+- if (classScope != null) {
+- return getFqn(classScope);
+- }
+-
+- return null;
+- }
+-
+- boolean hasSourceRetention(@NonNull String fqn, @Nullable Annotation annotation) {
+- if (sourceRetention == null) {
+- sourceRetention = Maps.newHashMapWithExpectedSize(20);
+- // The @IntDef and @String annotations have always had source retention,
+- // and always must (because we can't express fully qualified field references
+- // in a .class file.)
+- sourceRetention.put(INT_DEF_ANNOTATION, true);
+- sourceRetention.put(STRING_DEF_ANNOTATION, true);
+- // The @Nullable and @NonNull annotations have always had class retention
+- sourceRetention.put(SUPPORT_NOTNULL, false);
+- sourceRetention.put(SUPPORT_NULLABLE, false);
+-
+- // TODO: Look at support library statistics and put the other most
+- // frequently referenced annotations in here statically
+-
+- // The resource annotations vary: up until 22.0.1 they had source
+- // retention but then switched to class retention.
+- }
+-
+- Boolean source = sourceRetention.get(fqn);
+-
+- if (source != null) {
+- return source;
+- }
+-
+- if (annotation == null || annotation.type == null
+- || annotation.type.resolvedType == null) {
+- // Assume it's class retention: that's what nearly all annotations
+- // currently are. (We do dynamic lookup of unknown ones to allow for
+- // this version of the Gradle plugin to be able to work on future
+- // versions of the support library with new annotations, where it's
+- // possible some annotations need to use source retention.
+- sourceRetention.put(fqn, false);
+- return false;
+- } else if (annotation.type.resolvedType.getAnnotations() != null) {
+- for (AnnotationBinding binding : annotation.type.resolvedType.getAnnotations()) {
+- if (hasSourceRetention(binding)) {
+- sourceRetention.put(fqn, true);
+- return true;
+- }
+- }
+- }
+-
+- sourceRetention.put(fqn, false);
+- return false;
+- }
+-
+- @SuppressWarnings("unused")
+- static boolean hasSourceRetention(@NonNull AnnotationBinding a) {
+- if (new String(a.getAnnotationType().readableName()).equals("java.lang.annotation.Retention")) {
+- ElementValuePair[] pairs = a.getElementValuePairs();
+- if (pairs == null || pairs.length != 1) {
+- warning("Expected exactly one parameter passed to @Retention");
+- return false;
+- }
+- ElementValuePair pair = pairs[0];
+- Object value = pair.getValue();
+- if (value instanceof FieldBinding) {
+- FieldBinding field = (FieldBinding) value;
+- if ("SOURCE".equals(new String(field.readableName()))) {
+- return true;
+- }
+- }
+- }
+-
+- return false;
+- }
+-
+- @SuppressWarnings("unused")
+- static boolean hasSourceRetention(@NonNull Annotation[] annotations) {
+- for (Annotation annotation : annotations) {
+- String typeName = Extractor.getFqn(annotation);
+- if ("java.lang.annotation.Retention".equals(typeName)) {
+- MemberValuePair[] pairs = annotation.memberValuePairs();
+- if (pairs == null || pairs.length != 1) {
+- warning("Expected exactly one parameter passed to @Retention");
+- return false;
+- }
+- MemberValuePair pair = pairs[0];
+- Expression value = pair.value;
+- if (value instanceof NameReference) {
+- NameReference reference = (NameReference) value;
+- Binding binding = reference.binding;
+- if (binding != null) {
+- if (binding instanceof FieldBinding) {
+- FieldBinding fb = (FieldBinding) binding;
+- if ("SOURCE".equals(new String(fb.name)) &&
+- "java.lang.annotation.RetentionPolicy".equals(
+- new String(fb.declaringClass.readableName()))) {
+- return true;
+- }
+- }
+- }
+- }
+- }
+- }
+-
+- return false;
+- }
+-
+- private void addAnnotations(@Nullable Annotation[] annotations, @NonNull Item item) {
+- if (annotations != null) {
+- for (Annotation annotation : annotations) {
+- if (isRelevantAnnotation(annotation)) {
+- String fqn = getFqn(annotation);
+- if (SUPPORT_KEEP.equals(fqn)) {
+- // Put keep rules in a different place; we don't want to write
+- // these out into the external annotations database, they go
+- // into a special proguard file
+- keepItems.add(item);
+- } else {
+- addAnnotation(annotation, fqn, item.annotations);
+- }
+- }
+- }
+- }
+- }
+-
+- private void addAnnotation(@NonNull Annotation annotation, @Nullable String fqn,
+- @NonNull List<AnnotationData> list) {
+- if (fqn == null) {
+- return;
+- }
+-
+- if (fqn.equals(ANDROID_NULLABLE) || fqn.equals(SUPPORT_NULLABLE)) {
+- recordStats(fqn);
+- list.add(new AnnotationData(SUPPORT_NULLABLE));
+- return;
+- }
+-
+- if (fqn.equals(ANDROID_NOTNULL) || fqn.equals(SUPPORT_NOTNULL)) {
+- recordStats(fqn);
+- list.add(new AnnotationData(SUPPORT_NOTNULL));
+- return;
+- }
+-
+- if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
+- && fqn.endsWith(RESOURCE_TYPE_ANNOTATIONS_SUFFIX)) {
+- recordStats(fqn);
+- list.add(new AnnotationData(fqn));
+- return;
+- }
+-
+- if (fqn.startsWith(ANDROID_ANNOTATIONS_PREFIX)) {
+- // System annotations: translate to support library annotations
+- if (fqn.endsWith(RESOURCE_TYPE_ANNOTATIONS_SUFFIX)) {
+- // Translate e.g. android.annotation.DrawableRes to
+- // android.support.annotation.DrawableRes
+- String resAnnotation = SUPPORT_ANNOTATIONS_PREFIX +
+- fqn.substring(ANDROID_ANNOTATIONS_PREFIX.length());
+- if (!includeClassRetentionAnnotations
+- && !hasSourceRetention(resAnnotation, null)) {
+- return;
+- }
+- recordStats(resAnnotation);
+- list.add(new AnnotationData(resAnnotation));
+- return;
+- } else if (isRelevantFrameworkAnnotation(fqn)) {
+- // Translate other android.annotation annotations into corresponding
+- // support annotations
+- String supportAnnotation = SUPPORT_ANNOTATIONS_PREFIX +
+- fqn.substring(ANDROID_ANNOTATIONS_PREFIX.length());
+- if (!includeClassRetentionAnnotations
+- && !hasSourceRetention(supportAnnotation, null)) {
+- return;
+- }
+- recordStats(supportAnnotation);
+- list.add(createData(supportAnnotation, annotation));
+- }
+- }
+-
+- if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
+- recordStats(fqn);
+- list.add(createData(fqn, annotation));
+- return;
+- }
+-
+- if (isMagicConstant(fqn)) {
+- List<AnnotationData> indirect = types.get(fqn);
+- if (indirect != null) {
+- list.addAll(indirect);
+- }
+- }
+- }
+-
+- private void recordStats(String fqn) {
+- Integer count = stats.get(fqn);
+- if (count == null) {
+- count = 0;
+- }
+- stats.put(fqn, count + 1);
+- }
+-
+- private boolean hasRelevantAnnotations(@Nullable Annotation[] annotations) {
+- if (annotations == null) {
+- return false;
+- }
+-
+- for (Annotation annotation : annotations) {
+- if (isRelevantAnnotation(annotation)) {
+- return true;
+- }
+- }
+-
+- return false;
+- }
+-
+- private boolean isRelevantAnnotation(@NonNull Annotation annotation) {
+- String fqn = getFqn(annotation);
+- if (fqn == null || fqn.startsWith("java.lang.")) {
+- return false;
+- }
+- if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
+- if (fqn.equals(SUPPORT_KEEP)) {
+- return true; // even with class file retention we want to process these
+- }
+-
+- //noinspection PointlessBooleanExpression,ConstantConditions,RedundantIfStatement
+- if (!includeClassRetentionAnnotations && !hasSourceRetention(fqn, annotation)) {
+- return false;
+- }
+-
+- return true;
+- } else if (fqn.startsWith(ANDROID_ANNOTATIONS_PREFIX)) {
+- return isRelevantFrameworkAnnotation(fqn);
+- }
+- if (fqn.equals(ANDROID_NULLABLE) || fqn.equals(ANDROID_NOTNULL)
+- || isMagicConstant(fqn)) {
+- return true;
+- } else if (fqn.equals(IDEA_CONTRACT)) {
+- return true;
+- }
+-
+- return false;
+- }
+-
+- private static boolean isRelevantFrameworkAnnotation(@NonNull String fqn) {
+- return fqn.startsWith(ANDROID_ANNOTATIONS_PREFIX)
+- && !fqn.endsWith(".Widget")
+- && !fqn.endsWith(".TargetApi")
+- && !fqn.endsWith(".SystemApi")
+- && !fqn.endsWith(".SuppressLint")
+- && !fqn.endsWith(".SdkConstant");
+- }
+-
+- boolean isMagicConstant(String typeName) {
+- if (irrelevantAnnotations.contains(typeName)
+- || typeName.startsWith("java.lang.")) { // @Override, @SuppressWarnings, etc.
+- return false;
+- }
+- if (types.containsKey(typeName) ||
+- typeName.equals(INT_DEF_ANNOTATION) ||
+- typeName.equals(STRING_DEF_ANNOTATION) ||
+- typeName.equals(INT_RANGE_ANNOTATION) ||
+- typeName.equals(ANDROID_INT_RANGE) ||
+- typeName.equals(ANDROID_INT_DEF) ||
+- typeName.equals(ANDROID_STRING_DEF)) {
+- return true;
+- }
+-
+- List<Annotation> typeDefs = typedefs.get(typeName);
+- // We only support a single level of IntDef type annotations, not arbitrary nesting
+- if (typeDefs != null) {
+- boolean match = false;
+- for (Annotation typeDef : typeDefs) {
+- String fqn = getFqn(typeDef);
+- if (isNestedAnnotation(fqn)) {
+- List<AnnotationData> list = types.get(typeName);
+- if (list == null) {
+- list = new ArrayList<AnnotationData>(2);
+- types.put(typeName, list);
+- }
+- addAnnotation(typeDef, fqn, list);
+- match = true;
+- }
+- }
+-
+- return match;
+- }
+-
+- irrelevantAnnotations.add(typeName);
+-
+- return false;
+- }
+-
+- static boolean isNestedAnnotation(@Nullable String fqn) {
+- return (fqn != null &&
+- (fqn.equals(INT_DEF_ANNOTATION) ||
+- fqn.equals(STRING_DEF_ANNOTATION) ||
+- fqn.equals(REQUIRES_PERMISSION) ||
+- fqn.equals(ANDROID_REQUIRES_PERMISSION) ||
+- fqn.equals(INT_RANGE_ANNOTATION) ||
+- fqn.equals(ANDROID_INT_RANGE) ||
+- fqn.equals(ANDROID_INT_DEF) ||
+- fqn.equals(ANDROID_STRING_DEF)));
+- }
+-
+- private boolean writeKeepRules(@NonNull File proguardCfg) {
+- if (!keepItems.isEmpty()) {
+- try {
+- Writer writer = new BufferedWriter(new FileWriter(proguardCfg));
+- try {
+- Collections.sort(keepItems);
+- for (Item item : keepItems) {
+- writer.write(item.getKeepRule());
+- writer.write('\n');
+- }
+- } finally {
+- writer.close();
+- }
+- } catch (IOException ioe) {
+- error(ioe.toString());
+- return true;
+- }
+-
+- // Now that we've handled these items, remove them from the list
+- // such that we don't accidentally also emit them into the annotations.zip
+- // file, where they are not needed
+- for (Item item : keepItems) {
+- removeItem(item.getQualifiedClassName(), item);
+- }
+- } else if (proguardCfg.exists()) {
+- //noinspection ResultOfMethodCallIgnored
+- proguardCfg.delete();
+- }
+- return false;
+- }
+-
+- private boolean writeExternalAnnotations(@NonNull File annotationsZip) {
+- try {
+- FileOutputStream fileOutputStream = new FileOutputStream(annotationsZip);
+- JarOutputStream zos = new JarOutputStream(fileOutputStream);
+-
+- try {
+- // TODO: Extract to share with keep rules
+- List<String> sortedPackages = new ArrayList<String>(itemMap.keySet());
+- Collections.sort(sortedPackages);
+- for (String pkg : sortedPackages) {
+- // Note: Using / rather than File.separator: jar lib requires it
+- String name = pkg.replace('.', '/') + "/annotations.xml";
+-
+- JarEntry outEntry = new JarEntry(name);
+- zos.putNextEntry(outEntry);
+-
+- StringWriter stringWriter = new StringWriter(1000);
+- PrintWriter writer = new PrintWriter(stringWriter);
+- try {
+- writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+- "<root>");
+-
+- Map<String, List<Item>> classMap = itemMap.get(pkg);
+- List<String> classes = new ArrayList<String>(classMap.keySet());
+- Collections.sort(classes);
+- for (String cls : classes) {
+- List<Item> items = classMap.get(cls);
+- Collections.sort(items);
+- for (Item item : items) {
+- item.write(writer);
+- }
+- }
+-
+- writer.println("</root>\n");
+- writer.close();
+- String xml = stringWriter.toString();
+-
+- // Validate
+- if (assertionsEnabled()) {
+- Document document = checkDocument(pkg, xml, false);
+- if (document == null) {
+- error("Could not parse XML document back in for entry " + name
+- + ": invalid XML?\n\"\"\"\n" + xml + "\n\"\"\"\n");
+- return false;
+- }
+- }
+- byte[] bytes = xml.getBytes(Charsets.UTF_8);
+- zos.write(bytes);
+- zos.closeEntry();
+- } finally {
+- writer.close();
+- }
+- }
+- } finally {
+- zos.flush();
+- zos.close();
+- }
+- } catch (IOException ioe) {
+- error(ioe.toString());
+- return false;
+- }
+-
+- return true;
+- }
+-
+- private void addItem(@NonNull String fqn, @NonNull Item item) {
+- // Not part of the API?
+- if (apiFilter != null && item.isFiltered(apiFilter)) {
+- if (isListIgnored()) {
+- info("Skipping API because it is not part of the API file: " + item);
+- }
+-
+- filteredCount++;
+- return;
+- }
+-
+- String pkg = getPackage(fqn);
+- Map<String, List<Item>> classMap = itemMap.get(pkg);
+- if (classMap == null) {
+- classMap = Maps.newHashMapWithExpectedSize(100);
+- itemMap.put(pkg, classMap);
+- }
+- List<Item> items = classMap.get(fqn);
+- if (items == null) {
+- items = Lists.newArrayList();
+- classMap.put(fqn, items);
+- }
+-
+- items.add(item);
+- }
+-
+- private void removeItem(@NonNull String classFqn, @NonNull Item item) {
+- String pkg = getPackage(classFqn);
+- Map<String, List<Item>> classMap = itemMap.get(pkg);
+- if (classMap != null) {
+- List<Item> items = classMap.get(classFqn);
+- if (items != null) {
+- items.remove(item);
+- if (items.isEmpty()) {
+- classMap.remove(classFqn);
+- if (classMap.isEmpty()) {
+- itemMap.remove(pkg);
+- }
+- }
+- }
+- }
+- }
+-
+- @Nullable
+- private Item findItem(@NonNull String fqn, @NonNull Item item) {
+- String pkg = getPackage(fqn);
+- Map<String, List<Item>> classMap = itemMap.get(pkg);
+- if (classMap == null) {
+- return null;
+- }
+- List<Item> items = classMap.get(fqn);
+- if (items == null) {
+- return null;
+- }
+- for (Item existing : items) {
+- if (existing.equals(item)) {
+- return existing;
+- }
+- }
+-
+- return null;
+- }
+-
+- @Nullable
+- private static Document checkDocument(@NonNull String pkg, @NonNull String xml,
+- boolean namespaceAware) {
+- try {
+- return XmlUtils.parseDocument(xml, namespaceAware);
+- } catch (SAXException sax) {
+- warning("Failed to parse document for package " + pkg + ": " + sax.toString());
+- } catch (Exception e) {
+- // pass
+- // This method is deliberately silent; will return null
+- }
+-
+- return null;
+- }
+-
+- public void mergeExisting(@NonNull File file) {
+- if (file.isDirectory()) {
+- File[] files = file.listFiles();
+- if (files != null) {
+- for (File child : files) {
+- mergeExisting(child);
+- }
+- }
+- } else if (file.isFile()) {
+- if (file.getPath().endsWith(DOT_JAR)) {
+- mergeFromJar(file);
+- } else if (file.getPath().endsWith(DOT_XML)) {
+- try {
+- String xml = Files.toString(file, Charsets.UTF_8);
+- mergeAnnotationsXml(file.getPath(), xml);
+- } catch (IOException e) {
+- error("Aborting: I/O problem during transform: " + e.toString());
+- }
+- }
+- }
+- }
+-
+- private void mergeFromJar(@NonNull File jar) {
+- // Reads in an existing annotations jar and merges in entries found there
+- // with the annotations analyzed from source.
+- JarInputStream zis = null;
+- try {
+- @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
+- FileInputStream fis = new FileInputStream(jar);
+- zis = new JarInputStream(fis);
+- ZipEntry entry = zis.getNextEntry();
+- while (entry != null) {
+- if (entry.getName().endsWith(".xml")) {
+- byte[] bytes = ByteStreams.toByteArray(zis);
+- String xml = new String(bytes, Charsets.UTF_8);
+- mergeAnnotationsXml(jar.getPath() + ": " + entry, xml);
+- }
+- entry = zis.getNextEntry();
+- }
+- } catch (IOException e) {
+- error("Aborting: I/O problem during transform: " + e.toString());
+- } finally {
+- //noinspection deprecation
+- try {
+- Closeables.close(zis, true /* swallowIOException */);
+- } catch (IOException e) {
+- // cannot happen
+- }
+- }
+- }
+-
+- private void mergeAnnotationsXml(@NonNull String path, @NonNull String xml) {
+- try {
+- Document document = XmlUtils.parseDocument(xml, false);
+- mergeDocument(document);
+- } catch (Exception e) {
+- String message = "Failed to merge " + path + ": " + e.toString();
+- if (e instanceof SAXParseException) {
+- SAXParseException spe = (SAXParseException)e;
+- message = "Line " + spe.getLineNumber() + ":" + spe.getColumnNumber() + ": " + message;
+- }
+- error(message);
+- if (!(e instanceof IOException)) {
+- e.printStackTrace();
+- }
+- }
+- }
+-
+- private void mergeDocument(@NonNull Document document) {
+- @SuppressWarnings("SpellCheckingInspection")
+- final Pattern XML_SIGNATURE = Pattern.compile(
+- // Class (FieldName | Type? Name(ArgList) Argnum?)
+- //"(\\S+) (\\S+|(.*)\\s+(\\S+)\\((.*)\\)( \\d+)?)");
+- "(\\S+) (\\S+|((.*)\\s+)?(\\S+)\\((.*)\\)( \\d+)?)");
+-
+- Element root = document.getDocumentElement();
+- String rootTag = root.getTagName();
+- assert rootTag.equals("root") : rootTag;
+-
+- for (Element item : getChildren(root)) {
+- String signature = item.getAttribute(ATTR_NAME);
+- if (signature == null || signature.equals("null")) {
+- continue; // malformed item
+- }
+-
+- if (!hasRelevantAnnotations(item)) {
+- continue;
+- }
+-
+- signature = unescapeXml(signature);
+- if (signature.equals("java.util.Arrays void sort(T[], java.util.Comparator<?) 0")) {
+- // Incorrect metadata (unbalanced <>'s)
+- // See IDEA-137385
+- signature = "java.util.Arrays void sort(T[], java.util.Comparator<?>) 0";
+- }
+-
+- Matcher matcher = XML_SIGNATURE.matcher(signature);
+- if (matcher.matches()) {
+- String containingClass = matcher.group(1);
+- if (containingClass == null) {
+- warning("Could not find class for " + signature);
+- }
+- String methodName = matcher.group(5);
+- if (methodName != null) {
+- String type = matcher.group(4);
+- boolean isConstructor = type == null;
+- String parameters = matcher.group(6);
+- mergeMethodOrParameter(item, matcher, containingClass, methodName, type,
+- isConstructor, parameters);
+- } else {
+- String fieldName = matcher.group(2);
+- mergeField(item, containingClass, fieldName);
+- }
+- } else {
+- if (signature.indexOf(' ') != -1 || signature.indexOf('.') == -1) {
+- warning("No merge match for signature " + signature);
+- } // else: probably just a class signature, e.g. for @NonNls
+- }
+- }
+- }
+-
+- @NonNull
+- private static String unescapeXml(@NonNull String escaped) {
+- String workingString = escaped.replace(QUOT_ENTITY, "\"");
+- workingString = workingString.replace(LT_ENTITY, "<");
+- workingString = workingString.replace(GT_ENTITY, ">");
+- workingString = workingString.replace(APOS_ENTITY, "'");
+- workingString = workingString.replace(AMP_ENTITY, "&");
+-
+- return workingString;
+- }
+-
+- @NonNull
+- private static String escapeXml(@NonNull String unescaped) {
+- return XmlEscapers.xmlAttributeEscaper().escape(unescaped);
+- }
+-
+- private void mergeField(Element item, String containingClass, String fieldName) {
+- if (apiFilter != null &&
+- !apiFilter.hasField(containingClass, fieldName)) {
+- if (isListIgnored()) {
+- info("Skipping imported element because it is not part of the API file: "
+- + containingClass + "#" + fieldName);
+- }
+- filteredCount++;
+- } else {
+- FieldItem fieldItem = new FieldItem(containingClass, ClassKind.CLASS, fieldName, null);
+- Item existing = findItem(containingClass, fieldItem);
+- if (existing != null) {
+- mergedCount += mergeAnnotations(item, existing);
+- } else {
+- addItem(containingClass, fieldItem);
+- mergedCount += addAnnotations(item, fieldItem);
+- }
+- }
+- }
+-
+- private void mergeMethodOrParameter(Element item, Matcher matcher, String containingClass,
+- String methodName, String type, boolean constructor, String parameters) {
+- parameters = fixParameterString(parameters);
+-
+- if (apiFilter != null &&
+- !apiFilter.hasMethod(containingClass, methodName, parameters)) {
+- if (isListIgnored()) {
+- info("Skipping imported element because it is not part of the API file: "
+- + containingClass + "#" + methodName + "(" + parameters + ")");
+- }
+- filteredCount++;
+- return;
+- }
+-
+- String argNum = matcher.group(7);
+- if (argNum != null) {
+- argNum = argNum.trim();
+- ParameterItem parameterItem = new ParameterItem(containingClass, ClassKind.CLASS, type,
+- methodName, parameters, constructor, argNum);
+- Item existing = findItem(containingClass, parameterItem);
+-
+- if ("java.util.Calendar".equals(containingClass) && "set".equals(methodName)
+- && Integer.parseInt(argNum) > 0) {
+- // Skip the metadata for Calendar.set(int, int, int+); see
+- // https://code.google.com/p/android/issues/detail?id=73982
+- return;
+- }
+-
+- if (existing != null) {
+- mergedCount += mergeAnnotations(item, existing);
+- } else {
+- addItem(containingClass, parameterItem);
+- mergedCount += addAnnotations(item, parameterItem);
+- }
+- } else {
+- MethodItem methodItem = new MethodItem(containingClass, ClassKind.CLASS, type,
+- methodName, parameters, constructor);
+- Item existing = findItem(containingClass, methodItem);
+- if (existing != null) {
+- mergedCount += mergeAnnotations(item, existing);
+- } else {
+- addItem(containingClass, methodItem);
+- mergedCount += addAnnotations(item, methodItem);
+- }
+- }
+- }
+-
+- // The parameter declaration used in XML files should not have duplicated spaces,
+- // and there should be no space after commas (we can't however strip out all spaces,
+- // since for example the spaces around the "extends" keyword needs to be there in
+- // types like Map<String,? extends Number>
+- private static String fixParameterString(String parameters) {
+- return parameters.replace(" ", " ").replace(", ", ",");
+- }
+-
+- private boolean hasRelevantAnnotations(Element item) {
+- for (Element annotationElement : getChildren(item)) {
+- if (isRelevantAnnotation(annotationElement)) {
+- return true;
+- }
+- }
+-
+- return false;
+- }
+-
+- private boolean isRelevantAnnotation(Element annotationElement) {
+- AnnotationData annotation = createAnnotation(annotationElement);
+- if (annotation == null) {
+- // Unsupported annotation in import
+- return false;
+- }
+- if (isNullable(annotation.name) || isNonNull(annotation.name)
+- || annotation.name.startsWith(ANDROID_ANNOTATIONS_PREFIX)
+- || annotation.name.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
+- return true;
+- } else if (annotation.name.equals(IDEA_CONTRACT)) {
+- return true;
+- } else if (annotation.name.equals(IDEA_NON_NLS)) {
+- return false;
+- } else {
+- if (!ignoredAnnotations.contains(annotation.name)) {
+- ignoredAnnotations.add(annotation.name);
+- if (isListIgnored()) {
+- info("(Ignoring merge annotation " + annotation.name + ")");
+- }
+- }
+- }
+-
+- return false;
+- }
+-
+- @NonNull
+- private static List<Element> getChildren(@NonNull Element element) {
+- NodeList itemList = element.getChildNodes();
+- int length = itemList.getLength();
+- List<Element> result = new ArrayList<Element>(Math.max(5, length / 2 + 1));
+- for (int i = 0; i < length; i++) {
+- Node node = itemList.item(i);
+- if (node.getNodeType() != Node.ELEMENT_NODE) {
+- continue;
+- }
+-
+- result.add((Element) node);
+- }
+-
+- return result;
+- }
+-
+- private int addAnnotations(Element itemElement, Item item) {
+- int count = 0;
+- for (Element annotationElement : getChildren(itemElement)) {
+- if (!isRelevantAnnotation(annotationElement)) {
+- continue;
+- }
+- AnnotationData annotation = createAnnotation(annotationElement);
+- item.annotations.add(annotation);
+- count++;
+- }
+- return count;
+- }
+-
+- private int mergeAnnotations(Element itemElement, Item item) {
+- int count = 0;
+- loop:
+- for (Element annotationElement : getChildren(itemElement)) {
+- if (!isRelevantAnnotation(annotationElement)) {
+- continue;
+- }
+- AnnotationData annotation = createAnnotation(annotationElement);
+- if (annotation == null) {
+- continue;
+- }
+- boolean haveNullable = false;
+- boolean haveNotNull = false;
+- for (AnnotationData existing : item.annotations) {
+- if (isNonNull(existing.name)) {
+- haveNotNull = true;
+- }
+- if (isNullable(existing.name)) {
+- haveNullable = true;
+- }
+- if (existing.equals(annotation)) {
+- continue loop;
+- }
+- }
+-
+- // Make sure we don't have a conflict between nullable and not nullable
+- if (isNonNull(annotation.name) && haveNullable ||
+- isNullable(annotation.name) && haveNotNull) {
+- warning("Found both @Nullable and @NonNull after import for " + item);
+- continue;
+- }
+-
+- item.annotations.add(annotation);
+- count++;
+- }
+-
+- return count;
+- }
+-
+- private static boolean isNonNull(String name) {
+- return name.equals(IDEA_NOTNULL)
+- || name.equals(ANDROID_NOTNULL)
+- || name.equals(SUPPORT_NOTNULL);
+- }
+-
+- private static boolean isNullable(String name) {
+- return name.equals(IDEA_NULLABLE)
+- || name.equals(ANDROID_NULLABLE)
+- || name.equals(SUPPORT_NULLABLE);
+- }
+-
+- private AnnotationData createAnnotation(Element annotationElement) {
+- String tagName = annotationElement.getTagName();
+- assert tagName.equals("annotation") : tagName;
+- String name = annotationElement.getAttribute(ATTR_NAME);
+- assert name != null && !name.isEmpty();
+- AnnotationData annotation;
+- if (IDEA_MAGIC.equals(name)) {
+- List<Element> children = getChildren(annotationElement);
+- assert children.size() == 1 : children.size();
+- Element valueElement = children.get(0);
+- String valName = valueElement.getAttribute(ATTR_NAME);
+- String value = valueElement.getAttribute(ATTR_VAL);
+- boolean flagsFromClass = valName.equals("flagsFromClass");
+- boolean flag = valName.equals("flags") || flagsFromClass;
+- if (valName.equals("valuesFromClass") || flagsFromClass) {
+- // Not supported
+- boolean found = false;
+- if (value.endsWith(DOT_CLASS)) {
+- String clsName = value.substring(0, value.length() - DOT_CLASS.length());
+- StringBuilder sb = new StringBuilder();
+- sb.append('{');
+-
+-
+- Field[] reflectionFields = null;
+- try {
+- Class<?> cls = Class.forName(clsName);
+- reflectionFields = cls.getDeclaredFields();
+- } catch (Exception ignore) {
+- // Class not available: not a problem. We'll rely on API filter.
+- // It's mainly used for sorting anyway.
+- }
+- if (apiFilter != null) {
+- // Search in API database
+- Set<String> fields = apiFilter.getDeclaredIntFields(clsName);
+- if ("java.util.zip.ZipEntry".equals(clsName)) {
+- // The metadata says valuesFromClass ZipEntry, and unfortunately
+- // that class implements ZipConstants and therefore imports a large
+- // number of irrelevant constants that aren't valid here. Instead,
+- // only allow these two:
+- fields = Sets.newHashSet("STORED", "DEFLATED");
+- }
+-
+- if (fields != null) {
+- List<String> sorted = Lists.newArrayList(fields);
+- Collections.sort(sorted);
+- if (reflectionFields != null) {
+- final Map<String,Integer> rank = Maps.newHashMap();
+- for (int i = 0, n = sorted.size(); i < n; i++) {
+- rank.put(sorted.get(i), reflectionFields.length + i);
+-
+- }
+- for (int i = 0, n = reflectionFields.length; i < n; i++) {
+- rank.put(reflectionFields[i].getName(), i);
+- }
+- Collections.sort(sorted, new Comparator<String>() {
+- @Override
+- public int compare(String o1, String o2) {
+- int rank1 = rank.get(o1);
+- int rank2 = rank.get(o2);
+- int delta = rank1 - rank2;
+- if (delta != 0) {
+- return delta;
+- }
+- return o1.compareTo(o2);
+- }
+- });
+- }
+- boolean first = true;
+- for (String field : sorted) {
+- if (first) {
+- first = false;
+- } else {
+- sb.append(',').append(' ');
+- }
+- sb.append(clsName).append('.').append(field);
+- }
+- found = true;
+- }
+- }
+- // Attempt to sort in reflection order
+- if (!found && reflectionFields != null && (apiFilter == null || apiFilter.hasClass(clsName))) {
+- // Attempt with reflection
+- boolean first = true;
+- for (Field field : reflectionFields) {
+- if (field.getType() == Integer.TYPE ||
+- field.getType() == int.class) {
+- if (first) {
+- first = false;
+- } else {
+- sb.append(',').append(' ');
+- }
+- sb.append(clsName).append('.').append(field.getName());
+- }
+- }
+- }
+- sb.append('}');
+- value = sb.toString();
+- if (sb.length() > 2) { // 2: { }
+- found = true;
+- }
+- }
+-
+- if (!found) {
+- return null;
+- }
+- }
+-
+- //noinspection VariableNotUsedInsideIf
+- if (apiFilter != null) {
+- value = removeFiltered(value);
+- while (value.contains(", ,")) {
+- value = value.replace(", ,",",");
+- }
+- if (value.startsWith(", ")) {
+- value = value.substring(2);
+- }
+- }
+-
+- annotation = new AnnotationData(
+- valName.equals("stringValues") ? STRING_DEF_ANNOTATION : INT_DEF_ANNOTATION,
+- new String[] {
+- TYPE_DEF_VALUE_ATTRIBUTE, value,
+- flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null });
+- } else if (STRING_DEF_ANNOTATION.equals(name) || ANDROID_STRING_DEF.equals(name) ||
+- INT_DEF_ANNOTATION.equals(name) || ANDROID_INT_DEF.equals(name)) {
+- List<Element> children = getChildren(annotationElement);
+- Element valueElement = children.get(0);
+- String valName = valueElement.getAttribute(ATTR_NAME);
+- assert TYPE_DEF_VALUE_ATTRIBUTE.equals(valName);
+- String value = valueElement.getAttribute(ATTR_VAL);
+- boolean flag = false;
+- if (children.size() == 2) {
+- valueElement = children.get(1);
+- assert TYPE_DEF_FLAG_ATTRIBUTE.equals(valueElement.getAttribute(ATTR_NAME));
+- flag = VALUE_TRUE.equals(valueElement.getAttribute(ATTR_VAL));
+- }
+- boolean intDef = INT_DEF_ANNOTATION.equals(name) || ANDROID_INT_DEF.equals(name);
+- annotation = new AnnotationData(
+- intDef ? INT_DEF_ANNOTATION : STRING_DEF_ANNOTATION,
+- new String[] { TYPE_DEF_VALUE_ATTRIBUTE, value,
+- flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null});
+- } else if (IDEA_CONTRACT.equals(name)) {
+- List<Element> children = getChildren(annotationElement);
+- assert children.size() == 1 : children.size();
+- Element valueElement = children.get(0);
+- String value = valueElement.getAttribute(ATTR_VAL);
+- annotation = new AnnotationData(name, new String[] { TYPE_DEF_VALUE_ATTRIBUTE, value });
+- } else if (isNonNull(name)) {
+- annotation = new AnnotationData(SUPPORT_NOTNULL);
+- } else if (isNullable(name)) {
+- //noinspection PointlessBooleanExpression,ConstantConditions
+- if (!INCLUDE_INFERRED_NULLABLE && IDEA_NULLABLE.equals(name)) {
+- return null;
+- }
+- annotation = new AnnotationData(SUPPORT_NULLABLE);
+- } else {
+- List<Element> children = getChildren(annotationElement);
+- if (children.isEmpty()) {
+- return new AnnotationData(name);
+- }
+- List<String> attributeStrings = Lists.newArrayList();
+- for (Element valueElement : children) {
+- attributeStrings.add(valueElement.getAttribute(ATTR_NAME));
+- attributeStrings.add(valueElement.getAttribute(ATTR_VAL));
+- }
+- annotation = new AnnotationData(name, attributeStrings.toArray(
+- new String[attributeStrings.size()]));
+- }
+- return annotation;
+- }
+-
+- private String removeFiltered(String value) {
+- assert apiFilter != null;
+- if (value.startsWith("{")) {
+- value = value.substring(1);
+- }
+- if (value.endsWith("}")) {
+- value = value.substring(0, value.length() - 1);
+- }
+- value = value.trim();
+- StringBuilder sb = new StringBuilder(value.length());
+- sb.append('{');
+- for (String fqn : Splitter.on(',').omitEmptyStrings().trimResults().split(value)) {
+- fqn = unescapeXml(fqn);
+- if (fqn.startsWith("\"")) {
+- continue;
+- }
+- int index = fqn.lastIndexOf('.');
+- String cls = fqn.substring(0, index);
+- String field = fqn.substring(index + 1);
+- if (apiFilter.hasField(cls, field)) {
+- if (sb.length() > 1) { // 0: '{'
+- sb.append(", ");
+- }
+- sb.append(fqn);
+- } else if (isListIgnored()) {
+- info("Skipping constant from typedef because it is not part of the SDK: " + fqn);
+- }
+- }
+- sb.append('}');
+- return escapeXml(sb.toString());
+- }
+-
+-
+- private static String getPackage(String fqn) {
+- // Extract package from the given fqn. Attempts to handle inner classes;
+- // e.g. "foo.bar.Foo.Bar will return "foo.bar".
+- int index = 0;
+- int last = 0;
+- while (true) {
+- index = fqn.indexOf('.', index);
+- if (index == -1) {
+- break;
+- }
+- last = index;
+- if (index < fqn.length() - 1) {
+- char next = fqn.charAt(index + 1);
+- if (Character.isUpperCase(next)) {
+- break;
+- }
+- }
+- index++;
+- }
+-
+- return fqn.substring(0, last);
+- }
+-
+- @SuppressWarnings("UnusedDeclaration")
+- public void setListIgnored(boolean listIgnored) {
+- this.listIgnored = listIgnored;
+- }
+-
+- public boolean isListIgnored() {
+- return listIgnored;
+- }
+-
+- public AnnotationData createData(@NonNull String name, @NonNull Annotation annotation) {
+- MemberValuePair[] pairs = annotation.memberValuePairs();
+- if (pairs == null || pairs.length == 0) {
+- return new AnnotationData(name);
+- }
+- return new AnnotationData(name, pairs);
+- }
+-
+- private class AnnotationData {
+- @NonNull
+- public final String name;
+-
+- @Nullable
+- public String[] attributeStrings;
+-
+- @Nullable
+- public MemberValuePair[] attributes;
+-
+- private AnnotationData(@NonNull String name) {
+- this.name = name;
+- }
+-
+- private AnnotationData(@NonNull String name, @Nullable MemberValuePair[] pairs) {
+- this(name);
+- attributes = pairs;
+- assert attributes == null || attributes.length > 0;
+- }
+-
+- private AnnotationData(@NonNull String name, @Nullable String[] attributeStrings) {
+- this(name);
+- this.attributeStrings = attributeStrings;
+- assert attributeStrings != null && attributeStrings.length > 0;
+- }
+-
+- void write(PrintWriter writer) {
+- writer.print(" <annotation name=\"");
+- writer.print(name);
+-
+- if (attributes != null) {
+- writer.print("\">");
+- writer.println();
+- //noinspection PointlessBooleanExpression,ConstantConditions
+- if (attributes.length > 1 && sortAnnotations) {
+- // Ensure that the value attribute is written first
+- Arrays.sort(attributes, new Comparator<MemberValuePair>() {
+- private String getName(MemberValuePair pair) {
+- if (pair.name == null) {
+- return ATTR_VALUE;
+- } else {
+- return new String(pair.name);
+- }
+- }
+-
+- private int rank(MemberValuePair pair) {
+- return ATTR_VALUE.equals(getName(pair)) ? -1 : 0;
+- }
+-
+- @Override
+- public int compare(MemberValuePair o1, MemberValuePair o2) {
+- int r1 = rank(o1);
+- int r2 = rank(o2);
+- int delta = r1 - r2;
+- if (delta != 0) {
+- return delta;
+- }
+- return getName(o1).compareTo(getName(o2));
+- }
+- });
+- }
+-
+- MemberValuePair[] attributes = this.attributes;
+-
+- if (attributes.length == 1
+- && name.startsWith(REQUIRES_PERMISSION)
+- && name.length() > REQUIRES_PERMISSION.length()
+- && attributes[0].value instanceof SingleMemberAnnotation) {
+- // The external annotations format does not allow for nested/complex annotations.
+- // However, these special annotations (@RequiresPermission.Read,
+- // @RequiresPermission.Write, etc) are known to only be simple containers with a
+- // single permission child, so instead we "inline" the content:
+- // @Read(@RequiresPermission(allOf={P1,P2},conditional=true)
+- // =>
+- // @RequiresPermission.Read(allOf({P1,P2},conditional=true)
+- // That's setting attributes that don't actually exist on the container permission,
+- // but we'll counteract that on the read-annotations side.
+- SingleMemberAnnotation annotation = (SingleMemberAnnotation)attributes[0].value;
+- attributes = annotation.memberValuePairs();
+- }
+-
+- for (MemberValuePair pair : attributes) {
+- writer.print(" <val name=\"");
+- if (pair.name != null) {
+- writer.print(pair.name);
+- } else {
+- writer.print(ATTR_VALUE); // default name
+- }
+- writer.print("\" val=\"");
+- writer.print(escapeXml(attributeString(pair.value)));
+- writer.println("\" />");
+- }
+- writer.println(" </annotation>");
+-
+- } else if (attributeStrings != null) {
+- writer.print("\">");
+- writer.println();
+- for (int i = 0; i < attributeStrings.length; i += 2) {
+- String name = attributeStrings[i];
+- String value = attributeStrings[i + 1];
+- if (name == null) {
+- continue;
+- }
+- writer.print(" <val name=\"");
+- writer.print(name);
+- writer.print("\" val=\"");
+- writer.print(escapeXml(value));
+- writer.println("\" />");
+- }
+- writer.println(" </annotation>");
+- } else {
+- writer.println("\" />");
+- }
+- }
+-
+- @Override
+- public boolean equals(Object o) {
+- if (this == o) {
+- return true;
+- }
+- if (o == null || getClass() != o.getClass()) {
+- return false;
+- }
+-
+- AnnotationData that = (AnnotationData) o;
+-
+- return name.equals(that.name);
+- }
+-
+- @Override
+- public int hashCode() {
+- return name.hashCode();
+- }
+-
+- private String attributeString(@NonNull Expression value) {
+- StringBuilder sb = new StringBuilder();
+- appendExpression(sb, value);
+- return sb.toString();
+- }
+-
+- private boolean appendExpression(@NonNull StringBuilder sb,
+- @NonNull Expression expression) {
+- if (expression instanceof ArrayInitializer) {
+- sb.append('{');
+- ArrayInitializer initializer = (ArrayInitializer) expression;
+- boolean first = true;
+- int initialLength = sb.length();
+- for (Expression e : initializer.expressions) {
+- int length = sb.length();
+- if (first) {
+- first = false;
+- } else {
+- sb.append(", ");
+- }
+- boolean appended = appendExpression(sb, e);
+- if (!appended) {
+- // trunk off comma if it bailed for some reason (e.g. constant
+- // filtered out by API etc)
+- sb.setLength(length);
+- if (length == initialLength) {
+- first = true;
+- }
+- }
+- }
+- sb.append('}');
+- return true;
+- } else if (expression instanceof NameReference) {
+- NameReference reference = (NameReference) expression;
+- if (reference.binding != null) {
+- if (reference.binding instanceof FieldBinding) {
+- FieldBinding fb = (FieldBinding)reference.binding;
+- Constant constant = fb.constant();
+- if (constant != null && constant != Constant.NotAConstant &&
+- !(name.equals(INT_DEF_ANNOTATION)) &&
+- !(name.equals(STRING_DEF_ANNOTATION))) {
+- if (constant instanceof StringConstant) {
+- sb.append('"').append(constant.stringValue()).append('"');
+- return true;
+- } else if (constant instanceof IntConstant) {
+- sb.append(Integer.toString(constant.intValue()));
+- return true;
+- } else if (constant instanceof BooleanConstant) {
+- sb.append(Boolean.toString(constant.booleanValue()));
+- return true;
+- } else if (constant instanceof LongConstant) {
+- sb.append(Long.toString(constant.longValue()));
+- return true;
+- } else if (constant instanceof DoubleConstant) {
+- sb.append(Double.toString(constant.doubleValue()));
+- return true;
+- } else if (constant instanceof CharConstant) {
+- sb.append('\'').append(Character.toString(constant.charValue())).append('\'');
+- return true;
+- } else if (constant instanceof FloatConstant) {
+- sb.append(Float.toString(constant.floatValue()));
+- return true;
+- } else if (constant instanceof ShortConstant) {
+- sb.append(Short.toString(constant.shortValue()));
+- return true;
+- } else if (constant instanceof ByteConstant) {
+- sb.append(Byte.toString(constant.byteValue()));
+- return true;
+- }
+- }
+- if (fb.declaringClass != null) {
+- if (apiFilter != null &&
+- !apiFilter.hasField(
+- new String(fb.declaringClass.readableName()),
+- new String(fb.name))) {
+- if (isListIgnored()) {
+- info("Filtering out typedef constant "
+- + new String(fb.declaringClass.readableName()) + "."
+- + new String(fb.name) + "");
+- }
+- return false;
+- }
+- sb.append(fb.declaringClass.readableName());
+- sb.append('.');
+- sb.append(fb.name);
+- } else {
+- sb.append(reference.binding.readableName());
+- }
+- } else {
+- sb.append(reference.binding.readableName());
+- }
+- return true;
+- } else {
+- warning("No binding for reference " + reference);
+- }
+- return false;
+- } else if (expression instanceof StringLiteral) {
+- StringLiteral s = (StringLiteral) expression;
+- sb.append('"');
+- sb.append(s.source());
+- sb.append('"');
+- return true;
+- } else if (expression instanceof NumberLiteral) {
+- NumberLiteral number = (NumberLiteral) expression;
+- sb.append(number.source());
+- return true;
+- } else if (expression instanceof TrueLiteral) {
+- sb.append(true);
+- return true;
+- } else if (expression instanceof FalseLiteral) {
+- sb.append(false);
+- return true;
+- } else if (expression instanceof org.eclipse.jdt.internal.compiler.ast.NullLiteral) {
+- sb.append("null");
+- return true;
+- } else {
+- // BinaryExpression etc can happen if you put "3 + 4" in as an integer!
+- if (expression.constant != null) {
+- if (expression.constant.typeID() == TypeIds.T_int) {
+- sb.append(expression.constant.intValue());
+- return true;
+- } else if (expression.constant.typeID() == TypeIds.T_JavaLangString) {
+- sb.append('"');
+- sb.append(expression.constant.stringValue());
+- sb.append('"');
+- return true;
+- } else {
+- warning("Unexpected type for constant " + expression.constant.toString());
+- }
+- } else {
+- warning("Unexpected annotation expression of type " + expression.getClass() + " and is "
+- + expression);
+- }
+- }
+-
+- return false;
+- }
+- }
+-
+- public enum ClassKind {
+- CLASS,
+- INTERFACE,
+- ENUM,
+- ANNOTATION;
+-
+- @NonNull
+- public static ClassKind forType(@Nullable TypeDeclaration declaration) {
+- if (declaration == null) {
+- return CLASS;
+- }
+- switch (TypeDeclaration.kind(declaration.modifiers)) {
+- case TypeDeclaration.INTERFACE_DECL:
+- return INTERFACE;
+- case TypeDeclaration.ANNOTATION_TYPE_DECL:
+- return ANNOTATION;
+- case TypeDeclaration.ENUM_DECL:
+- return ENUM;
+- default:
+- return CLASS;
+- }
+- }
+-
+- public String getKeepType() {
+- // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+- switch (this) {
+- case INTERFACE:
+- return "interface";
+- case ENUM:
+- return "enum";
+-
+- case ANNOTATION:
+- case CLASS:
+- default:
+- return "class";
+- }
+- }
+- }
+-
+- /**
+- * An item in the XML file: this corresponds to a method, a field, or a method parameter, and
+- * has an associated set of annotations
+- */
+- private abstract static class Item implements Comparable<Item> {
+- @NonNull public final String containingClass;
+- @NonNull public final ClassKind classKind;
+-
+- public Item(@NonNull String containingClass, @NonNull ClassKind classKind) {
+- this.containingClass = containingClass;
+- this.classKind = classKind;
+- }
+-
+- public final List<AnnotationData> annotations = Lists.newArrayList();
+-
+- void write(PrintWriter writer) {
+- if (annotations.isEmpty()) {
+- return;
+- }
+- writer.print(" <item name=\"");
+- writer.print(getSignature());
+- writer.println("\">");
+-
+- for (AnnotationData annotation : annotations) {
+- annotation.write(writer);
+- }
+- writer.print(" </item>");
+- writer.println();
+- }
+-
+- abstract boolean isFiltered(@NonNull ApiDatabase database);
+-
+- @NonNull
+- abstract String getSignature();
+-
+- @Override
+- public int compareTo(@SuppressWarnings("NullableProblems") @NonNull Item item) {
+- String signature1 = getSignature();
+- String signature2 = item.getSignature();
+-
+- // IntelliJ's sorting order is not on the escaped HTML but the original
+- // signatures, which means android.os.AsyncTask<Params,Progress,Result>
+- // should appear *after* android.os.AsyncTask.Status, which when the <'s are
+- // escaped it does not
+- signature1 = signature1.replace('&', '.');
+- signature2 = signature2.replace('&', '.');
+-
+- return signature1.compareTo(signature2);
+- }
+-
+- @NonNull
+- public abstract String getKeepRule();
+-
+- @NonNull
+- public abstract String getQualifiedClassName();
+- }
+-
+- private static class ClassItem extends Item {
+- private ClassItem(@NonNull String containingClass, @NonNull ClassKind classKind) {
+- super(containingClass, classKind);
+- }
+-
+- @NonNull
+- static ClassItem create(@NonNull String classFqn, @NonNull ClassKind kind) {
+- classFqn = ApiDatabase.getRawClass(classFqn);
+- return new ClassItem(classFqn, kind);
+- }
+-
+- @Override
+- boolean isFiltered(@NonNull ApiDatabase database) {
+- return !database.hasClass(containingClass);
+- }
+-
+- @NonNull
+- @Override
+- String getSignature() {
+- return escapeXml(containingClass);
+- }
+-
+- @NonNull
+- @Override
+- public String getKeepRule() {
+- // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+- return "-keep " + classKind.getKeepType() + " " + containingClass + "\n";
+- }
+-
+- @NonNull
+- @Override
+- public String getQualifiedClassName() {
+- return containingClass;
+- }
+-
+- @Override
+- public String toString() {
+- return "Class " + containingClass;
+- }
+-
+- @Override
+- public boolean equals(Object o) {
+- if (this == o) {
+- return true;
+- }
+- if (o == null || getClass() != o.getClass()) {
+- return false;
+- }
+-
+- ClassItem that = (ClassItem) o;
+-
+- return containingClass.equals(that.containingClass);
+- }
+-
+- @Override
+- public int hashCode() {
+- return containingClass.hashCode();
+- }
+- }
+-
+- private static class FieldItem extends Item {
+-
+- @NonNull
+- public final String fieldName;
+-
+- @Nullable
+- public final String fieldType;
+-
+- private FieldItem(@NonNull String containingClass, @NonNull ClassKind classKind,
+- @NonNull String fieldName, @Nullable String fieldType) {
+- super(containingClass, classKind);
+- this.fieldName = fieldName;
+- this.fieldType = fieldType;
+- }
+-
+- @Nullable
+- static FieldItem create(String classFqn, @NonNull ClassKind classKind, FieldBinding field) {
+- String name = new String(field.name);
+- String type = getFieldType(field);
+- return classFqn != null ? new FieldItem(classFqn, classKind, name, type) : null;
+- }
+-
+- @Nullable
+- private static String getFieldType(FieldBinding binding) {
+- if (binding.type != null) {
+- return new String(binding.type.readableName());
+- }
+-
+- return null;
+- }
+-
+- @Override
+- boolean isFiltered(@NonNull ApiDatabase database) {
+- return !database.hasField(containingClass, fieldName);
+- }
+-
+- @NonNull
+- @Override
+- String getSignature() {
+- return escapeXml(containingClass) + ' ' + fieldName;
+- }
+-
+- @NonNull
+- @Override
+- public String getKeepRule() {
+- if (fieldType == null) {
+- return ""; // imported item; these can't have keep rules
+- }
+- // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+- return "-keep " + classKind.getKeepType() + " " + containingClass +
+- " {\n " + fieldType + " " + fieldName + "\n}\n";
+- }
+-
+- @NonNull
+- @Override
+- public String getQualifiedClassName() {
+- return containingClass;
+- }
+-
+- @Override
+- public String toString() {
+- return "Field " + containingClass + "#" + fieldName;
+- }
+-
+- @Override
+- public boolean equals(Object o) {
+- if (this == o) {
+- return true;
+- }
+- if (o == null || getClass() != o.getClass()) {
+- return false;
+- }
+-
+- FieldItem that = (FieldItem) o;
+-
+- return containingClass.equals(that.containingClass) &&
+- fieldName.equals(that.fieldName);
+- }
+-
+- @Override
+- public int hashCode() {
+- int result = fieldName.hashCode();
+- result = 31 * result + containingClass.hashCode();
+- return result;
+- }
+- }
+-
+- private static class MethodItem extends Item {
+-
+- @NonNull
+- public final String methodName;
+-
+- @NonNull
+- public final String parameterList;
+-
+- @Nullable
+- public final String returnType;
+-
+- public final boolean isConstructor;
+-
+- private MethodItem(
+- @NonNull String containingClass,
+- @NonNull ClassKind classKind,
+- @Nullable String returnType,
+- @NonNull String methodName,
+- @NonNull String parameterList,
+- boolean isConstructor) {
+- super(containingClass, classKind);
+- this.returnType = returnType;
+- this.methodName = methodName;
+- this.parameterList = parameterList;
+- this.isConstructor = isConstructor;
+- }
+-
+- @NonNull
+- public String getName() {
+- return methodName;
+- }
+-
+- @Nullable
+- static MethodItem create(@Nullable String classFqn,
+- @NonNull ClassKind classKind,
+- @NonNull AbstractMethodDeclaration declaration,
+- @Nullable MethodBinding binding) {
+- if (classFqn == null || binding == null) {
+- return null;
+- }
+- String returnType = getReturnType(binding);
+- String methodName = getMethodName(binding);
+- Argument[] arguments = declaration.arguments;
+- boolean isVarargs = arguments != null && arguments.length > 0 &&
+- arguments[arguments.length - 1].isVarArgs();
+- String parameterList = getParameterList(binding, isVarargs);
+- if (returnType == null || methodName == null) {
+- return null;
+- }
+- //noinspection PointlessBooleanExpression,ConstantConditions
+- if (!INCLUDE_TYPE_ARGS) {
+- classFqn = ApiDatabase.getRawClass(classFqn);
+- methodName = ApiDatabase.getRawMethod(methodName);
+- }
+- return new MethodItem(classFqn, classKind, returnType,
+- methodName, parameterList,
+- binding.isConstructor());
+- }
+-
+- @NonNull
+- @Override
+- String getSignature() {
+- StringBuilder sb = new StringBuilder(100);
+- sb.append(escapeXml(containingClass));
+- sb.append(' ');
+-
+- if (isConstructor) {
+- sb.append(escapeXml(methodName));
+- } else {
+- assert returnType != null;
+- sb.append(escapeXml(returnType));
+- sb.append(' ');
+- sb.append(escapeXml(methodName));
+- }
+-
+- sb.append('(');
+-
+- // The signature must match *exactly* the formatting used by IDEA,
+- // since it looks up external annotations in a map by this key.
+- // Therefore, it is vital that the parameter list uses exactly one
+- // space after each comma between parameters, and *no* spaces between
+- // generics variables, e.g. foo(Map<A,B>, int)
+-
+- // Insert spaces between commas, but not in generics signatures
+- int balance = 0;
+- for (int i = 0, n = parameterList.length(); i < n; i++) {
+- char c = parameterList.charAt(i);
+- if (c == '<') {
+- balance++;
+- sb.append("<");
+- } else if (c == '>') {
+- balance--;
+- sb.append(">");
+- } else if (c == ',') {
+- sb.append(',');
+- if (balance == 0) {
+- sb.append(' ');
+- }
+- } else {
+- sb.append(c);
+- }
+- }
+- sb.append(')');
+- return sb.toString();
+- }
+-
+- @Override
+- boolean isFiltered(@NonNull ApiDatabase database) {
+- return !database.hasMethod(containingClass, methodName, parameterList);
+- }
+-
+- @Override
+- public boolean equals(Object o) {
+- if (this == o) {
+- return true;
+- }
+- if (o == null || getClass() != o.getClass()) {
+- return false;
+- }
+-
+- MethodItem that = (MethodItem) o;
+-
+- return isConstructor == that.isConstructor && containingClass
+- .equals(that.containingClass) && methodName.equals(that.methodName)
+- && parameterList.equals(that.parameterList);
+-
+- }
+-
+- @Override
+- public int hashCode() {
+- int result = methodName.hashCode();
+- result = 31 * result + containingClass.hashCode();
+- result = 31 * result + parameterList.hashCode();
+- result = 31 * result + (returnType != null ? returnType.hashCode() : 0);
+- result = 31 * result + (isConstructor ? 1 : 0);
+- return result;
+- }
+-
+- @Override
+- public String toString() {
+- return "Method " + containingClass + "#" + methodName;
+- }
+-
+- @NonNull
+- @Override
+- public String getKeepRule() {
+- // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+- StringBuilder sb = new StringBuilder();
+- sb.append("-keep ");
+- sb.append(classKind.getKeepType());
+- sb.append(" ");
+- sb.append(containingClass);
+- sb.append(" {\n");
+- sb.append(" ");
+- if (isConstructor) {
+- sb.append("<init>");
+- } else {
+- sb.append(returnType);
+- sb.append(" ");
+- sb.append(methodName);
+- }
+- sb.append("(");
+- sb.append(parameterList); // TODO: Strip generics?
+- sb.append(")\n");
+- sb.append("}\n");
+-
+- return sb.toString();
+- }
+-
+- @NonNull
+- @Override
+- public String getQualifiedClassName() {
+- return containingClass;
+- }
+- }
+-
+- @Nullable
+- private static String getReturnType(MethodBinding binding) {
+- if (binding.returnType != null) {
+- return new String(binding.returnType.readableName());
+- } else if (binding.declaringClass != null) {
+- assert binding.isConstructor();
+- return new String(binding.declaringClass.readableName());
+- }
+-
+- return null;
+- }
+-
+- @Nullable
+- private static String getMethodName(@NonNull MethodBinding binding) {
+- if (binding.isConstructor()) {
+- if (binding.declaringClass != null) {
+- String classFqn = new String(binding.declaringClass.readableName());
+- return classFqn.substring(classFqn.lastIndexOf('.') + 1);
+- }
+-
+- }
+- if (binding.selector != null) {
+- return new String(binding.selector);
+- }
+-
+- assert binding.isConstructor();
+-
+- return null;
+- }
+-
+- @NonNull
+- private static String getParameterList(@NonNull MethodBinding binding, boolean isVarargs) {
+- // Create compact type signature (no spaces around commas or generics arguments)
+- StringBuilder sb = new StringBuilder();
+- TypeBinding[] typeParameters = binding.parameters;
+- if (typeParameters != null) {
+- for (int i = 0, n = typeParameters.length; i < n; i++) {
+- TypeBinding parameter = typeParameters[i];
+- if (i > 0) {
+- sb.append(',');
+- }
+- String str = fixParameterString(new String(parameter.readableName()));
+- if (isVarargs && i == n - 1 && str.endsWith("[]")) {
+- str = str.substring(0, str.length() - 2) + "...";
+- }
+- sb.append(str);
+- }
+- }
+- return sb.toString();
+- }
+-
+- private static class ParameterItem extends MethodItem {
+- @NonNull
+- public final String argIndex;
+-
+- private ParameterItem(
+- @NonNull String containingClass,
+- @NonNull ClassKind classKind,
+- @Nullable String returnType,
+- @NonNull String methodName,
+- @NonNull String parameterList,
+- boolean isConstructor,
+- @NonNull String argIndex) {
+- super(containingClass, classKind, returnType, methodName, parameterList, isConstructor);
+- this.argIndex = argIndex;
+- }
+-
+- @Nullable
+- static ParameterItem create(
+- AbstractMethodDeclaration methodDeclaration,
+- Argument argument,
+- String classFqn,
+- ClassKind classKind,
+- MethodBinding methodBinding,
+- LocalVariableBinding parameterBinding) {
+- if (classFqn == null || methodBinding == null || parameterBinding == null) {
+- return null;
+- }
+-
+- String methodName = getMethodName(methodBinding);
+- Argument[] arguments = methodDeclaration.arguments;
+- boolean isVarargs = arguments != null && arguments.length > 0 &&
+- arguments[arguments.length - 1].isVarArgs();
+- String parameterList = getParameterList(methodBinding, isVarargs);
+- String returnType = getReturnType(methodBinding);
+- if (methodName == null || returnType == null) {
+- return null;
+- }
+-
+- int index = 0;
+- boolean found = false;
+- if (methodDeclaration.arguments != null) {
+- for (Argument a : methodDeclaration.arguments) {
+- if (a == argument) {
+- found = true;
+- break;
+- }
+- index++;
+- }
+- }
+- if (!found) {
+- return null;
+- }
+- String argNum = Integer.toString(index);
+-
+- //noinspection PointlessBooleanExpression,ConstantConditions
+- if (!INCLUDE_TYPE_ARGS) {
+- classFqn = ApiDatabase.getRawClass(classFqn);
+- methodName = ApiDatabase.getRawMethod(methodName);
+- }
+- return new ParameterItem(classFqn, classKind, returnType, methodName, parameterList,
+- methodBinding.isConstructor(), argNum);
+- }
+-
+-
+- @NonNull
+- @Override
+- String getSignature() {
+- return super.getSignature() + ' ' + argIndex;
+- }
+-
+- @Override
+- public boolean equals(Object o) {
+- if (this == o) {
+- return true;
+- }
+- if (o == null || getClass() != o.getClass()) {
+- return false;
+- }
+- if (!super.equals(o)) {
+- return false;
+- }
+-
+- ParameterItem that = (ParameterItem) o;
+-
+- return argIndex.equals(that.argIndex);
+-
+- }
+-
+- @Override
+- public int hashCode() {
+- int result = super.hashCode();
+- result = 31 * result + argIndex.hashCode();
+- return result;
+- }
+-
+- @Override
+- public String toString() {
+- return "Parameter #" + argIndex + " in " + super.toString();
+- }
+-
+- @NonNull
+- @Override
+- public String getKeepRule() {
+- return "";
+- }
+- }
+-
+- private class AnnotationVisitor extends ASTVisitor {
+- @Override
+- public boolean visit(Argument argument, BlockScope scope) {
+- Annotation[] annotations = argument.annotations;
+- if (hasRelevantAnnotations(annotations)) {
+- ReferenceContext referenceContext = scope.referenceContext();
+- if (referenceContext instanceof AbstractMethodDeclaration) {
+- MethodBinding binding = ((AbstractMethodDeclaration) referenceContext).binding;
+- ClassScope classScope = findClassScope(scope);
+- if (classScope == null) {
+- return false;
+- }
+- String fqn = getFqn(classScope);
+- ClassKind kind = ClassKind.forType(classScope.referenceContext);
+- Item item = ParameterItem.create(
+- (AbstractMethodDeclaration) referenceContext, argument, fqn, kind,
+- binding, argument.binding);
+- if (item != null) {
+- addItem(fqn, item);
+- addAnnotations(annotations, item);
+- }
+- }
+- }
+- return false;
+- }
+-
+- @Override
+- public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
+- Annotation[] annotations = constructorDeclaration.annotations;
+- if (hasRelevantAnnotations(annotations)) {
+- MethodBinding constructorBinding = constructorDeclaration.binding;
+- if (constructorBinding == null) {
+- return false;
+- }
+-
+- String fqn = getFqn(scope);
+- ClassKind kind = ClassKind.forType(scope.referenceContext);
+- Item item = MethodItem.create(fqn, kind, constructorDeclaration, constructorBinding);
+- if (item != null) {
+- addItem(fqn, item);
+- addAnnotations(annotations, item);
+- }
+- }
+-
+- Argument[] arguments = constructorDeclaration.arguments;
+- if (arguments != null) {
+- for (Argument argument : arguments) {
+- argument.traverse(this, constructorDeclaration.scope);
+- }
+- }
+- return false;
+- }
+-
+- @Override
+- public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
+- Annotation[] annotations = fieldDeclaration.annotations;
+- if (hasRelevantAnnotations(annotations)) {
+- FieldBinding fieldBinding = fieldDeclaration.binding;
+- if (fieldBinding == null) {
+- return false;
+- }
+-
+- String fqn = getFqn(scope);
+- ClassKind kind = scope.referenceContext instanceof TypeDeclaration ?
+- ClassKind.forType((TypeDeclaration)scope.referenceContext) :
+- ClassKind.CLASS;
+- Item item = FieldItem.create(fqn, kind, fieldBinding);
+- if (item != null && fqn != null) {
+- addItem(fqn, item);
+- addAnnotations(annotations, item);
+- }
+- }
+- return false;
+- }
+-
+- @Override
+- public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
+- Annotation[] annotations = methodDeclaration.annotations;
+- if (hasRelevantAnnotations(annotations)) {
+- MethodBinding methodBinding = methodDeclaration.binding;
+- if (methodBinding == null) {
+- return false;
+- }
+- String fqn = getFqn(scope);
+- ClassKind kind = ClassKind.forType(scope.referenceContext);
+- MethodItem item = MethodItem.create(fqn, kind, methodDeclaration,
+- methodDeclaration.binding);
+- if (item != null) {
+- addItem(fqn, item);
+-
+- // Deliberately skip findViewById()'s return nullability
+- // for now; it's true that findViewById can return null,
+- // but that means all code which does findViewById(R.id.foo).something()
+- // will be flagged as potentially throwing an NPE, and many developers
+- // will do this when they *know* that the id exists (in which case
+- // the method won't return null.)
+- boolean skipReturnAnnotations = false;
+- if ("findViewById".equals(item.getName())) {
+- skipReturnAnnotations = true;
+- if (item.annotations.isEmpty()) {
+- // No other annotations so far: just remove it
+- removeItem(fqn, item);
+- }
+- }
+-
+- if (!skipReturnAnnotations) {
+- addAnnotations(annotations, item);
+- }
+- }
+- }
+-
+- Argument[] arguments = methodDeclaration.arguments;
+- if (arguments != null) {
+- for (Argument argument : arguments) {
+- argument.traverse(this, methodDeclaration.scope);
+- }
+- }
+- return false;
+- }
+-
+- @Override
+- public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
+- Annotation[] annotations = localTypeDeclaration.annotations;
+- if (hasRelevantAnnotations(annotations)) {
+- SourceTypeBinding binding = localTypeDeclaration.binding;
+- if (binding == null) {
+- return true;
+- }
+-
+- String fqn = getFqn(scope);
+- if (fqn == null) {
+- fqn = new String(localTypeDeclaration.binding.readableName());
+- }
+- Item item = ClassItem.create(fqn, ClassKind.forType(localTypeDeclaration));
+- addItem(fqn, item);
+- addAnnotations(annotations, item);
+-
+- }
+- return true;
+- }
+-
+- @Override
+- public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
+- Annotation[] annotations = memberTypeDeclaration.annotations;
+- if (hasRelevantAnnotations(annotations)) {
+- SourceTypeBinding binding = memberTypeDeclaration.binding;
+- if (!(binding instanceof MemberTypeBinding)) {
+- return true;
+- }
+- if (binding.isAnnotationType() || binding.isAnonymousType()) {
+- return false;
+- }
+-
+- String fqn = new String(memberTypeDeclaration.binding.readableName());
+- Item item = ClassItem.create(fqn, ClassKind.forType(memberTypeDeclaration));
+- addItem(fqn, item);
+- addAnnotations(annotations, item);
+- }
+- return true;
+- }
+-
+- @Override
+- public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
+- Annotation[] annotations = typeDeclaration.annotations;
+- if (hasRelevantAnnotations(annotations)) {
+- SourceTypeBinding binding = typeDeclaration.binding;
+- if (binding == null) {
+- return true;
+- }
+- String fqn = new String(typeDeclaration.binding.readableName());
+- Item item = ClassItem.create(fqn, ClassKind.forType(typeDeclaration));
+- addItem(fqn, item);
+- addAnnotations(annotations, item);
+-
+- }
+- return true;
+- }
+- }
+-}
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
+deleted file mode 100644
+index 5924f4c..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
++++ /dev/null
+@@ -1,154 +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.
+- */
+-
+-package com.android.build.gradle.tasks.annotations;
+-
+-import com.android.annotations.NonNull;
+-import com.google.common.collect.Lists;
+-import com.google.common.collect.Maps;
+-
+-import org.eclipse.jdt.internal.compiler.ASTVisitor;
+-import org.eclipse.jdt.internal.compiler.ast.Annotation;
+-import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+-import org.eclipse.jdt.internal.compiler.ast.Javadoc;
+-import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+-import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+-import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+-import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
+-import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
+-
+-import java.io.File;
+-import java.util.ArrayList;
+-import java.util.Collection;
+-import java.util.List;
+-import java.util.Map;
+-
+-/** Gathers information about typedefs (@IntDef and @StringDef */
+-public class TypedefCollector extends ASTVisitor {
+- private Map<String,List<Annotation>> mMap = Maps.newHashMap();
+-
+- private final boolean mRequireHide;
+- private final boolean mRequireSourceRetention;
+- private CompilationUnitDeclaration mCurrentUnit;
+- private List<String> mTypedefClasses = Lists.newArrayList();
+-
+- public TypedefCollector(
+- @NonNull Collection<CompilationUnitDeclaration> units,
+- boolean requireHide,
+- boolean requireSourceRetention) {
+- mRequireHide = requireHide;
+- mRequireSourceRetention = requireSourceRetention;
+-
+- for (CompilationUnitDeclaration unit : units) {
+- mCurrentUnit = unit;
+- unit.traverse(this, unit.scope);
+- mCurrentUnit = null;
+- }
+- }
+-
+- public List<String> getNonPublicTypedefClasses() {
+- return mTypedefClasses;
+- }
+-
+- public Map<String,List<Annotation>> getTypedefs() {
+- return mMap;
+- }
+-
+- @Override
+- public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
+- return recordTypedefs(memberTypeDeclaration);
+-
+- }
+-
+- @Override
+- public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
+- return recordTypedefs(typeDeclaration);
+- }
+-
+- private boolean recordTypedefs(TypeDeclaration declaration) {
+- SourceTypeBinding binding = declaration.binding;
+- if (binding == null) {
+- return false;
+- }
+- Annotation[] annotations = declaration.annotations;
+- if (annotations != null) {
+- if (declaration.binding.isAnnotationType()) {
+- for (Annotation annotation : annotations) {
+- String typeName = Extractor.getFqn(annotation);
+- if (typeName == null) {
+- continue;
+- }
+-
+- if (Extractor.isNestedAnnotation(typeName)) {
+- String fqn = new String(binding.readableName());
+-
+- List<Annotation> list = mMap.get(fqn);
+- if (list == null) {
+- list = new ArrayList<Annotation>(2);
+- mMap.put(fqn, list);
+- }
+- list.add(annotation);
+-
+- if (mRequireHide) {
+- Javadoc javadoc = declaration.javadoc;
+- if (javadoc != null) {
+- StringBuffer stringBuffer = new StringBuffer(200);
+- javadoc.print(0, stringBuffer);
+- String documentation = stringBuffer.toString();
+- if (!documentation.contains("@hide")) {
+- Extractor.warning(getFileName()
+- + ": The typedef annotation " + fqn
+- + " should specify @hide in a doc comment");
+- }
+- }
+- }
+- if (mRequireSourceRetention
+- && !Extractor.hasSourceRetention(annotations)) {
+- Extractor.warning(getFileName()
+- + ": The typedef annotation " + fqn
+- + " should have @Retention(RetentionPolicy.SOURCE)");
+- }
+- if (declaration.binding != null
+- && (declaration.modifiers & ClassFileConstants.AccPublic) == 0) {
+- StringBuilder sb = new StringBuilder(100);
+- for (char c : declaration.binding.qualifiedPackageName()) {
+- if (c == '.') {
+- sb.append('/');
+- } else {
+- sb.append(c);
+- }
+- }
+- sb.append(File.separatorChar);
+- for (char c : declaration.binding.qualifiedSourceName()) {
+- if (c == '.') {
+- sb.append('$');
+- } else {
+- sb.append(c);
+- }
+- }
+- mTypedefClasses.add(sb.toString());
+- }
+- }
+- }
+- }
+- }
+- return true;
+- }
+-
+- private String getFileName() {
+- return new String(mCurrentUnit.getFileName());
+- }
+-}
+diff --git a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java b/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java
+deleted file mode 100644
+index 758f125..0000000
+--- a/base/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java
++++ /dev/null
+@@ -1,164 +0,0 @@
+-/*
+- * Copyright (C) 2015 The Android Open Source Project
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package com.android.build.gradle.tasks.annotations;
+-
+-import static com.android.SdkConstants.DOT_CLASS;
+-import static org.objectweb.asm.Opcodes.ASM5;
+-
+-import com.android.annotations.NonNull;
+-import com.google.common.collect.Lists;
+-import com.google.common.collect.Sets;
+-import com.google.common.io.Files;
+-
+-import org.objectweb.asm.ClassReader;
+-import org.objectweb.asm.ClassVisitor;
+-import org.objectweb.asm.ClassWriter;
+-
+-import java.io.File;
+-import java.io.IOException;
+-import java.util.List;
+-import java.util.Set;
+-
+-/**
+- * Finds and deletes typedef annotation classes (and also warns if their
+- * retention is wrong, such that usages of the annotation embeds data
+- * into the .class file.)
+- * <p>
+- * (Based on the similar class in {@code development/tools/rmtypedefs/})
+- */
+- at SuppressWarnings("SpellCheckingInspection")
+-public class TypedefRemover {
+- private final Extractor mExtractor;
+- private final boolean mQuiet;
+- private final boolean mVerbose;
+- private final boolean mDryRun;
+-
+- public TypedefRemover(
+- @NonNull Extractor extractor,
+- boolean quiet,
+- boolean verbose,
+- boolean dryRun) {
+- mExtractor = extractor;
+- mQuiet = quiet;
+- mVerbose = verbose;
+- mDryRun = dryRun;
+- }
+-
+- private Set<String> mAnnotationNames = Sets.newHashSet();
+- private List<File> mAnnotationClassFiles = Lists.newArrayList();
+- private Set<File> mAnnotationOuterClassFiles = Sets.newHashSet();
+-
+- public void remove(@NonNull File classDir, @NonNull List<String> owners) {
+- if (!mQuiet) {
+- mExtractor.info("Deleting @IntDef and @StringDef annotation class files");
+- }
+-
+- // Record typedef annotation names and files
+- for (String owner : owners) {
+- File file = new File(classDir, owner.replace('/', File.separatorChar) + DOT_CLASS);
+- addTypeDef(owner, file);
+- }
+-
+- // Rewrite the .class files for any classes that *contain* typedefs as innerclasses
+- rewriteOuterClasses();
+-
+- // Removes the actual .class files for the typedef annotations
+- deleteAnnotationClasses();
+- }
+-
+- /**
+- * Records the given class name (internal name) and class file path as corresponding to a
+- * typedef annotation
+- * */
+- private void addTypeDef(String name, File file) {
+- mAnnotationClassFiles.add(file);
+- mAnnotationNames.add(name);
+-
+- String fileName = file.getName();
+- int index = fileName.lastIndexOf('$');
+- if (index != -1) {
+- File parentFile = file.getParentFile();
+- assert parentFile != null : file;
+- File container = new File(parentFile, fileName.substring(0, index) + ".class");
+- if (container.exists()) {
+- mAnnotationOuterClassFiles.add(container);
+- } else {
+- Extractor.error("Warning: Could not find outer class " + container
+- + " for typedef " + file);
+- }
+- }
+- }
+-
+- /**
+- * Rewrites the outer classes containing the typedefs such that they no longer refer to
+- * the (now removed) typedef annotation inner classes
+- */
+- private void rewriteOuterClasses() {
+- for (File file : mAnnotationOuterClassFiles) {
+- byte[] bytes;
+- try {
+- bytes = Files.toByteArray(file);
+- } catch (IOException e) {
+- Extractor.error("Could not read " + file + ": " + e.getLocalizedMessage());
+- continue;
+- }
+-
+- ClassWriter classWriter = new ClassWriter(ASM5);
+- ClassVisitor classVisitor = new ClassVisitor(ASM5, classWriter) {
+- @Override
+- public void visitInnerClass(String name, String outerName, String innerName,
+- int access) {
+- if (!mAnnotationNames.contains(name)) {
+- super.visitInnerClass(name, outerName, innerName, access);
+- }
+- }
+- };
+- ClassReader reader = new ClassReader(bytes);
+- reader.accept(classVisitor, 0);
+- byte[] rewritten = classWriter.toByteArray();
+- try {
+- Files.write(rewritten, file);
+- } catch (IOException e) {
+- Extractor.error("Could not write " + file + ": " + e.getLocalizedMessage());
+- //noinspection UnnecessaryContinue
+- continue;
+- }
+- }
+- }
+-
+- /**
+- * Performs the actual deletion (or display, if in dry-run mode) of the typedef annotation
+- * files
+- */
+- private void deleteAnnotationClasses() {
+- for (File mFile : mAnnotationClassFiles) {
+- if (mVerbose) {
+- if (mDryRun) {
+- mExtractor.info("Would delete " + mFile);
+- } else {
+- mExtractor.info("Deleting " + mFile);
+- }
+- }
+- if (!mDryRun) {
+- boolean deleted = mFile.delete();
+- if (!deleted) {
+- Extractor.warning("Could not delete " + mFile);
+- }
+- }
+- }
+- }
+-}
+\ No newline at end of file
+diff --git a/base/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java b/base/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java
+index b4f4bb0..b91a3dd 100644
+--- a/base/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java
++++ b/base/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java
+@@ -34,7 +34,6 @@ import com.android.build.gradle.internal.dsl.BuildType;
+ import com.android.build.gradle.internal.dsl.CoreBuildType;
+ import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+ import com.android.build.gradle.internal.dsl.DexOptions;
+-import com.android.build.gradle.internal.dsl.LintOptions;
+ import com.android.build.gradle.internal.dsl.PackagingOptions;
+ import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+ import com.android.build.gradle.internal.dsl.ProductFlavor;
+@@ -92,9 +91,6 @@ public abstract class BaseExtension implements AndroidConfig {
+ /** Options for aapt, tool for packaging resources. */
+ final AaptOptions aaptOptions;
+
+- /** Lint options. */
+- final LintOptions lintOptions;
+-
+ /** Dex options. */
+ final DexOptions dexOptions;
+
+@@ -188,7 +184,6 @@ public abstract class BaseExtension implements AndroidConfig {
+
+ aaptOptions = instantiator.newInstance(AaptOptions.class);
+ dexOptions = instantiator.newInstance(DexOptions.class);
+- lintOptions = instantiator.newInstance(LintOptions.class);
+ testOptions = instantiator.newInstance(TestOptions.class);
+ compileOptions = instantiator.newInstance(CompileOptions.class);
+ packagingOptions = instantiator.newInstance(PackagingOptions.class);
+@@ -405,14 +400,6 @@ public abstract class BaseExtension implements AndroidConfig {
+ action.execute(dexOptions);
+ }
+
+- /**
+- * Configure lint options.
+- */
+- public void lintOptions(Action<LintOptions> action) {
+- checkWritability();
+- action.execute(lintOptions);
+- }
+-
+ /** Configures the test options. */
+ public void testOptions(Action<TestOptions> action) {
+ checkWritability();
+@@ -757,12 +744,6 @@ public abstract class BaseExtension implements AndroidConfig {
+
+ /** {@inheritDoc} */
+ @Override
+- public LintOptions getLintOptions() {
+- return lintOptions;
+- }
+-
+- /** {@inheritDoc} */
+- @Override
+ public PackagingOptions getPackagingOptions() {
+ return packagingOptions;
+ }
diff --git a/debian/patches/gradle-experimental.patch b/debian/patches/gradle-experimental.patch
new file mode 100644
index 0000000..6a1d793
--- /dev/null
+++ b/debian/patches/gradle-experimental.patch
@@ -0,0 +1,62 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sat, 6 Feb 2016 21:14:41 +0100
+Subject: gradle experimental
+
+---
+ base/build-system/gradle-experimental/build.gradle | 24 +++++++++++-----------
+ build.gradle | 2 +-
+ 2 files changed, 13 insertions(+), 13 deletions(-)
+
+diff --git a/base/build-system/gradle-experimental/build.gradle b/base/build-system/gradle-experimental/build.gradle
+index ce579e0..c224332 100644
+--- a/base/build-system/gradle-experimental/build.gradle
++++ b/base/build-system/gradle-experimental/build.gradle
+@@ -1,23 +1,23 @@
+ apply plugin: 'groovy'
+
+ // Extract gradle libraries to ensure gradle-core is compatible with older version.
+-String gradleVersion = "2.5"
+-File gradleBinary = file("$rootProject.projectDir/external/gradle/gradle-$gradleVersion-all.zip")
+-File gradleLib = file("$rootProject.ext.androidHostOut/alternate-gradle/gradle-$gradleVersion/lib")
++//String gradleVersion = "2.5"
++//File gradleBinary = file("$rootProject.projectDir/external/gradle/gradle-$gradleVersion-all.zip")
++//File gradleLib = file("$rootProject.ext.androidHostOut/alternate-gradle/gradle-$gradleVersion/lib")
+
+-task extractGradleLibs(type: Copy) {
+- from zipTree(gradleBinary)
+- into gradleLib.parentFile.parentFile
+-}
++//task extractGradleLibs(type: Copy) {
++// from zipTree(gradleBinary)
++// into gradleLib.parentFile.parentFile
++//}
+
+-compileJava.dependsOn extractGradleLibs
++//compileJava.dependsOn extractGradleLibs
+
+-task setupGradleInIde {
+- dependsOn extractGradleLibs
+-}
++//task setupGradleInIde {
++// dependsOn extractGradleLibs
++//}
+
+ dependencies {
+- compile fileTree(dir:gradleLib)
++ //compile fileTree(dir:gradleLib)
+ compile project(':base:gradle-core')
+
+ testCompile 'junit:junit:4.12'
+diff --git a/build.gradle b/build.gradle
+index bf35058..6674c9a 100644
+--- a/build.gradle
++++ b/build.gradle
+@@ -118,7 +118,7 @@ task init {
+ dependsOn prepareRepo
+ dependsOn setupGradleInIde
+ dependsOn copyGradleProperty
+- dependsOn tasks.findByPath(':base:gradle-experimental:setupGradleInIde')
++//dependsOn tasks.findByPath(':base:gradle-experimental:setupGradleInIde')
+ dependsOn tasks.findByPath(':base:builder:generateVersionConstantsJava')
+ }
+
diff --git a/debian/patches/project-test-lib.patch b/debian/patches/project-test-lib.patch
new file mode 100644
index 0000000..d61bd7b
--- /dev/null
+++ b/debian/patches/project-test-lib.patch
@@ -0,0 +1,35 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sun, 7 Feb 2016 20:24:29 +0100
+Subject: project-test-lib
+
+---
+ base/build-system/gradle-core/build.gradle | 2 +-
+ base/build-system/gradle/build.gradle | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/base/build-system/gradle-core/build.gradle b/base/build-system/gradle-core/build.gradle
+index 7e3a673..e36483b 100644
+--- a/base/build-system/gradle-core/build.gradle
++++ b/base/build-system/gradle-core/build.gradle
+@@ -26,7 +26,7 @@ dependencies {
+
+ testCompile 'junit:junit:4.12'
+ testCompile 'org.mockito:mockito-all:1.9.5'
+- testCompile project(':base:project-test-lib')
++ //testCompile project(':base:project-test-lib')
+ testCompile project(':base:testutils')
+ }
+
+diff --git a/base/build-system/gradle/build.gradle b/base/build-system/gradle/build.gradle
+index b36e23e..affa879 100644
+--- a/base/build-system/gradle/build.gradle
++++ b/base/build-system/gradle/build.gradle
+@@ -5,7 +5,7 @@ dependencies {
+ compile gradleApi()
+
+ testCompile 'junit:junit:4.12'
+- testCompile project(':base:project-test-lib')
++ //testCompile project(':base:project-test-lib')
+ }
+
+ group = 'com.android.tools.build'
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..63b7123
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,7 @@
+build.gradle.patch
+settings.gradle.patch
+gradle-experimental.patch
+SdkTestCase.patch
+project-test-lib.patch
+trove3.patch
+disable-lint.patch
diff --git a/debian/patches/settings.gradle.patch b/debian/patches/settings.gradle.patch
new file mode 100644
index 0000000..f721034
--- /dev/null
+++ b/debian/patches/settings.gradle.patch
@@ -0,0 +1,87 @@
+From: Markus Koschany <apo at debian.org>
+Date: Fri, 22 Jan 2016 00:51:17 +0100
+Subject: settings.gradle
+
+---
+ settings.gradle | 38 +++++++++++++++++++-------------------
+ 1 file changed, 19 insertions(+), 19 deletions(-)
+
+diff --git a/settings.gradle b/settings.gradle
+index eb684bb..9ccf1b2 100644
+--- a/settings.gradle
++++ b/settings.gradle
+@@ -4,18 +4,18 @@ include ':base:ant-tasks'
+ include ':base:archquery'
+ include ':base:asset-studio'
+ include ':base:common'
+-include ':base:docs'
++//include ':base:docs'
+ include ':base:ddmlib'
+ include ':base:perflib'
+ include ':base:chartlib'
+ include ':base:draw9patch'
+ include ':base:dvlib'
+-include ':base:jobb'
++//include ':base:jobb'
+ include ':base:layoutlib-api'
+-include ':base:lint'
+-include ':base:lint-api'
+-include ':base:lint-checks'
+-include ':base:lint-tests'
++//include ':base:lint'
++//include ':base:lint-api'
++//include ':base:lint-checks'
++//include ':base:lint-tests'
+ include ':base:manifest-merger'
+ include ':base:ninepatch'
+ include ':base:rule-api'
+@@ -35,10 +35,10 @@ include ':base:builder'
+ include ':base:gradle-model'
+ include ':base:gradle-core'
+ include ':base:gradle'
+-include ':base:gradle-experimental'
+-include ':base:integration-test'
+-include ':base:project-test-lib'
+-include ':base:project-test'
++//include ':base:gradle-experimental'
++//include ':base:integration-test'
++//include ':base:project-test-lib'
++//include ':base:project-test'
+ include ':base:google-services'
+
+ include ':base:templates'
+@@ -50,10 +50,10 @@ project(':base:api-generator' ).projectDir = new File(rootDir, 'base/misc/a
+ project(':base:ant-tasks' ).projectDir = new File(rootDir, 'base/legacy/ant-tasks')
+ project(':base:archquery' ).projectDir = new File(rootDir, 'base/legacy/archquery')
+ project(':base:dvlib' ).projectDir = new File(rootDir, 'base/device_validator/dvlib')
+-project(':base:lint' ).projectDir = new File(rootDir, 'base/lint/cli')
+-project(':base:lint-api' ).projectDir = new File(rootDir, 'base/lint/libs/lint-api')
+-project(':base:lint-checks' ).projectDir = new File(rootDir, 'base/lint/libs/lint-checks')
+-project(':base:lint-tests' ).projectDir = new File(rootDir, 'base/lint/libs/lint-tests')
++//project(':base:lint' ).projectDir = new File(rootDir, 'base/lint/cli')
++//project(':base:lint-api' ).projectDir = new File(rootDir, 'base/lint/libs/lint-api')
++//project(':base:lint-checks' ).projectDir = new File(rootDir, 'base/lint/libs/lint-checks')
++//project(':base:lint-tests' ).projectDir = new File(rootDir, 'base/lint/libs/lint-tests')
+ project(':base:screenshot2' ).projectDir = new File(rootDir, 'base/misc/screenshot2')
+ project(':base:sdklib-test' ).projectDir = new File(rootDir, 'base/sdklib')
+ project(':base:sdklib-test' ).buildFileName = 'test.gradle'
+@@ -63,14 +63,14 @@ project(':base:builder-model' ).projectDir = new File(rootDir, 'base/build-
+ project(':base:builder-test-api' ).projectDir = new File(rootDir, 'base/build-system/builder-test-api')
+ project(':base:transform-api' ).projectDir = new File(rootDir, 'base/build-system/transform-api')
+ project(':base:builder' ).projectDir = new File(rootDir, 'base/build-system/builder')
+-project(':base:docs' ).projectDir = new File(rootDir, 'base/build-system/docs')
++//project(':base:docs' ).projectDir = new File(rootDir, 'base/build-system/docs')
+ project(':base:manifest-merger' ).projectDir = new File(rootDir, 'base/build-system/manifest-merger')
+ project(':base:gradle-core' ).projectDir = new File(rootDir, 'base/build-system/gradle-core')
+ project(':base:gradle' ).projectDir = new File(rootDir, 'base/build-system/gradle')
+-project(':base:gradle-experimental').projectDir = new File(rootDir, 'base/build-system/gradle-experimental')
+-project(':base:integration-test' ).projectDir = new File(rootDir, 'base/build-system/integration-test')
+-project(':base:project-test-lib' ).projectDir = new File(rootDir, 'base/build-system/project-test-lib')
+-project(':base:project-test' ).projectDir = new File(rootDir, 'base/build-system/project-test')
++//project(':base:gradle-experimental').projectDir = new File(rootDir, 'base/build-system/gradle-experimental')
++//project(':base:integration-test' ).projectDir = new File(rootDir, 'base/build-system/integration-test')
++//project(':base:project-test-lib' ).projectDir = new File(rootDir, 'base/build-system/project-test-lib')
++//project(':base:project-test' ).projectDir = new File(rootDir, 'base/build-system/project-test')
+ project(':base:google-services' ).projectDir = new File(rootDir, 'base/build-system/google-services')
+
+ include ':swt:chimpchat'
diff --git a/debian/patches/trove3.patch b/debian/patches/trove3.patch
new file mode 100644
index 0000000..bc6ec92
--- /dev/null
+++ b/debian/patches/trove3.patch
@@ -0,0 +1,154 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sun, 7 Feb 2016 20:46:50 +0100
+Subject: trove3
+
+---
+ .../java/com/android/tools/chartlib/TimelineComponent.java | 2 +-
+ .../main/java/com/android/tools/perflib/heap/ClassObj.java | 6 +++---
+ .../src/main/java/com/android/tools/perflib/heap/Heap.java | 12 ++++++------
+ .../java/com/android/tools/perflib/heap/HprofParser.java | 2 +-
+ .../com/android/tools/perflib/heap/NonRecursiveVisitor.java | 2 +-
+ .../main/java/com/android/tools/perflib/heap/Snapshot.java | 2 +-
+ .../android/tools/perflib/heap/analysis/TopologicalSort.java | 2 +-
+ 7 files changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/base/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java b/base/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java
+index d90b1b8..8138478 100644
+--- a/base/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java
++++ b/base/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java
+@@ -36,7 +36,7 @@ import java.util.Map;
+
+ import javax.swing.Icon;
+
+-import gnu.trove.TIntObjectHashMap;
++import gnu.trove.map.hash.TIntObjectHashMap;
+
+ /**
+ * A component to display a TimelineData object. It locks the timeline object to prevent
+diff --git a/base/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java b/base/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java
+index 121c27c..213deae 100644
+--- a/base/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java
++++ b/base/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java
+@@ -19,7 +19,7 @@ package com.android.tools.perflib.heap;
+ import com.android.annotations.NonNull;
+ import com.android.annotations.Nullable;
+ import com.android.annotations.VisibleForTesting;
+-import gnu.trove.TIntObjectHashMap;
++import gnu.trove.map.hash.TIntObjectHashMap;
+
+ import java.util.*;
+
+@@ -267,7 +267,7 @@ public class ClassObj extends Instance implements Comparable<ClassObj> {
+
+ public int getInstanceCount() {
+ int count = 0;
+- for (Object heapStat : mHeapData.getValues()) {
++ for (Object heapStat : mHeapData.values()) {
+ count += ((HeapData)heapStat).mInstances.size();
+ }
+ return count;
+@@ -275,7 +275,7 @@ public class ClassObj extends Instance implements Comparable<ClassObj> {
+
+ public int getShallowSize() {
+ int size = 0;
+- for (Object heapStat : mHeapData.getValues()) {
++ for (Object heapStat : mHeapData.values()) {
+ size += ((HeapData)heapStat).mShallowSize;
+ }
+ return size;
+diff --git a/base/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java b/base/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java
+index 3d617e6..95d45dc 100644
+--- a/base/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java
++++ b/base/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java
+@@ -22,9 +22,9 @@ import java.util.ArrayList;
+ import java.util.Collection;
+
+ import com.google.common.collect.*;
+-import gnu.trove.TIntObjectHashMap;
+-import gnu.trove.TLongObjectHashMap;
+-import gnu.trove.TObjectProcedure;
++import gnu.trove.map.hash.TIntObjectHashMap;
++import gnu.trove.map.hash.TLongObjectHashMap;
++import gnu.trove.procedure.TObjectProcedure;
+
+ public class Heap {
+
+@@ -145,7 +145,7 @@ public class Heap {
+ }
+
+ public final void dumpInstanceCounts() {
+- for (Object value : mClassesById.getValues()) {
++ for (Object value : mClassesById.values()) {
+ ClassObj theClass = (ClassObj) value;
+ int count = theClass.getInstanceCount();
+
+@@ -156,7 +156,7 @@ public class Heap {
+ }
+
+ public final void dumpSubclasses() {
+- for (Object value : mClassesById.getValues()) {
++ for (Object value : mClassesById.values()) {
+ ClassObj theClass = (ClassObj) value;
+ int count = theClass.mSubclasses.size();
+
+@@ -168,7 +168,7 @@ public class Heap {
+ }
+
+ public final void dumpSizes() {
+- for (Object value : mClassesById.getValues()) {
++ for (Object value : mClassesById.values()) {
+ ClassObj theClass = (ClassObj) value;
+
+ int size = 0;
+diff --git a/base/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java b/base/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java
+index 80271ab..e4d9767 100644
+--- a/base/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java
++++ b/base/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java
+@@ -24,7 +24,7 @@ import com.google.common.primitives.UnsignedInts;
+ import java.io.EOFException;
+ import java.io.IOException;
+
+-import gnu.trove.TLongObjectHashMap;
++import gnu.trove.map.hash.TLongObjectHashMap;
+
+ public class HprofParser {
+
+diff --git a/base/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java b/base/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java
+index c481321..b94f1e9 100644
+--- a/base/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java
++++ b/base/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java
+@@ -21,7 +21,7 @@ import com.android.annotations.NonNull;
+ import java.util.ArrayDeque;
+ import java.util.Deque;
+
+-import gnu.trove.TLongHashSet;
++import gnu.trove.set.hash.TLongHashSet;
+
+ /**
+ * Non-recursive depth-first visitor, managing its own stack.
+diff --git a/base/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java b/base/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java
+index 0b074bf..b216001 100644
+--- a/base/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java
++++ b/base/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java
+@@ -23,7 +23,7 @@ import com.android.tools.perflib.heap.analysis.ShortestDistanceVisitor;
+ import com.android.tools.perflib.heap.analysis.TopologicalSort;
+ import com.android.tools.perflib.heap.io.HprofBuffer;
+ import com.google.common.collect.ImmutableList;
+-import gnu.trove.THashSet;
++import gnu.trove.set.hash.THashSet;
+
+ import java.util.*;
+
+diff --git a/base/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java b/base/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java
+index 3bcc704..02b08cc 100644
+--- a/base/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java
++++ b/base/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java
+@@ -26,7 +26,7 @@ import com.google.common.collect.Lists;
+
+ import java.util.List;
+
+-import gnu.trove.TLongHashSet;
++import gnu.trove.set.hash.TLongHashSet;
+
+ public class TopologicalSort {
+
diff --git a/debian/poms/android-tools-annotations-pom.xml b/debian/poms/android-tools-annotations-pom.xml
new file mode 100644
index 0000000..a5b131a
--- /dev/null
+++ b/debian/poms/android-tools-annotations-pom.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.android.tools</groupId>
+ <artifactId>annotations</artifactId>
+ <version>24.5.0</version>
+ <description>annotations used throughout the Android tools libraries.</description>
+ <url>http://tools.android.com/</url>
+ <name>com.android.tools.annotations</name>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+ <developers>
+ <developer>
+ <name>The Android Open Source Project</name>
+ </developer>
+ </developers>
+ <scm>
+ <connection>git://android.googlesource.com/platform/tools/base.git</connection>
+ <url>https://android.googlesource.com/platform/tools/base</url>
+ </scm>
+</project>
\ No newline at end of file
diff --git a/debian/poms/android-tools-common-pom.xml b/debian/poms/android-tools-common-pom.xml
new file mode 100644
index 0000000..e3e6b9e
--- /dev/null
+++ b/debian/poms/android-tools-common-pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.android.tools</groupId>
+ <artifactId>common</artifactId>
+ <version>24.5.0</version>
+ <dependencies>
+ <dependency>
+ <groupId>com.android.tools</groupId>
+ <artifactId>annotations</artifactId>
+ <version>24.5.0</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>17.0</version>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+ <description>common library used by other Android tools libraries.</description>
+ <url>http://tools.android.com/</url>
+ <name>com.android.tools.common</name>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+ <developers>
+ <developer>
+ <name>The Android Open Source Project</name>
+ </developer>
+ </developers>
+ <scm>
+ <connection>git://android.googlesource.com/platform/tools/base.git</connection>
+ <url>https://android.googlesource.com/platform/tools/base</url>
+ </scm>
+</project>
\ No newline at end of file
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..2521765
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,11 @@
+#!/usr/bin/make -f
+
+export JAVA_HOME=/usr/lib/jvm/default-java
+
+%:
+ dh $@ --parallel --with javahelper,maven_repo_helper --buildsystem=gradle
+
+override_dh_auto_build:
+ dh_auto_build -- -x test assemble
+ mv out/build/base/common/build/libs/common-24.5.0.jar com.android.tools.common.jar
+ mv out/build/base/annotations/build/libs/annotations-24.5.0.jar com.android.tools.annotations.jar
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..4bcdd22
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,2 @@
+# This watch file does currently nothing until there is a way to track new
+# upstream releases
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/android-platform-tools-base.git
More information about the pkg-java-commits
mailing list