From 6ba5c97e9ad94b307b3383d9d18c9ef30e3f8322 Mon Sep 17 00:00:00 2001 From: Alex Humesky Date: Mon, 11 Jan 2016 22:46:40 +0000 Subject: [PATCH] Adds ApkBuilder from the Android SDK to bazel's third_party with some modifications to remove deprecation warnings, to create deterministic apks (removes timestamps), and to make calling ApkBuilder more convenient. RELNOTES: Timestamps within Android apks are removed to make apks deterministic. -- MOS_MIGRATED_REVID=111890659 --- src/BUILD | 1 + src/java_tools/singlejar/BUILD | 4 + .../android_sdk_repository_template.txt | 25 +- third_party/README.md | 8 + third_party/java/apkbuilder/BUILD | 20 + third_party/java/apkbuilder/BUILD.tools | 15 + third_party/java/apkbuilder/LICENSE | 189 +++ .../java/com/android/SdkConstants.java | 1359 +++++++++++++++++ .../java/com/android/annotations/NonNull.java | 38 + .../com/android/annotations/Nullable.java | 49 + .../com/android/prefs/AndroidLocation.java | 166 ++ .../com/android/sdklib/build/ApkBuilder.java | 1005 ++++++++++++ .../android/sdklib/build/ApkBuilderMain.java | 264 ++++ .../sdklib/build/ApkCreationException.java | 36 + .../sdklib/build/DuplicateFileException.java | 55 + .../android/sdklib/build/IArchiveBuilder.java | 35 + .../sdklib/build/SealedApkException.java | 36 + .../internal/build/DebugKeyProvider.java | 215 +++ .../sdklib/internal/build/KeystoreHelper.java | 173 +++ .../internal/build/SignedJarBuilder.java | 399 +++++ .../com/android/utils/GrabProcessOutput.java | 193 +++ 21 files changed, 4267 insertions(+), 18 deletions(-) create mode 100644 third_party/java/apkbuilder/BUILD create mode 100644 third_party/java/apkbuilder/BUILD.tools create mode 100644 third_party/java/apkbuilder/LICENSE create mode 100644 third_party/java/apkbuilder/java/com/android/SdkConstants.java create mode 100644 third_party/java/apkbuilder/java/com/android/annotations/NonNull.java create mode 100644 third_party/java/apkbuilder/java/com/android/annotations/Nullable.java create mode 100644 third_party/java/apkbuilder/java/com/android/prefs/AndroidLocation.java create mode 100644 third_party/java/apkbuilder/java/com/android/sdklib/build/ApkBuilder.java create mode 100644 third_party/java/apkbuilder/java/com/android/sdklib/build/ApkBuilderMain.java create mode 100644 third_party/java/apkbuilder/java/com/android/sdklib/build/ApkCreationException.java create mode 100644 third_party/java/apkbuilder/java/com/android/sdklib/build/DuplicateFileException.java create mode 100644 third_party/java/apkbuilder/java/com/android/sdklib/build/IArchiveBuilder.java create mode 100644 third_party/java/apkbuilder/java/com/android/sdklib/build/SealedApkException.java create mode 100644 third_party/java/apkbuilder/java/com/android/sdklib/internal/build/DebugKeyProvider.java create mode 100644 third_party/java/apkbuilder/java/com/android/sdklib/internal/build/KeystoreHelper.java create mode 100644 third_party/java/apkbuilder/java/com/android/sdklib/internal/build/SignedJarBuilder.java create mode 100644 third_party/java/apkbuilder/java/com/android/utils/GrabProcessOutput.java diff --git a/src/BUILD b/src/BUILD index 2612be24cb7aa7..a1dac3d0595f77 100644 --- a/src/BUILD +++ b/src/BUILD @@ -103,6 +103,7 @@ genrule( "//src/java_tools/singlejar:SingleJar_deploy.jar", "//src/java_tools/buildjar/java/com/google/devtools/build/buildjar/genclass:GenClass_deploy.jar", "//third_party/ijar", + "//third_party/java/apkbuilder:embedded_tools", ] + select({ ":darwin": [":darwin_tools"], ":darwin_x86_64": [":darwin_tools"], diff --git a/src/java_tools/singlejar/BUILD b/src/java_tools/singlejar/BUILD index 215f475e33d6b3..60a28b65dbd079 100644 --- a/src/java_tools/singlejar/BUILD +++ b/src/java_tools/singlejar/BUILD @@ -9,6 +9,10 @@ filegroup( java_library( name = "libSingleJar", srcs = glob(["java/**/singlejar/**/*.java"]), + visibility = [ + "//src:__subpackages__", + "//third_party/java/apkbuilder:__pkg__", + ], deps = [ "//src/java_tools/singlejar/java/com/google/devtools/build/zip", "//src/main/java/com/google/devtools/build/lib:shell", diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt index 22f5477bb52014..df3d27b3ffe7b0 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt @@ -117,7 +117,7 @@ android_sdk( shrinked_android_jar = "platforms/android-%api_level%/android.jar", annotations_jar = "tools/support/annotations.jar", main_dex_classes = "build-tools/%build_tools_version%/mainDexClasses.rules", - apkbuilder = ":apkbuilder_binary", + apkbuilder = ":apkbuilder_wrapper", zipalign = ":zipalign_binary", android_jack = ":empty", jack = ":fail", @@ -223,14 +223,12 @@ java_import( ) genrule( - name = "apkbuilder_source", + name = "gen_apkbuilder_wrapper_source", srcs = [], - outs = ["apkbuilder_binary.sh"], + outs = ["apkbuilder_wrapper.sh"], cmd = "\n".join(["cat > $@ << 'EOF'", "#!/bin/bash -eu", - "BINARY=$${0##*-out/host/bin/}", - "DIRNAME=$$(dirname $$BINARY)", - "APKBUILDER=$${0}.runfiles/$${DIRNAME}/apkbuilder", + "APKBUILDER=$${0}.runfiles/external/bazel_tools/third_party/java/apkbuilder/embedded_apkbuilder", "if [[ -n \"$${KEYSTORE-}\" ]]; then", " mkdir -p /tmp/keystore/.android", " cp $$KEYSTORE /tmp/keystore/.android/debug.keystore", @@ -241,20 +239,11 @@ genrule( ) sh_binary( - name = "apkbuilder_binary", - srcs = ["apkbuilder_binary.sh"], - data = [":apkbuilder"], + name = "apkbuilder_wrapper", + srcs = ["apkbuilder_wrapper.sh"], + data = ["@bazel_tools//third_party/java/apkbuilder:embedded_apkbuilder"], ) -java_binary( - name = "apkbuilder", - runtime_deps = [":sdklib"], - main_class = "com.android.sdklib.build.ApkBuilderMain") - -java_import( - name = "sdklib", - jars = ["tools/lib/sdklib.jar", "tools/lib/common.jar", "tools/lib/guava-17.0.jar"]) - GOOGLE_PLAY_SERVICES_DIR = "extras/google/google_play_services/libproject/google-play-services_lib" java_import( diff --git a/third_party/README.md b/third_party/README.md index ed1404ba2dd478..b0a4efc9887219 100644 --- a/third_party/README.md +++ b/third_party/README.md @@ -62,6 +62,14 @@ a minimal set of extra dependencies. * License: Apache License 2.0. +## apkbuilder + +* [apkbuilder](http://search.maven.org/remotecontent?filepath=com/android/tools/sdklib/22.8.4/sdklib-22.8.4-sources.jar) +* [android common](http://search.maven.org/remotecontent?filepath=com/android/tools/sdk-common/22.8.4/sdk-common-22.8.4-sources.jar) +* Version: 22.8.4 +* License: Apache License 2.0. + + ## [asm](https://asm.ow2.org) * Version: 5.0.4 diff --git a/third_party/java/apkbuilder/BUILD b/third_party/java/apkbuilder/BUILD new file mode 100644 index 00000000000000..b7f12483066153 --- /dev/null +++ b/third_party/java/apkbuilder/BUILD @@ -0,0 +1,20 @@ +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "embedded_tools", + srcs = [ + "BUILD.tools", + "apkbuilder_deploy.jar", + ], + visibility = ["//src:__pkg__"], +) + +java_binary( + name = "apkbuilder", + srcs = glob(["java/**/*.java"]), + main_class = "com.android.sdklib.build.ApkBuilderMain", + deps = [ + "//src/java_tools/singlejar:libSingleJar", + "//third_party:guava", + ], +) diff --git a/third_party/java/apkbuilder/BUILD.tools b/third_party/java/apkbuilder/BUILD.tools new file mode 100644 index 00000000000000..580ada40d29a56 --- /dev/null +++ b/third_party/java/apkbuilder/BUILD.tools @@ -0,0 +1,15 @@ +# The build file for apkbuilder when it's in bazel's embedded tools repo. + +licenses(["notice"]) # Apache 2.0 + +java_binary( + name = "embedded_apkbuilder", + main_class = "com.android.sdklib.build.ApkBuilderMain", + runtime_deps = [":apkbuilder_deploy_jar"], + visibility = ["//visibility:public"], +) + +java_import( + name = "apkbuilder_deploy_jar", + jars = [":apkbuilder_deploy.jar"] +) diff --git a/third_party/java/apkbuilder/LICENSE b/third_party/java/apkbuilder/LICENSE new file mode 100644 index 00000000000000..da6a0e722200da --- /dev/null +++ b/third_party/java/apkbuilder/LICENSE @@ -0,0 +1,189 @@ +Copyright (c) 2005-2008, 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. + +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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) 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. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + diff --git a/third_party/java/apkbuilder/java/com/android/SdkConstants.java b/third_party/java/apkbuilder/java/com/android/SdkConstants.java new file mode 100644 index 00000000000000..56b6a14f226d77 --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/SdkConstants.java @@ -0,0 +1,1359 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android; + +import java.io.File; + +/** + * Constant definition class.
+ *
+ * Most constants have a prefix defining the content. + * + */ +@SuppressWarnings("javadoc") // Not documenting all the fields here +public final class SdkConstants { + public static final int PLATFORM_UNKNOWN = 0; + public static final int PLATFORM_LINUX = 1; + public static final int PLATFORM_WINDOWS = 2; + public static final int PLATFORM_DARWIN = 3; + + /** + * Returns current platform, one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, + * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. + */ + public static final int CURRENT_PLATFORM = currentPlatform(); + + /** Environment variable that specifies the path of an Android SDK. */ + public static final String ANDROID_HOME_ENV = "ANDROID_HOME"; + + /** Property in local.properties file that specifies the path of the Android SDK. */ + public static final String SDK_DIR_PROPERTY = "sdk.dir"; + + /** Property in gradle-wrapper.properties file that specifies the URL to the correct Gradle distribution. */ + public static final String GRADLE_DISTRIBUTION_URL_PROPERTY = "distributionUrl"; //$NON-NLS-1$ + + /** + * The encoding we strive to use for all files we write. + *

+ * When possible, use the APIs which take a {@link java.nio.charset.Charset} and pass in + * {@link com.google.common.base.Charsets#UTF_8} instead of using the String encoding + * method. + */ + public static final String UTF_8 = "UTF-8"; //$NON-NLS-1$ + + /** + * Charset for the ini file handled by the SDK. + */ + public static final String INI_CHARSET = UTF_8; + + /** Path separator used by Gradle */ + public static final String GRADLE_PATH_SEPARATOR = ":"; //$NON-NLS-1$ + + /** An SDK Project's AndroidManifest.xml file */ + public static final String FN_ANDROID_MANIFEST_XML= "AndroidManifest.xml"; //$NON-NLS-1$ + /** pre-dex jar filename. i.e. "classes.jar" */ + public static final String FN_CLASSES_JAR = "classes.jar"; //$NON-NLS-1$ + /** Dex filename inside the APK. i.e. "classes.dex" */ + public static final String FN_APK_CLASSES_DEX = "classes.dex"; //$NON-NLS-1$ + + /** An SDK Project's build.xml file */ + public static final String FN_BUILD_XML = "build.xml"; //$NON-NLS-1$ + /** An SDK Project's build.gradle file */ + public static final String FN_BUILD_GRADLE = "build.gradle"; //$NON-NLS-1$ + /** An SDK Project's settings.gradle file */ + public static final String FN_SETTINGS_GRADLE = "settings.gradle"; //$NON-NLS-1$ + /** An SDK Project's gradle.properties file */ + public static final String FN_GRADLE_PROPERTIES = "gradle.properties"; //$NON-NLS-1$ + /** An SDK Project's gradle daemon executable */ + public static final String FN_GRADLE_UNIX = "gradle"; //$NON-NLS-1$ + /** An SDK Project's gradle.bat daemon executable (gradle for windows) */ + public static final String FN_GRADLE_WIN = FN_GRADLE_UNIX + ".bat"; //$NON-NLS-1$ + /** An SDK Project's gradlew file */ + public static final String FN_GRADLE_WRAPPER_UNIX = "gradlew"; //$NON-NLS-1$ + /** An SDK Project's gradlew.bat file (gradlew for windows) */ + public static final String FN_GRADLE_WRAPPER_WIN = FN_GRADLE_WRAPPER_UNIX + ".bat"; //$NON-NLS-1$ + /** An SDK Project's gradle wrapper library */ + public static final String FN_GRADLE_WRAPPER_JAR = "gradle-wrapper.jar"; //$NON-NLS-1$ + /** Name of the framework library, i.e. "android.jar" */ + public static final String FN_FRAMEWORK_LIBRARY = "android.jar"; //$NON-NLS-1$ + /** Name of the framework library, i.e. "uiautomator.jar" */ + public static final String FN_UI_AUTOMATOR_LIBRARY = "uiautomator.jar"; //$NON-NLS-1$ + /** Name of the layout attributes, i.e. "attrs.xml" */ + public static final String FN_ATTRS_XML = "attrs.xml"; //$NON-NLS-1$ + /** Name of the layout attributes, i.e. "attrs_manifest.xml" */ + public static final String FN_ATTRS_MANIFEST_XML = "attrs_manifest.xml"; //$NON-NLS-1$ + /** framework aidl import file */ + public static final String FN_FRAMEWORK_AIDL = "framework.aidl"; //$NON-NLS-1$ + /** framework renderscript folder */ + public static final String FN_FRAMEWORK_RENDERSCRIPT = "renderscript"; //$NON-NLS-1$ + /** framework include folder */ + public static final String FN_FRAMEWORK_INCLUDE = "include"; //$NON-NLS-1$ + /** framework include (clang) folder */ + public static final String FN_FRAMEWORK_INCLUDE_CLANG = "clang-include"; //$NON-NLS-1$ + /** layoutlib.jar file */ + public static final String FN_LAYOUTLIB_JAR = "layoutlib.jar"; //$NON-NLS-1$ + /** widget list file */ + public static final String FN_WIDGETS = "widgets.txt"; //$NON-NLS-1$ + /** Intent activity actions list file */ + public static final String FN_INTENT_ACTIONS_ACTIVITY = "activity_actions.txt"; //$NON-NLS-1$ + /** Intent broadcast actions list file */ + public static final String FN_INTENT_ACTIONS_BROADCAST = "broadcast_actions.txt"; //$NON-NLS-1$ + /** Intent service actions list file */ + public static final String FN_INTENT_ACTIONS_SERVICE = "service_actions.txt"; //$NON-NLS-1$ + /** Intent category list file */ + public static final String FN_INTENT_CATEGORIES = "categories.txt"; //$NON-NLS-1$ + + /** annotations support jar */ + public static final String FN_ANNOTATIONS_JAR = "annotations.jar"; //$NON-NLS-1$ + + /** platform build property file */ + public static final String FN_BUILD_PROP = "build.prop"; //$NON-NLS-1$ + /** plugin properties file */ + public static final String FN_PLUGIN_PROP = "plugin.prop"; //$NON-NLS-1$ + /** add-on manifest file */ + public static final String FN_MANIFEST_INI = "manifest.ini"; //$NON-NLS-1$ + /** add-on layout device XML file. */ + public static final String FN_DEVICES_XML = "devices.xml"; //$NON-NLS-1$ + /** hardware properties definition file */ + public static final String FN_HARDWARE_INI = "hardware-properties.ini"; //$NON-NLS-1$ + + /** project property file */ + public static final String FN_PROJECT_PROPERTIES = "project.properties"; //$NON-NLS-1$ + + /** project local property file */ + public static final String FN_LOCAL_PROPERTIES = "local.properties"; //$NON-NLS-1$ + + /** project ant property file */ + public static final String FN_ANT_PROPERTIES = "ant.properties"; //$NON-NLS-1$ + + /** project local property file */ + public static final String FN_GRADLE_WRAPPER_PROPERTIES = "gradle-wrapper.properties"; //$NON-NLS-1$ + + /** Skin layout file */ + public static final String FN_SKIN_LAYOUT = "layout"; //$NON-NLS-1$ + + /** dx.jar file */ + public static final String FN_DX_JAR = "dx.jar"; //$NON-NLS-1$ + + /** dx executable (with extension for the current OS) */ + public static final String FN_DX = + "dx" + ext(".bat", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** aapt executable (with extension for the current OS) */ + public static final String FN_AAPT = + "aapt" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** aidl executable (with extension for the current OS) */ + public static final String FN_AIDL = + "aidl" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** renderscript executable (with extension for the current OS) */ + public static final String FN_RENDERSCRIPT = + "llvm-rs-cc" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** renderscript support exe (with extension for the current OS) */ + public static final String FN_BCC_COMPAT = + "bcc_compat" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** renderscript support linker for ARM (with extension for the current OS) */ + public static final String FN_LD_ARM = + "arm-linux-androideabi-ld" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** renderscript support linker for X86 (with extension for the current OS) */ + public static final String FN_LD_X86 = + "i686-linux-android-ld" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** renderscript support linker for MIPS (with extension for the current OS) */ + public static final String FN_LD_MIPS = + "mipsel-linux-android-ld" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** adb executable (with extension for the current OS) */ + public static final String FN_ADB = + "adb" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** emulator executable for the current OS */ + public static final String FN_EMULATOR = + "emulator" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** zipalign executable (with extension for the current OS) */ + public static final String FN_ZIPALIGN = + "zipalign" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** dexdump executable (with extension for the current OS) */ + public static final String FN_DEXDUMP = + "dexdump" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** proguard executable (with extension for the current OS) */ + public static final String FN_PROGUARD = + "proguard" + ext(".bat", ".sh"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** find_lock for Windows (with extension for the current OS) */ + public static final String FN_FIND_LOCK = + "find_lock" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** hprof-conv executable (with extension for the current OS) */ + public static final String FN_HPROF_CONV = + "hprof-conv" + ext(".exe", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** properties file for SDK Updater packages */ + public static final String FN_SOURCE_PROP = "source.properties"; //$NON-NLS-1$ + /** properties file for content hash of installed packages */ + public static final String FN_CONTENT_HASH_PROP = "content_hash.properties"; //$NON-NLS-1$ + /** properties file for the SDK */ + public static final String FN_SDK_PROP = "sdk.properties"; //$NON-NLS-1$ + + + public static final String FN_RENDERSCRIPT_V8_JAR = "renderscript-v8.jar"; //$NON-NLS-1$ + + /** + * filename for gdbserver. + */ + public static final String FN_GDBSERVER = "gdbserver"; //$NON-NLS-1$ + public static final String FN_GDB_SETUP = "gdb.setup"; //$NON-NLS-1$ + + /** global Android proguard config file */ + public static final String FN_ANDROID_PROGUARD_FILE = "proguard-android.txt"; //$NON-NLS-1$ + /** global Android proguard config file with optimization enabled */ + public static final String FN_ANDROID_OPT_PROGUARD_FILE = "proguard-android-optimize.txt"; //$NON-NLS-1$ + /** default proguard config file with new file extension (for project specific stuff) */ + public static final String FN_PROJECT_PROGUARD_FILE = "proguard-project.txt"; //$NON-NLS-1$ + + /* Folder Names for Android Projects . */ + + /** Resources folder name, i.e. "res". */ + public static final String FD_RESOURCES = "res"; //$NON-NLS-1$ + /** Assets folder name, i.e. "assets" */ + public static final String FD_ASSETS = "assets"; //$NON-NLS-1$ + /** Default source folder name in an SDK project, i.e. "src". + *

+ * Note: this is not the same as {@link #FD_PKG_SOURCES} + * which is an SDK sources folder for packages. */ + public static final String FD_SOURCES = "src"; //$NON-NLS-1$ + /** Default main source set folder name, i.e. "main" */ + public static final String FD_MAIN = "main"; //$NON-NLS-1$ + /** Default test source set folder name, i.e. "androidTest" */ + public static final String FD_TEST = "androidTest"; //$NON-NLS-1$ + /** Default java code folder name, i.e. "java" */ + public static final String FD_JAVA = "java"; //$NON-NLS-1$ + /** Default gradle folder name, i.e. "gradle" */ + public static final String FD_GRADLE = "gradle"; //$NON-NLS-1$ + /** Default gradle wrapper folder name, i.e. "gradle/wrapper" */ + public static final String FD_GRADLE_WRAPPER = FD_GRADLE + File.separator + "wrapper"; //$NON-NLS-1$ + /** Default generated source folder name, i.e. "gen" */ + public static final String FD_GEN_SOURCES = "gen"; //$NON-NLS-1$ + /** Default native library folder name inside the project, i.e. "libs" + * While the folder inside the .apk is "lib", we call that one libs because + * that's what we use in ant for both .jar and .so and we need to make the 2 development ways + * compatible. */ + public static final String FD_NATIVE_LIBS = "libs"; //$NON-NLS-1$ + /** Native lib folder inside the APK: "lib" */ + public static final String FD_APK_NATIVE_LIBS = "lib"; //$NON-NLS-1$ + /** Default output folder name, i.e. "bin" */ + public static final String FD_OUTPUT = "bin"; //$NON-NLS-1$ + /** Classes output folder name, i.e. "classes" */ + public static final String FD_CLASSES_OUTPUT = "classes"; //$NON-NLS-1$ + /** proguard output folder for mapping, etc.. files */ + public static final String FD_PROGUARD = "proguard"; //$NON-NLS-1$ + /** aidl output folder for copied aidl files */ + public static final String FD_AIDL = "aidl"; //$NON-NLS-1$ + + /** rs Libs output folder for support mode */ + public static final String FD_RS_LIBS = "rsLibs"; //$NON-NLS-1$ + /** rs Libs output folder for support mode */ + public static final String FD_RS_OBJ = "rsObj"; //$NON-NLS-1$ + + /* Folder Names for the Android SDK */ + + /** Name of the SDK platforms folder. */ + public static final String FD_PLATFORMS = "platforms"; //$NON-NLS-1$ + /** Name of the SDK addons folder. */ + public static final String FD_ADDONS = "add-ons"; //$NON-NLS-1$ + /** Name of the SDK system-images folder. */ + public static final String FD_SYSTEM_IMAGES = "system-images"; //$NON-NLS-1$ + /** Name of the SDK sources folder where source packages are installed. + *

+ * Note this is not the same as {@link #FD_SOURCES} which is the folder name where sources + * are installed inside a project. */ + public static final String FD_PKG_SOURCES = "sources"; //$NON-NLS-1$ + /** Name of the SDK tools folder. */ + public static final String FD_TOOLS = "tools"; //$NON-NLS-1$ + /** Name of the SDK tools/support folder. */ + public static final String FD_SUPPORT = "support"; //$NON-NLS-1$ + /** Name of the SDK platform tools folder. */ + public static final String FD_PLATFORM_TOOLS = "platform-tools"; //$NON-NLS-1$ + /** Name of the SDK build tools folder. */ + public static final String FD_BUILD_TOOLS = "build-tools"; //$NON-NLS-1$ + /** Name of the SDK tools/lib folder. */ + public static final String FD_LIB = "lib"; //$NON-NLS-1$ + /** Name of the SDK docs folder. */ + public static final String FD_DOCS = "docs"; //$NON-NLS-1$ + /** Name of the doc folder containing API reference doc (javadoc) */ + public static final String FD_DOCS_REFERENCE = "reference"; //$NON-NLS-1$ + /** Name of the SDK images folder. */ + public static final String FD_IMAGES = "images"; //$NON-NLS-1$ + /** Name of the ABI to support. */ + public static final String ABI_ARMEABI = "armeabi"; //$NON-NLS-1$ + public static final String ABI_ARMEABI_V7A = "armeabi-v7a"; //$NON-NLS-1$ + public static final String ABI_ARM64_V8A = "arm64-v8a"; //$NON-NLS-1$ + public static final String ABI_INTEL_ATOM = "x86"; //$NON-NLS-1$ + public static final String ABI_INTEL_ATOM64 = "x86_64"; //$NON-NLS-1$ + public static final String ABI_MIPS = "mips"; //$NON-NLS-1$ + public static final String ABI_MIPS64 = "mips64"; //$NON-NLS-1$ + /** Name of the CPU arch to support. */ + public static final String CPU_ARCH_ARM = "arm"; //$NON-NLS-1$ + public static final String CPU_ARCH_ARM64 = "arm64"; //$NON-NLS-1$ + public static final String CPU_ARCH_INTEL_ATOM = "x86"; //$NON-NLS-1$ + public static final String CPU_ARCH_INTEL_ATOM64 = "x86_64"; //$NON-NLS-1$ + public static final String CPU_ARCH_MIPS = "mips"; //$NON-NLS-1$ + /** TODO double-check this is appropriate value for mips64 */ + public static final String CPU_ARCH_MIPS64 = "mips64"; //$NON-NLS-1$ + /** Name of the CPU model to support. */ + public static final String CPU_MODEL_CORTEX_A8 = "cortex-a8"; //$NON-NLS-1$ + + /** Name of the SDK skins folder. */ + public static final String FD_SKINS = "skins"; //$NON-NLS-1$ + /** Name of the SDK samples folder. */ + public static final String FD_SAMPLES = "samples"; //$NON-NLS-1$ + /** Name of the SDK extras folder. */ + public static final String FD_EXTRAS = "extras"; //$NON-NLS-1$ + public static final String FD_M2_REPOSITORY = "m2repository"; //$NON-NLS-1$ + /** + * Name of an extra's sample folder. + * Ideally extras should have one {@link #FD_SAMPLES} folder containing + * one or more sub-folders (one per sample). However some older extras + * might contain a single "sample" folder with directly the samples files + * in it. When possible we should encourage extras' owners to move to the + * multi-samples format. + */ + public static final String FD_SAMPLE = "sample"; //$NON-NLS-1$ + /** Name of the SDK templates folder, i.e. "templates" */ + public static final String FD_TEMPLATES = "templates"; //$NON-NLS-1$ + /** Name of the SDK Ant folder, i.e. "ant" */ + public static final String FD_ANT = "ant"; //$NON-NLS-1$ + /** Name of the SDK data folder, i.e. "data" */ + public static final String FD_DATA = "data"; //$NON-NLS-1$ + /** Name of the SDK renderscript folder, i.e. "rs" */ + public static final String FD_RENDERSCRIPT = "rs"; //$NON-NLS-1$ + /** Name of the SDK resources folder, i.e. "res" */ + public static final String FD_RES = "res"; //$NON-NLS-1$ + /** Name of the SDK font folder, i.e. "fonts" */ + public static final String FD_FONTS = "fonts"; //$NON-NLS-1$ + /** Name of the android sources directory and the root of the SDK sources package folder. */ + public static final String FD_ANDROID_SOURCES = "sources"; //$NON-NLS-1$ + /** Name of the addon libs folder. */ + public static final String FD_ADDON_LIBS = "libs"; //$NON-NLS-1$ + + /** Name of the cache folder in the $HOME/.android. */ + public static final String FD_CACHE = "cache"; //$NON-NLS-1$ + + /** API codename of a release (non preview) system image or platform. **/ + public static final String CODENAME_RELEASE = "REL"; //$NON-NLS-1$ + + /** Namespace for the resource XML, i.e. "http://schemas.android.com/apk/res/android" */ + public static final String NS_RESOURCES = + "http://schemas.android.com/apk/res/android"; //$NON-NLS-1$ + + /** + * Namespace pattern for the custom resource XML, i.e. "http://schemas.android.com/apk/res/%s" + *

+ * This string contains a %s. It must be combined with the desired Java package, e.g.: + *

+     *    String.format(SdkConstants.NS_CUSTOM_RESOURCES_S, "android");
+     *    String.format(SdkConstants.NS_CUSTOM_RESOURCES_S, "com.test.mycustomapp");
+     * 
+ * + * Note: if you need an URI specifically for the "android" namespace, consider using + * {@link SdkConstants#NS_RESOURCES} instead. + */ + public final static String NS_CUSTOM_RESOURCES_S = "http://schemas.android.com/apk/res/%1$s"; //$NON-NLS-1$ + + + /** The name of the uses-library that provides "android.test.runner" */ + public static final String ANDROID_TEST_RUNNER_LIB = + "android.test.runner"; //$NON-NLS-1$ + + /* Folder path relative to the SDK root */ + /** Path of the documentation directory relative to the sdk folder. + * This is an OS path, ending with a separator. */ + public static final String OS_SDK_DOCS_FOLDER = FD_DOCS + File.separator; + + /** Path of the tools directory relative to the sdk folder, or to a platform folder. + * This is an OS path, ending with a separator. */ + public static final String OS_SDK_TOOLS_FOLDER = FD_TOOLS + File.separator; + + /** Path of the lib directory relative to the sdk folder, or to a platform folder. + * This is an OS path, ending with a separator. */ + public static final String OS_SDK_TOOLS_LIB_FOLDER = + OS_SDK_TOOLS_FOLDER + FD_LIB + File.separator; + + /** + * Path of the lib directory relative to the sdk folder, or to a platform + * folder. This is an OS path, ending with a separator. + */ + public static final String OS_SDK_TOOLS_LIB_EMULATOR_FOLDER = OS_SDK_TOOLS_LIB_FOLDER + + "emulator" + File.separator; //$NON-NLS-1$ + + /** Path of the platform tools directory relative to the sdk folder. + * This is an OS path, ending with a separator. */ + public static final String OS_SDK_PLATFORM_TOOLS_FOLDER = FD_PLATFORM_TOOLS + File.separator; + + /** Path of the build tools directory relative to the sdk folder. + * This is an OS path, ending with a separator. */ + public static final String OS_SDK_BUILD_TOOLS_FOLDER = FD_BUILD_TOOLS + File.separator; + + /** Path of the Platform tools Lib directory relative to the sdk folder. + * This is an OS path, ending with a separator. */ + public static final String OS_SDK_PLATFORM_TOOLS_LIB_FOLDER = + OS_SDK_PLATFORM_TOOLS_FOLDER + FD_LIB + File.separator; + + /** Path of the bin folder of proguard folder relative to the sdk folder. + * This is an OS path, ending with a separator. */ + public static final String OS_SDK_TOOLS_PROGUARD_BIN_FOLDER = + SdkConstants.OS_SDK_TOOLS_FOLDER + + "proguard" + File.separator + //$NON-NLS-1$ + "bin" + File.separator; //$NON-NLS-1$ + + /** Path of the template gradle wrapper folder relative to the sdk folder. + * This is an OS path, ending with a separator. */ + public static final String OS_SDK_TOOLS_TEMPLATES_GRADLE_WRAPPER_FOLDER = + OS_SDK_TOOLS_FOLDER + FD_TEMPLATES + File.separator + FD_GRADLE_WRAPPER + File.separator; + + /* Folder paths relative to a platform or add-on folder */ + + /** Path of the images directory relative to a platform or addon folder. + * This is an OS path, ending with a separator. */ + public static final String OS_IMAGES_FOLDER = FD_IMAGES + File.separator; + + /** Path of the skin directory relative to a platform or addon folder. + * This is an OS path, ending with a separator. */ + public static final String OS_SKINS_FOLDER = FD_SKINS + File.separator; + + /* Folder paths relative to a Platform folder */ + + /** Path of the data directory relative to a platform folder. + * This is an OS path, ending with a separator. */ + public static final String OS_PLATFORM_DATA_FOLDER = FD_DATA + File.separator; + + /** Path of the renderscript directory relative to a platform folder. + * This is an OS path, ending with a separator. */ + public static final String OS_PLATFORM_RENDERSCRIPT_FOLDER = FD_RENDERSCRIPT + File.separator; + + + /** Path of the samples directory relative to a platform folder. + * This is an OS path, ending with a separator. */ + public static final String OS_PLATFORM_SAMPLES_FOLDER = FD_SAMPLES + File.separator; + + /** Path of the resources directory relative to a platform folder. + * This is an OS path, ending with a separator. */ + public static final String OS_PLATFORM_RESOURCES_FOLDER = + OS_PLATFORM_DATA_FOLDER + FD_RES + File.separator; + + /** Path of the fonts directory relative to a platform folder. + * This is an OS path, ending with a separator. */ + public static final String OS_PLATFORM_FONTS_FOLDER = + OS_PLATFORM_DATA_FOLDER + FD_FONTS + File.separator; + + /** Path of the android source directory relative to a platform folder. + * This is an OS path, ending with a separator. */ + public static final String OS_PLATFORM_SOURCES_FOLDER = FD_ANDROID_SOURCES + File.separator; + + /** Path of the android templates directory relative to a platform folder. + * This is an OS path, ending with a separator. */ + public static final String OS_PLATFORM_TEMPLATES_FOLDER = FD_TEMPLATES + File.separator; + + /** Path of the Ant build rules directory relative to a platform folder. + * This is an OS path, ending with a separator. */ + public static final String OS_PLATFORM_ANT_FOLDER = FD_ANT + File.separator; + + /** Path of the attrs.xml file relative to a platform folder. */ + public static final String OS_PLATFORM_ATTRS_XML = + OS_PLATFORM_RESOURCES_FOLDER + SdkConstants.FD_RES_VALUES + File.separator + + FN_ATTRS_XML; + + /** Path of the attrs_manifest.xml file relative to a platform folder. */ + public static final String OS_PLATFORM_ATTRS_MANIFEST_XML = + OS_PLATFORM_RESOURCES_FOLDER + SdkConstants.FD_RES_VALUES + File.separator + + FN_ATTRS_MANIFEST_XML; + + /** Path of the layoutlib.jar file relative to a platform folder. */ + public static final String OS_PLATFORM_LAYOUTLIB_JAR = + OS_PLATFORM_DATA_FOLDER + FN_LAYOUTLIB_JAR; + + /** Path of the renderscript include folder relative to a platform folder. */ + public static final String OS_FRAMEWORK_RS = + FN_FRAMEWORK_RENDERSCRIPT + File.separator + FN_FRAMEWORK_INCLUDE; + /** Path of the renderscript (clang) include folder relative to a platform folder. */ + public static final String OS_FRAMEWORK_RS_CLANG = + FN_FRAMEWORK_RENDERSCRIPT + File.separator + FN_FRAMEWORK_INCLUDE_CLANG; + + /* Folder paths relative to a addon folder */ + /** Path of the images directory relative to a folder folder. + * This is an OS path, ending with a separator. */ + public static final String OS_ADDON_LIBS_FOLDER = FD_ADDON_LIBS + File.separator; + + /** Skin default **/ + public static final String SKIN_DEFAULT = "default"; //$NON-NLS-1$ + + /** SDK property: ant templates revision */ + public static final String PROP_SDK_ANT_TEMPLATES_REVISION = + "sdk.ant.templates.revision"; //$NON-NLS-1$ + + /** SDK property: default skin */ + public static final String PROP_SDK_DEFAULT_SKIN = "sdk.skin.default"; //$NON-NLS-1$ + + /* Android Class Constants */ + public static final String CLASS_ACTIVITY = "android.app.Activity"; //$NON-NLS-1$ + public static final String CLASS_APPLICATION = "android.app.Application"; //$NON-NLS-1$ + public static final String CLASS_SERVICE = "android.app.Service"; //$NON-NLS-1$ + public static final String CLASS_BROADCASTRECEIVER = "android.content.BroadcastReceiver"; //$NON-NLS-1$ + public static final String CLASS_CONTENTPROVIDER = "android.content.ContentProvider"; //$NON-NLS-1$ + public static final String CLASS_INSTRUMENTATION = "android.app.Instrumentation"; //$NON-NLS-1$ + public static final String CLASS_INSTRUMENTATION_RUNNER = + "android.test.InstrumentationTestRunner"; //$NON-NLS-1$ + public static final String CLASS_BUNDLE = "android.os.Bundle"; //$NON-NLS-1$ + public static final String CLASS_R = "android.R"; //$NON-NLS-1$ + public static final String CLASS_R_PREFIX = CLASS_R + "."; //$NON-NLS-1$ + public static final String CLASS_MANIFEST_PERMISSION = "android.Manifest$permission"; //$NON-NLS-1$ + public static final String CLASS_INTENT = "android.content.Intent"; //$NON-NLS-1$ + public static final String CLASS_CONTEXT = "android.content.Context"; //$NON-NLS-1$ + public static final String CLASS_VIEW = "android.view.View"; //$NON-NLS-1$ + public static final String CLASS_VIEWGROUP = "android.view.ViewGroup"; //$NON-NLS-1$ + public static final String CLASS_NAME_LAYOUTPARAMS = "LayoutParams"; //$NON-NLS-1$ + public static final String CLASS_VIEWGROUP_LAYOUTPARAMS = + CLASS_VIEWGROUP + "$" + CLASS_NAME_LAYOUTPARAMS; //$NON-NLS-1$ + public static final String CLASS_NAME_FRAMELAYOUT = "FrameLayout"; //$NON-NLS-1$ + public static final String CLASS_FRAMELAYOUT = + "android.widget." + CLASS_NAME_FRAMELAYOUT; //$NON-NLS-1$ + public static final String CLASS_PREFERENCE = "android.preference.Preference"; //$NON-NLS-1$ + public static final String CLASS_NAME_PREFERENCE_SCREEN = "PreferenceScreen"; //$NON-NLS-1$ + public static final String CLASS_PREFERENCES = + "android.preference." + CLASS_NAME_PREFERENCE_SCREEN; //$NON-NLS-1$ + public static final String CLASS_PREFERENCEGROUP = "android.preference.PreferenceGroup"; //$NON-NLS-1$ + public static final String CLASS_PARCELABLE = "android.os.Parcelable"; //$NON-NLS-1$ + public static final String CLASS_FRAGMENT = "android.app.Fragment"; //$NON-NLS-1$ + public static final String CLASS_V4_FRAGMENT = "android.support.v4.app.Fragment"; //$NON-NLS-1$ + /** MockView is part of the layoutlib bridge and used to display classes that have + * no rendering in the graphical layout editor. */ + public static final String CLASS_MOCK_VIEW = "com.android.layoutlib.bridge.MockView"; //$NON-NLS-1$ + + /** Returns the appropriate name for the 'android' command, which is 'android.exe' for + * Windows and 'android' for all other platforms. */ + public static String androidCmdName() { + String os = System.getProperty("os.name"); //$NON-NLS-1$ + String cmd = "android"; //$NON-NLS-1$ + if (os.startsWith("Windows")) { //$NON-NLS-1$ + cmd += ".bat"; //$NON-NLS-1$ + } + return cmd; + } + + /** Returns the appropriate name for the 'mksdcard' command, which is 'mksdcard.exe' for + * Windows and 'mkdsdcard' for all other platforms. */ + public static String mkSdCardCmdName() { + String os = System.getProperty("os.name"); //$NON-NLS-1$ + String cmd = "mksdcard"; //$NON-NLS-1$ + if (os.startsWith("Windows")) { //$NON-NLS-1$ + cmd += ".exe"; //$NON-NLS-1$ + } + return cmd; + } + + /** + * Returns current platform + * + * @return one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, + * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. + */ + public static int currentPlatform() { + String os = System.getProperty("os.name"); //$NON-NLS-1$ + if (os.startsWith("Mac OS")) { //$NON-NLS-1$ + return PLATFORM_DARWIN; + } else if (os.startsWith("Windows")) { //$NON-NLS-1$ + return PLATFORM_WINDOWS; + } else if (os.startsWith("Linux")) { //$NON-NLS-1$ + return PLATFORM_LINUX; + } + + return PLATFORM_UNKNOWN; + } + + /** + * Returns current platform's UI name + * + * @return one of "Windows", "Mac OS X", "Linux" or "other". + */ + public static String currentPlatformName() { + String os = System.getProperty("os.name"); //$NON-NLS-1$ + if (os.startsWith("Mac OS")) { //$NON-NLS-1$ + return "Mac OS X"; //$NON-NLS-1$ + } else if (os.startsWith("Windows")) { //$NON-NLS-1$ + return "Windows"; //$NON-NLS-1$ + } else if (os.startsWith("Linux")) { //$NON-NLS-1$ + return "Linux"; //$NON-NLS-1$ + } + + return "Other"; + } + + private static String ext(String windowsExtension, String nonWindowsExtension) { + if (CURRENT_PLATFORM == PLATFORM_WINDOWS) { + return windowsExtension; + } else { + return nonWindowsExtension; + } + } + + /** Default anim resource folder name, i.e. "anim" */ + public static final String FD_RES_ANIM = "anim"; //$NON-NLS-1$ + /** Default animator resource folder name, i.e. "animator" */ + public static final String FD_RES_ANIMATOR = "animator"; //$NON-NLS-1$ + /** Default color resource folder name, i.e. "color" */ + public static final String FD_RES_COLOR = "color"; //$NON-NLS-1$ + /** Default drawable resource folder name, i.e. "drawable" */ + public static final String FD_RES_DRAWABLE = "drawable"; //$NON-NLS-1$ + /** Default interpolator resource folder name, i.e. "interpolator" */ + public static final String FD_RES_INTERPOLATOR = "interpolator"; //$NON-NLS-1$ + /** Default layout resource folder name, i.e. "layout" */ + public static final String FD_RES_LAYOUT = "layout"; //$NON-NLS-1$ + /** Default menu resource folder name, i.e. "menu" */ + public static final String FD_RES_MENU = "menu"; //$NON-NLS-1$ + /** Default menu resource folder name, i.e. "mipmap" */ + public static final String FD_RES_MIPMAP = "mipmap"; //$NON-NLS-1$ + /** Default values resource folder name, i.e. "values" */ + public static final String FD_RES_VALUES = "values"; //$NON-NLS-1$ + /** Default xml resource folder name, i.e. "xml" */ + public static final String FD_RES_XML = "xml"; //$NON-NLS-1$ + /** Default raw resource folder name, i.e. "raw" */ + public static final String FD_RES_RAW = "raw"; //$NON-NLS-1$ + /** Separator between the resource folder qualifier. */ + public static final String RES_QUALIFIER_SEP = "-"; //$NON-NLS-1$ + /** Namespace used in XML files for Android attributes */ + + // ---- XML ---- + + /** URI of the reserved "xmlns" prefix */ + public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; //$NON-NLS-1$ + /** The "xmlns" attribute name */ + public static final String XMLNS = "xmlns"; //$NON-NLS-1$ + /** The default prefix used for the {@link #XMLNS_URI} */ + public static final String XMLNS_PREFIX = "xmlns:"; //$NON-NLS-1$ + /** Qualified name of the xmlns android declaration element */ + public static final String XMLNS_ANDROID = "xmlns:android"; //$NON-NLS-1$ + /** The default prefix used for the {@link #ANDROID_URI} name space */ + public static final String ANDROID_NS_NAME = "android"; //$NON-NLS-1$ + /** The default prefix used for the {@link #ANDROID_URI} name space including the colon */ + public static final String ANDROID_NS_NAME_PREFIX = "android:"; //$NON-NLS-1$ + public static final int ANDROID_NS_NAME_PREFIX_LEN = ANDROID_NS_NAME_PREFIX.length(); + + /** The default prefix used for the app */ + public static final String APP_PREFIX = "app"; //$NON-NLS-1$ + /** The entity for the ampersand character */ + public static final String AMP_ENTITY = "&"; //$NON-NLS-1$ + /** The entity for the quote character */ + public static final String QUOT_ENTITY = """; //$NON-NLS-1$ + /** The entity for the apostrophe character */ + public static final String APOS_ENTITY = "'"; //$NON-NLS-1$ + /** The entity for the less than character */ + public static final String LT_ENTITY = "<"; //$NON-NLS-1$ + /** The entity for the greater than character */ + public static final String GT_ENTITY = ">"; //$NON-NLS-1$ + + // ---- Elements and Attributes ---- + + /** Namespace prefix used for all resources */ + public static final String URI_PREFIX = + "http://schemas.android.com/apk/res/"; //$NON-NLS-1$ + /** Namespace used in XML files for Android attributes */ + public static final String ANDROID_URI = + "http://schemas.android.com/apk/res/android"; //$NON-NLS-1$ + /** Namespace used in XML files for Android Tooling attributes */ + public static final String TOOLS_URI = + "http://schemas.android.com/tools"; //$NON-NLS-1$ + /** Namespace used for auto-adjusting namespaces */ + public static final String AUTO_URI = + "http://schemas.android.com/apk/res-auto"; //$NON-NLS-1$ + /** Default prefix used for tools attributes */ + public static final String TOOLS_PREFIX = "tools"; //$NON-NLS-1$ + public static final String R_CLASS = "R"; //$NON-NLS-1$ + public static final String ANDROID_PKG = "android"; //$NON-NLS-1$ + + // Tags: Manifest + public static final String TAG_SERVICE = "service"; //$NON-NLS-1$ + public static final String TAG_PERMISSION = "permission"; //$NON-NLS-1$ + public static final String TAG_USES_FEATURE = "uses-feature"; //$NON-NLS-1$ + public static final String TAG_USES_PERMISSION = "uses-permission";//$NON-NLS-1$ + public static final String TAG_USES_LIBRARY = "uses-library"; //$NON-NLS-1$ + public static final String TAG_APPLICATION = "application"; //$NON-NLS-1$ + public static final String TAG_INTENT_FILTER = "intent-filter"; //$NON-NLS-1$ + public static final String TAG_USES_SDK = "uses-sdk"; //$NON-NLS-1$ + public static final String TAG_ACTIVITY = "activity"; //$NON-NLS-1$ + public static final String TAG_RECEIVER = "receiver"; //$NON-NLS-1$ + public static final String TAG_PROVIDER = "provider"; //$NON-NLS-1$ + public static final String TAG_GRANT_PERMISSION = "grant-uri-permission"; //$NON-NLS-1$ + public static final String TAG_PATH_PERMISSION = "path-permission"; //$NON-NLS-1$ + + // Tags: Resources + public static final String TAG_RESOURCES = "resources"; //$NON-NLS-1$ + public static final String TAG_STRING = "string"; //$NON-NLS-1$ + public static final String TAG_ARRAY = "array"; //$NON-NLS-1$ + public static final String TAG_STYLE = "style"; //$NON-NLS-1$ + public static final String TAG_ITEM = "item"; //$NON-NLS-1$ + public static final String TAG_GROUP = "group"; //$NON-NLS-1$ + public static final String TAG_STRING_ARRAY = "string-array"; //$NON-NLS-1$ + public static final String TAG_PLURALS = "plurals"; //$NON-NLS-1$ + public static final String TAG_INTEGER_ARRAY = "integer-array"; //$NON-NLS-1$ + public static final String TAG_COLOR = "color"; //$NON-NLS-1$ + public static final String TAG_DIMEN = "dimen"; //$NON-NLS-1$ + public static final String TAG_DRAWABLE = "drawable"; //$NON-NLS-1$ + public static final String TAG_MENU = "menu"; //$NON-NLS-1$ + public static final String TAG_ENUM = "enum"; //$NON-NLS-1$ + public static final String TAG_FLAG = "flag"; //$NON-NLS-1$ + public static final String TAG_ATTR = "attr"; //$NON-NLS-1$ + public static final String TAG_DECLARE_STYLEABLE = "declare-styleable"; //$NON-NLS-1$ + public static final String TAG_EAT_COMMENT = "eat-comment"; //$NON-NLS-1$ + public static final String TAG_SKIP = "skip"; //$NON-NLS-1$ + + // Tags: XML + public static final String TAG_HEADER = "header"; //$NON-NLS-1$ + + // Tags: Layouts + public static final String VIEW_TAG = "view"; //$NON-NLS-1$ + public static final String VIEW_INCLUDE = "include"; //$NON-NLS-1$ + public static final String VIEW_MERGE = "merge"; //$NON-NLS-1$ + public static final String VIEW_FRAGMENT = "fragment"; //$NON-NLS-1$ + public static final String REQUEST_FOCUS = "requestFocus"; //$NON-NLS-1$ + public static final String TAG = "tag"; //$NON-NLS-1$ + + public static final String VIEW = "View"; //$NON-NLS-1$ + public static final String VIEW_GROUP = "ViewGroup"; //$NON-NLS-1$ + public static final String FRAME_LAYOUT = "FrameLayout"; //$NON-NLS-1$ + public static final String LINEAR_LAYOUT = "LinearLayout"; //$NON-NLS-1$ + public static final String RELATIVE_LAYOUT = "RelativeLayout"; //$NON-NLS-1$ + public static final String GRID_LAYOUT = "GridLayout"; //$NON-NLS-1$ + public static final String SCROLL_VIEW = "ScrollView"; //$NON-NLS-1$ + public static final String BUTTON = "Button"; //$NON-NLS-1$ + public static final String COMPOUND_BUTTON = "CompoundButton"; //$NON-NLS-1$ + public static final String ADAPTER_VIEW = "AdapterView"; //$NON-NLS-1$ + public static final String GALLERY = "Gallery"; //$NON-NLS-1$ + public static final String GRID_VIEW = "GridView"; //$NON-NLS-1$ + public static final String TAB_HOST = "TabHost"; //$NON-NLS-1$ + public static final String RADIO_GROUP = "RadioGroup"; //$NON-NLS-1$ + public static final String RADIO_BUTTON = "RadioButton"; //$NON-NLS-1$ + public static final String SWITCH = "Switch"; //$NON-NLS-1$ + public static final String EDIT_TEXT = "EditText"; //$NON-NLS-1$ + public static final String LIST_VIEW = "ListView"; //$NON-NLS-1$ + public static final String TEXT_VIEW = "TextView"; //$NON-NLS-1$ + public static final String CHECKED_TEXT_VIEW = "CheckedTextView"; //$NON-NLS-1$ + public static final String IMAGE_VIEW = "ImageView"; //$NON-NLS-1$ + public static final String SURFACE_VIEW = "SurfaceView"; //$NON-NLS-1$ + public static final String ABSOLUTE_LAYOUT = "AbsoluteLayout"; //$NON-NLS-1$ + public static final String TABLE_LAYOUT = "TableLayout"; //$NON-NLS-1$ + public static final String TABLE_ROW = "TableRow"; //$NON-NLS-1$ + public static final String TAB_WIDGET = "TabWidget"; //$NON-NLS-1$ + public static final String IMAGE_BUTTON = "ImageButton"; //$NON-NLS-1$ + public static final String SEEK_BAR = "SeekBar"; //$NON-NLS-1$ + public static final String VIEW_STUB = "ViewStub"; //$NON-NLS-1$ + public static final String SPINNER = "Spinner"; //$NON-NLS-1$ + public static final String WEB_VIEW = "WebView"; //$NON-NLS-1$ + public static final String TOGGLE_BUTTON = "ToggleButton"; //$NON-NLS-1$ + public static final String CHECK_BOX = "CheckBox"; //$NON-NLS-1$ + public static final String ABS_LIST_VIEW = "AbsListView"; //$NON-NLS-1$ + public static final String PROGRESS_BAR = "ProgressBar"; //$NON-NLS-1$ + public static final String ABS_SPINNER = "AbsSpinner"; //$NON-NLS-1$ + public static final String ABS_SEEK_BAR = "AbsSeekBar"; //$NON-NLS-1$ + public static final String VIEW_ANIMATOR = "ViewAnimator"; //$NON-NLS-1$ + public static final String VIEW_SWITCHER = "ViewSwitcher"; //$NON-NLS-1$ + public static final String EXPANDABLE_LIST_VIEW = "ExpandableListView"; //$NON-NLS-1$ + public static final String HORIZONTAL_SCROLL_VIEW = "HorizontalScrollView"; //$NON-NLS-1$ + public static final String MULTI_AUTO_COMPLETE_TEXT_VIEW = "MultiAutoCompleteTextView"; //$NON-NLS-1$ + public static final String AUTO_COMPLETE_TEXT_VIEW = "AutoCompleteTextView"; //$NON-NLS-1$ + public static final String CHECKABLE = "Checkable"; //$NON-NLS-1$ + + // Tags: Drawables + public static final String TAG_BITMAP = "bitmap"; //$NON-NLS-1$ + + // Attributes: Manifest + public static final String ATTR_EXPORTED = "exported"; //$NON-NLS-1$ + public static final String ATTR_PERMISSION = "permission"; //$NON-NLS-1$ + public static final String ATTR_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-1$ + public static final String ATTR_TARGET_SDK_VERSION = "targetSdkVersion"; //$NON-NLS-1$ + public static final String ATTR_ICON = "icon"; //$NON-NLS-1$ + public static final String ATTR_PACKAGE = "package"; //$NON-NLS-1$ + public static final String ATTR_CORE_APP = "coreApp"; //$NON-NLS-1$ + public static final String ATTR_THEME = "theme"; //$NON-NLS-1$ + public static final String ATTR_PATH = "path"; //$NON-NLS-1$ + public static final String ATTR_PATH_PREFIX = "pathPrefix"; //$NON-NLS-1$ + public static final String ATTR_PATH_PATTERN = "pathPattern"; //$NON-NLS-1$ + public static final String ATTR_ALLOW_BACKUP = "allowBackup"; //$NON_NLS-1$ + public static final String ATTR_DEBUGGABLE = "debuggable"; //$NON-NLS-1$ + public static final String ATTR_READ_PERMISSION = "readPermission"; //$NON_NLS-1$ + public static final String ATTR_WRITE_PERMISSION = "writePermission"; //$NON_NLS-1$ + public static final String ATTR_VERSION_CODE = "versionCode"; //$NON_NLS-1$ + public static final String ATTR_VERSION_NAME = "versionName"; //$NON_NLS-1$ + + // Attributes: Resources + public static final String ATTR_NAME = "name"; //$NON-NLS-1$ + public static final String ATTR_FRAGMENT = "fragment"; //$NON-NLS-1$ + public static final String ATTR_TYPE = "type"; //$NON-NLS-1$ + public static final String ATTR_PARENT = "parent"; //$NON-NLS-1$ + public static final String ATTR_TRANSLATABLE = "translatable"; //$NON-NLS-1$ + public static final String ATTR_COLOR = "color"; //$NON-NLS-1$ + public static final String ATTR_DRAWABLE = "drawable"; //$NON-NLS-1$ + public static final String ATTR_VALUE = "value"; //$NON-NLS-1$ + public static final String ATTR_QUANTITY = "quantity"; //$NON-NLS-1$ + public static final String ATTR_FORMAT = "format"; //$NON-NLS-1$ + + // Attributes: Layout + public static final String ATTR_LAYOUT_RESOURCE_PREFIX = "layout_";//$NON-NLS-1$ + public static final String ATTR_CLASS = "class"; //$NON-NLS-1$ + public static final String ATTR_STYLE = "style"; //$NON-NLS-1$ + public static final String ATTR_CONTEXT = "context"; //$NON-NLS-1$ + public static final String ATTR_ID = "id"; //$NON-NLS-1$ + public static final String ATTR_TEXT = "text"; //$NON-NLS-1$ + public static final String ATTR_TEXT_SIZE = "textSize"; //$NON-NLS-1$ + public static final String ATTR_LABEL = "label"; //$NON-NLS-1$ + public static final String ATTR_HINT = "hint"; //$NON-NLS-1$ + public static final String ATTR_PROMPT = "prompt"; //$NON-NLS-1$ + public static final String ATTR_ON_CLICK = "onClick"; //$NON-NLS-1$ + public static final String ATTR_INPUT_TYPE = "inputType"; //$NON-NLS-1$ + public static final String ATTR_INPUT_METHOD = "inputMethod"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_GRAVITY = "layout_gravity"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_WIDTH = "layout_width"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_HEIGHT = "layout_height"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_WEIGHT = "layout_weight"; //$NON-NLS-1$ + public static final String ATTR_PADDING = "padding"; //$NON-NLS-1$ + public static final String ATTR_PADDING_BOTTOM = "paddingBottom"; //$NON-NLS-1$ + public static final String ATTR_PADDING_TOP = "paddingTop"; //$NON-NLS-1$ + public static final String ATTR_PADDING_RIGHT = "paddingRight"; //$NON-NLS-1$ + public static final String ATTR_PADDING_LEFT = "paddingLeft"; //$NON-NLS-1$ + public static final String ATTR_PADDING_START = "paddingStart"; //$NON-NLS-1$ + public static final String ATTR_PADDING_END = "paddingEnd"; //$NON-NLS-1$ + public static final String ATTR_FOREGROUND = "foreground"; //$NON-NLS-1$ + public static final String ATTR_BACKGROUND = "background"; //$NON-NLS-1$ + public static final String ATTR_ORIENTATION = "orientation"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT = "layout"; //$NON-NLS-1$ + public static final String ATTR_ROW_COUNT = "rowCount"; //$NON-NLS-1$ + public static final String ATTR_COLUMN_COUNT = "columnCount"; //$NON-NLS-1$ + public static final String ATTR_LABEL_FOR = "labelFor"; //$NON-NLS-1$ + public static final String ATTR_BASELINE_ALIGNED = "baselineAligned"; //$NON-NLS-1$ + public static final String ATTR_CONTENT_DESCRIPTION = "contentDescription"; //$NON-NLS-1$ + public static final String ATTR_IME_ACTION_LABEL = "imeActionLabel"; //$NON-NLS-1$ + public static final String ATTR_PRIVATE_IME_OPTIONS = "privateImeOptions"; //$NON-NLS-1$ + public static final String VALUE_NONE = "none"; //$NON-NLS-1$ + public static final String VALUE_NO = "no"; //$NON-NLS-1$ + public static final String ATTR_NUMERIC = "numeric"; //$NON-NLS-1$ + public static final String ATTR_IME_ACTION_ID = "imeActionId"; //$NON-NLS-1$ + public static final String ATTR_IME_OPTIONS = "imeOptions"; //$NON-NLS-1$ + public static final String ATTR_FREEZES_TEXT = "freezesText"; //$NON-NLS-1$ + public static final String ATTR_EDITOR_EXTRAS = "editorExtras"; //$NON-NLS-1$ + public static final String ATTR_EDITABLE = "editable"; //$NON-NLS-1$ + public static final String ATTR_DIGITS = "digits"; //$NON-NLS-1$ + public static final String ATTR_CURSOR_VISIBLE = "cursorVisible"; //$NON-NLS-1$ + public static final String ATTR_CAPITALIZE = "capitalize"; //$NON-NLS-1$ + public static final String ATTR_PHONE_NUMBER = "phoneNumber"; //$NON-NLS-1$ + public static final String ATTR_PASSWORD = "password"; //$NON-NLS-1$ + public static final String ATTR_BUFFER_TYPE = "bufferType"; //$NON-NLS-1$ + public static final String ATTR_AUTO_TEXT = "autoText"; //$NON-NLS-1$ + public static final String ATTR_ENABLED = "enabled"; //$NON-NLS-1$ + public static final String ATTR_SINGLE_LINE = "singleLine"; //$NON-NLS-1$ + public static final String ATTR_SCALE_TYPE = "scaleType"; //$NON-NLS-1$ + public static final String ATTR_VISIBILITY = "visibility"; //$NON-NLS-1$ + public static final String ATTR_TEXT_IS_SELECTABLE = + "textIsSelectable"; //$NON-NLS-1$ + public static final String ATTR_IMPORTANT_FOR_ACCESSIBILITY = + "importantForAccessibility"; //$NON-NLS-1$ + public static final String ATTR_LIST_PREFERRED_ITEM_PADDING_LEFT = + "listPreferredItemPaddingLeft"; //$NON-NLS-1$ + public static final String ATTR_LIST_PREFERRED_ITEM_PADDING_RIGHT = + "listPreferredItemPaddingRight"; //$NON-NLS-1$ + public static final String ATTR_LIST_PREFERRED_ITEM_PADDING_START = + "listPreferredItemPaddingStart"; //$NON-NLS-1$ + public static final String ATTR_LIST_PREFERRED_ITEM_PADDING_END = + "listPreferredItemPaddingEnd"; //$NON-NLS-1$ + public static final String ATTR_INDEX = "index"; //$NON-NLS-1$ + + // AbsoluteLayout layout params + public static final String ATTR_LAYOUT_Y = "layout_y"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_X = "layout_x"; //$NON-NLS-1$ + + // GridLayout layout params + public static final String ATTR_LAYOUT_ROW = "layout_row"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ROW_SPAN = "layout_rowSpan";//$NON-NLS-1$ + public static final String ATTR_LAYOUT_COLUMN = "layout_column"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_COLUMN_SPAN = "layout_columnSpan"; //$NON-NLS-1$ + + // TableRow + public static final String ATTR_LAYOUT_SPAN = "layout_span"; //$NON-NLS-1$ + + // RelativeLayout layout params: + public static final String ATTR_LAYOUT_ALIGN_LEFT = "layout_alignLeft"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_RIGHT = "layout_alignRight"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_START = "layout_alignStart"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_END = "layout_alignEnd"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_TOP = "layout_alignTop"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_BOTTOM = "layout_alignBottom"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_PARENT_LEFT = "layout_alignParentLeft"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_PARENT_RIGHT = "layout_alignParentRight"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_PARENT_START = "layout_alignParentStart"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_PARENT_END = "layout_alignParentEnd"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_PARENT_TOP = "layout_alignParentTop"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_PARENT_BOTTOM = "layout_alignParentBottom"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING = "layout_alignWithParentIfMissing"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_BASELINE = "layout_alignBaseline"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_CENTER_IN_PARENT = "layout_centerInParent"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_CENTER_VERTICAL = "layout_centerVertical"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_CENTER_HORIZONTAL = "layout_centerHorizontal"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_TO_RIGHT_OF = "layout_toRightOf"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_TO_LEFT_OF = "layout_toLeftOf"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_TO_START_OF = "layout_toStartOf"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_TO_END_OF = "layout_toEndOf"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_BELOW = "layout_below"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ABOVE = "layout_above"; //$NON-NLS-1$ + + // Margins + public static final String ATTR_LAYOUT_MARGIN = "layout_margin"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_MARGIN_LEFT = "layout_marginLeft"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_MARGIN_RIGHT = "layout_marginRight"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_MARGIN_START = "layout_marginStart"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_MARGIN_END = "layout_marginEnd"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_MARGIN_TOP = "layout_marginTop"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_MARGIN_BOTTOM = "layout_marginBottom"; //$NON-NLS-1$ + + // Attributes: Drawables + public static final String ATTR_TILE_MODE = "tileMode"; //$NON-NLS-1$ + + // Values: Manifest + public static final String VALUE_SPLIT_ACTION_BAR_WHEN_NARROW = "splitActionBarWhenNarrow"; // NON-NLS-$1 + + // Values: Layouts + public static final String VALUE_FILL_PARENT = "fill_parent"; //$NON-NLS-1$ + public static final String VALUE_MATCH_PARENT = "match_parent"; //$NON-NLS-1$ + public static final String VALUE_VERTICAL = "vertical"; //$NON-NLS-1$ + public static final String VALUE_TRUE = "true"; //$NON-NLS-1$ + public static final String VALUE_EDITABLE = "editable"; //$NON-NLS-1$ + public static final String VALUE_AUTO_FIT = "auto_fit"; //$NON-NLS-1$ + public static final String VALUE_SELECTABLE_ITEM_BACKGROUND = + "?android:attr/selectableItemBackground"; //$NON-NLS-1$ + + + // Values: Resources + public static final String VALUE_ID = "id"; //$NON-NLS-1$ + + // Values: Drawables + public static final String VALUE_DISABLED = "disabled"; //$NON-NLS-1$ + public static final String VALUE_CLAMP = "clamp"; //$NON-NLS-1$ + + // Menus + public static final String ATTR_SHOW_AS_ACTION = "showAsAction"; //$NON-NLS-1$ + public static final String ATTR_TITLE = "title"; //$NON-NLS-1$ + public static final String ATTR_VISIBLE = "visible"; //$NON-NLS-1$ + public static final String VALUE_IF_ROOM = "ifRoom"; //$NON-NLS-1$ + public static final String VALUE_ALWAYS = "always"; //$NON-NLS-1$ + + // Units + public static final String UNIT_DP = "dp"; //$NON-NLS-1$ + public static final String UNIT_DIP = "dip"; //$NON-NLS-1$ + public static final String UNIT_SP = "sp"; //$NON-NLS-1$ + public static final String UNIT_PX = "px"; //$NON-NLS-1$ + public static final String UNIT_IN = "in"; //$NON-NLS-1$ + public static final String UNIT_MM = "mm"; //$NON-NLS-1$ + public static final String UNIT_PT = "pt"; //$NON-NLS-1$ + + // Filenames and folder names + public static final String ANDROID_MANIFEST_XML = "AndroidManifest.xml"; //$NON-NLS-1$ + public static final String OLD_PROGUARD_FILE = "proguard.cfg"; //$NON-NLS-1$ + public static final String CLASS_FOLDER = + "bin" + File.separator + "classes"; //$NON-NLS-1$ //$NON-NLS-2$ + public static final String GEN_FOLDER = "gen"; //$NON-NLS-1$ + public static final String SRC_FOLDER = "src"; //$NON-NLS-1$ + public static final String LIBS_FOLDER = "libs"; //$NON-NLS-1$ + public static final String BIN_FOLDER = "bin"; //$NON-NLS-1$ + + public static final String RES_FOLDER = "res"; //$NON-NLS-1$ + public static final String DOT_XML = ".xml"; //$NON-NLS-1$ + public static final String DOT_GIF = ".gif"; //$NON-NLS-1$ + public static final String DOT_JPG = ".jpg"; //$NON-NLS-1$ + public static final String DOT_JPEG = ".jpeg"; //$NON-NLS-1$ + public static final String DOT_PNG = ".png"; //$NON-NLS-1$ + public static final String DOT_9PNG = ".9.png"; //$NON-NLS-1$ + public static final String DOT_JAVA = ".java"; //$NON-NLS-1$ + public static final String DOT_CLASS = ".class"; //$NON-NLS-1$ + public static final String DOT_JAR = ".jar"; //$NON-NLS-1$ + public static final String DOT_GRADLE = ".gradle"; //$NON-NLS-1$ + public static final String DOT_PROPERTIES = ".properties"; //$NON-NLS-1$ + + /** Extension of the Application package Files, i.e. "apk". */ + public static final String EXT_ANDROID_PACKAGE = "apk"; //$NON-NLS-1$ + /** Extension of java files, i.e. "java" */ + public static final String EXT_JAVA = "java"; //$NON-NLS-1$ + /** Extension of compiled java files, i.e. "class" */ + public static final String EXT_CLASS = "class"; //$NON-NLS-1$ + /** Extension of xml files, i.e. "xml" */ + public static final String EXT_XML = "xml"; //$NON-NLS-1$ + /** Extension of gradle files, i.e. "gradle" */ + public static final String EXT_GRADLE = "gradle"; //$NON-NLS-1$ + /** Extension of jar files, i.e. "jar" */ + public static final String EXT_JAR = "jar"; //$NON-NLS-1$ + /** Extension of ZIP files, i.e. "zip" */ + public static final String EXT_ZIP = "zip"; //$NON-NLS-1$ + /** Extension of aidl files, i.e. "aidl" */ + public static final String EXT_AIDL = "aidl"; //$NON-NLS-1$ + /** Extension of Renderscript files, i.e. "rs" */ + public static final String EXT_RS = "rs"; //$NON-NLS-1$ + /** Extension of Renderscript files, i.e. "rsh" */ + public static final String EXT_RSH = "rsh"; //$NON-NLS-1$ + /** Extension of FilterScript files, i.e. "fs" */ + public static final String EXT_FS = "fs"; //$NON-NLS-1$ + /** Extension of Renderscript bitcode files, i.e. "bc" */ + public static final String EXT_BC = "bc"; //$NON-NLS-1$ + /** Extension of dependency files, i.e. "d" */ + public static final String EXT_DEP = "d"; //$NON-NLS-1$ + /** Extension of native libraries, i.e. "so" */ + public static final String EXT_NATIVE_LIB = "so"; //$NON-NLS-1$ + /** Extension of dex files, i.e. "dex" */ + public static final String EXT_DEX = "dex"; //$NON-NLS-1$ + /** Extension for temporary resource files, ie "ap_ */ + public static final String EXT_RES = "ap_"; //$NON-NLS-1$ + /** Extension for pre-processable images. Right now pngs */ + public static final String EXT_PNG = "png"; //$NON-NLS-1$ + /** Extension for Android archive files */ + public static final String EXT_AAR = "aar"; //$NON-NLS-1$ + /** Extension for Java heap dumps. */ + public static final String EXT_HPROF = "hprof"; //$NON-NLS-1$ + + private static final String DOT = "."; //$NON-NLS-1$ + + /** Dot-Extension of the Application package Files, i.e. ".apk". */ + public static final String DOT_ANDROID_PACKAGE = DOT + EXT_ANDROID_PACKAGE; + /** Dot-Extension of aidl files, i.e. ".aidl" */ + public static final String DOT_AIDL = DOT + EXT_AIDL; + /** Dot-Extension of renderscript files, i.e. ".rs" */ + public static final String DOT_RS = DOT + EXT_RS; + /** Dot-Extension of renderscript header files, i.e. ".rsh" */ + public static final String DOT_RSH = DOT + EXT_RSH; + /** Dot-Extension of FilterScript files, i.e. ".fs" */ + public static final String DOT_FS = DOT + EXT_FS; + /** Dot-Extension of renderscript bitcode files, i.e. ".bc" */ + public static final String DOT_BC = DOT + EXT_BC; + /** Dot-Extension of dependency files, i.e. ".d" */ + public static final String DOT_DEP = DOT + EXT_DEP; + /** Dot-Extension of dex files, i.e. ".dex" */ + public static final String DOT_DEX = DOT + EXT_DEX; + /** Dot-Extension for temporary resource files, ie "ap_ */ + public static final String DOT_RES = DOT + EXT_RES; + /** Dot-Extension for BMP files, i.e. ".bmp" */ + public static final String DOT_BMP = ".bmp"; //$NON-NLS-1$ + /** Dot-Extension for SVG files, i.e. ".svg" */ + public static final String DOT_SVG = ".svg"; //$NON-NLS-1$ + /** Dot-Extension for template files */ + public static final String DOT_FTL = ".ftl"; //$NON-NLS-1$ + /** Dot-Extension of text files, i.e. ".txt" */ + public static final String DOT_TXT = ".txt"; //$NON-NLS-1$ + /** Dot-Extension for Android archive files */ + public static final String DOT_AAR = DOT + EXT_AAR; //$NON-NLS-1$ + + /** Resource base name for java files and classes */ + public static final String FN_RESOURCE_BASE = "R"; //$NON-NLS-1$ + /** Resource java class filename, i.e. "R.java" */ + public static final String FN_RESOURCE_CLASS = FN_RESOURCE_BASE + DOT_JAVA; + /** Resource class file filename, i.e. "R.class" */ + public static final String FN_COMPILED_RESOURCE_CLASS = FN_RESOURCE_BASE + DOT_CLASS; + /** Resource text filename, i.e. "R.txt" */ + public static final String FN_RESOURCE_TEXT = FN_RESOURCE_BASE + DOT_TXT; + /** Generated manifest class name */ + public static final String FN_MANIFEST_BASE = "Manifest"; //$NON-NLS-1$ + /** Generated BuildConfig class name */ + public static final String FN_BUILD_CONFIG_BASE = "BuildConfig"; //$NON-NLS-1$ + /** Manifest java class filename, i.e. "Manifest.java" */ + public static final String FN_MANIFEST_CLASS = FN_MANIFEST_BASE + DOT_JAVA; + /** BuildConfig java class filename, i.e. "BuildConfig.java" */ + public static final String FN_BUILD_CONFIG = FN_BUILD_CONFIG_BASE + DOT_JAVA; + + public static final String DRAWABLE_FOLDER = "drawable"; //$NON-NLS-1$ + public static final String DRAWABLE_XHDPI = "drawable-xhdpi"; //$NON-NLS-1$ + public static final String DRAWABLE_XXHDPI = "drawable-xxhdpi"; //$NON-NLS-1$ + public static final String DRAWABLE_HDPI = "drawable-hdpi"; //$NON-NLS-1$ + public static final String DRAWABLE_MDPI = "drawable-mdpi"; //$NON-NLS-1$ + public static final String DRAWABLE_LDPI = "drawable-ldpi"; //$NON-NLS-1$ + + // Resources + public static final String PREFIX_RESOURCE_REF = "@"; //$NON-NLS-1$ + public static final String PREFIX_THEME_REF = "?"; //$NON-NLS-1$ + public static final String ANDROID_PREFIX = "@android:"; //$NON-NLS-1$ + public static final String ANDROID_THEME_PREFIX = "?android:"; //$NON-NLS-1$ + public static final String LAYOUT_RESOURCE_PREFIX = "@layout/"; //$NON-NLS-1$ + public static final String STYLE_RESOURCE_PREFIX = "@style/"; //$NON-NLS-1$ + public static final String COLOR_RESOURCE_PREFIX = "@color/"; //$NON-NLS-1$ + public static final String NEW_ID_PREFIX = "@+id/"; //$NON-NLS-1$ + public static final String ID_PREFIX = "@id/"; //$NON-NLS-1$ + public static final String DRAWABLE_PREFIX = "@drawable/"; //$NON-NLS-1$ + public static final String STRING_PREFIX = "@string/"; //$NON-NLS-1$ + public static final String DIMEN_PREFIX = "@dimen/"; //$NON-NLS-1$ + + public static final String ANDROID_LAYOUT_RESOURCE_PREFIX = "@android:layout/"; //$NON-NLS-1$ + public static final String ANDROID_STYLE_RESOURCE_PREFIX = "@android:style/"; //$NON-NLS-1$ + public static final String ANDROID_NEW_ID_PREFIX = "@android:+id/"; //$NON-NLS-1$ + public static final String ANDROID_ID_PREFIX = "@android:id/"; //$NON-NLS-1$ + public static final String ANDROID_DRAWABLE_PREFIX = "@android:drawable/"; //$NON-NLS-1$ + public static final String ANDROID_STRING_PREFIX = "@android:string/"; //$NON-NLS-1$ + + public static final String RESOURCE_CLZ_ID = "id"; //$NON-NLS-1$ + public static final String RESOURCE_CLZ_COLOR = "color"; //$NON-NLS-1$ + public static final String RESOURCE_CLZ_ARRAY = "array"; //$NON-NLS-1$ + public static final String RESOURCE_CLZ_ATTR = "attr"; //$NON-NLS-1$ + public static final String RESOURCE_CLR_STYLEABLE = "styleable"; //$NON-NLS-1$ + public static final String NULL_RESOURCE = "@null"; //$NON-NLS-1$ + public static final String TRANSPARENT_COLOR = "@android:color/transparent"; //$NON-NLS-1$ + public static final String REFERENCE_STYLE = "style/"; //$NON-NLS-1$ + public static final String PREFIX_ANDROID = "android:"; //$NON-NLS-1$ + + // Resource Types + public static final String DRAWABLE_TYPE = "drawable"; //$NON-NLS-1$ + public static final String MENU_TYPE = "menu"; //$NON-NLS-1$ + + // Packages + public static final String ANDROID_PKG_PREFIX = "android."; //$NON-NLS-1$ + public static final String WIDGET_PKG_PREFIX = "android.widget."; //$NON-NLS-1$ + public static final String VIEW_PKG_PREFIX = "android.view."; //$NON-NLS-1$ + + // Project properties + public static final String ANDROID_LIBRARY = "android.library"; //$NON-NLS-1$ + public static final String PROGUARD_CONFIG = "proguard.config"; //$NON-NLS-1$ + public static final String ANDROID_LIBRARY_REFERENCE_FORMAT = "android.library.reference.%1$d";//$NON-NLS-1$ + public static final String PROJECT_PROPERTIES = "project.properties";//$NON-NLS-1$ + + // Java References + public static final String ATTR_REF_PREFIX = "?attr/"; //$NON-NLS-1$ + public static final String R_PREFIX = "R."; //$NON-NLS-1$ + public static final String R_ID_PREFIX = "R.id."; //$NON-NLS-1$ + public static final String R_LAYOUT_RESOURCE_PREFIX = "R.layout."; //$NON-NLS-1$ + public static final String R_DRAWABLE_PREFIX = "R.drawable."; //$NON-NLS-1$ + public static final String R_STYLEABLE_PREFIX = "R.styleable."; //$NON-NLS-1$ + public static final String R_ATTR_PREFIX = "R.attr."; //$NON-NLS-1$ + + // Attributes related to tools + public static final String ATTR_IGNORE = "ignore"; //$NON-NLS-1$ + public static final String ATTR_LOCALE = "locale"; //$NON-NLS-1$ + + // SuppressLint + public static final String SUPPRESS_ALL = "all"; //$NON-NLS-1$ + public static final String SUPPRESS_LINT = "SuppressLint"; //$NON-NLS-1$ + public static final String TARGET_API = "TargetApi"; //$NON-NLS-1$ + public static final String ATTR_TARGET_API = "targetApi"; //$NON-NLS-1$ + public static final String FQCN_SUPPRESS_LINT = "android.annotation." + SUPPRESS_LINT; //$NON-NLS-1$ + public static final String FQCN_TARGET_API = "android.annotation." + TARGET_API; //$NON-NLS-1$ + + // Class Names + public static final String CONSTRUCTOR_NAME = ""; //$NON-NLS-1$ + public static final String CLASS_CONSTRUCTOR = ""; //$NON-NLS-1$ + public static final String FRAGMENT = "android/app/Fragment"; //$NON-NLS-1$ + public static final String FRAGMENT_V4 = "android/support/v4/app/Fragment"; //$NON-NLS-1$ + public static final String ANDROID_APP_ACTIVITY = "android/app/Activity"; //$NON-NLS-1$ + public static final String ANDROID_APP_SERVICE = "android/app/Service"; //$NON-NLS-1$ + public static final String ANDROID_CONTENT_CONTENT_PROVIDER = + "android/content/ContentProvider"; //$NON-NLS-1$ + public static final String ANDROID_CONTENT_BROADCAST_RECEIVER = + "android/content/BroadcastReceiver"; //$NON-NLS-1$ + public static final String ANDROID_VIEW_VIEW = "android/view/View"; //$NON-NLS-1$ + + // Method Names + public static final String FORMAT_METHOD = "format"; //$NON-NLS-1$ + public static final String GET_STRING_METHOD = "getString"; //$NON-NLS-1$ + + + + + public static final String ATTR_TAG = "tag"; //$NON-NLS-1$ + public static final String ATTR_NUM_COLUMNS = "numColumns"; //$NON-NLS-1$ + + // Some common layout element names + public static final String CALENDAR_VIEW = "CalendarView"; //$NON-NLS-1$ + public static final String SPACE = "Space"; //$NON-NLS-1$ + public static final String GESTURE_OVERLAY_VIEW = "GestureOverlayView";//$NON-NLS-1$ + + public static final String ATTR_HANDLE = "handle"; //$NON-NLS-1$ + public static final String ATTR_CONTENT = "content"; //$NON-NLS-1$ + public static final String ATTR_CHECKED = "checked"; //$NON-NLS-1$ + + // TextView + public static final String ATTR_DRAWABLE_RIGHT = "drawableRight"; //$NON-NLS-1$ + public static final String ATTR_DRAWABLE_LEFT = "drawableLeft"; //$NON-NLS-1$ + public static final String ATTR_DRAWABLE_START = "drawableStart"; //$NON-NLS-1$ + public static final String ATTR_DRAWABLE_END = "drawableEnd"; //$NON-NLS-1$ + public static final String ATTR_DRAWABLE_BOTTOM = "drawableBottom"; //$NON-NLS-1$ + public static final String ATTR_DRAWABLE_TOP = "drawableTop"; //$NON-NLS-1$ + public static final String ATTR_DRAWABLE_PADDING = "drawablePadding"; //$NON-NLS-1$ + + public static final String ATTR_USE_DEFAULT_MARGINS = "useDefaultMargins"; //$NON-NLS-1$ + public static final String ATTR_MARGINS_INCLUDED_IN_ALIGNMENT = "marginsIncludedInAlignment"; //$NON-NLS-1$ + + public static final String VALUE_WRAP_CONTENT = "wrap_content"; //$NON-NLS-1$ + public static final String VALUE_FALSE= "false"; //$NON-NLS-1$ + public static final String VALUE_N_DP = "%ddp"; //$NON-NLS-1$ + public static final String VALUE_ZERO_DP = "0dp"; //$NON-NLS-1$ + public static final String VALUE_ONE_DP = "1dp"; //$NON-NLS-1$ + public static final String VALUE_TOP = "top"; //$NON-NLS-1$ + public static final String VALUE_BOTTOM = "bottom"; //$NON-NLS-1$ + public static final String VALUE_CENTER_VERTICAL = "center_vertical"; //$NON-NLS-1$ + public static final String VALUE_CENTER_HORIZONTAL = "center_horizontal"; //$NON-NLS-1$ + public static final String VALUE_FILL_HORIZONTAL = "fill_horizontal"; //$NON-NLS-1$ + public static final String VALUE_FILL_VERTICAL = "fill_vertical"; //$NON-NLS-1$ + public static final String VALUE_0 = "0"; //$NON-NLS-1$ + public static final String VALUE_1 = "1"; //$NON-NLS-1$ + + // Gravity values. These have the GRAVITY_ prefix in front of value because we already + // have VALUE_CENTER_HORIZONTAL defined for layouts, and its definition conflicts + // (centerHorizontal versus center_horizontal) + public static final String GRAVITY_VALUE_ = "center"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_CENTER = "center"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_LEFT = "left"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_RIGHT = "right"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_START = "start"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_END = "end"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_BOTTOM = "bottom"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_TOP = "top"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_FILL_HORIZONTAL = "fill_horizontal"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_FILL_VERTICAL = "fill_vertical"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_CENTER_HORIZONTAL = "center_horizontal"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_CENTER_VERTICAL = "center_vertical"; //$NON-NLS-1$ + public static final String GRAVITY_VALUE_FILL = "fill"; //$NON-NLS-1$ + + /** + * The top level android package as a prefix, "android.". + */ + public static final String ANDROID_SUPPORT_PKG_PREFIX = ANDROID_PKG_PREFIX + "support."; //$NON-NLS-1$ + + /** The android.view. package prefix */ + public static final String ANDROID_VIEW_PKG = ANDROID_PKG_PREFIX + "view."; //$NON-NLS-1$ + + /** The android.widget. package prefix */ + public static final String ANDROID_WIDGET_PREFIX = ANDROID_PKG_PREFIX + "widget."; //$NON-NLS-1$ + + /** The android.webkit. package prefix */ + public static final String ANDROID_WEBKIT_PKG = ANDROID_PKG_PREFIX + "webkit."; //$NON-NLS-1$ + + /** The LayoutParams inner-class name suffix, .LayoutParams */ + public static final String DOT_LAYOUT_PARAMS = ".LayoutParams"; //$NON-NLS-1$ + + /** The fully qualified class name of an EditText view */ + public static final String FQCN_EDIT_TEXT = "android.widget.EditText"; //$NON-NLS-1$ + + /** The fully qualified class name of a LinearLayout view */ + public static final String FQCN_LINEAR_LAYOUT = "android.widget.LinearLayout"; //$NON-NLS-1$ + + /** The fully qualified class name of a RelativeLayout view */ + public static final String FQCN_RELATIVE_LAYOUT = "android.widget.RelativeLayout"; //$NON-NLS-1$ + + /** The fully qualified class name of a RelativeLayout view */ + public static final String FQCN_GRID_LAYOUT = "android.widget.GridLayout"; //$NON-NLS-1$ + public static final String FQCN_GRID_LAYOUT_V7 = "android.support.v7.widget.GridLayout"; //$NON-NLS-1$ + + /** The fully qualified class name of a FrameLayout view */ + public static final String FQCN_FRAME_LAYOUT = "android.widget.FrameLayout"; //$NON-NLS-1$ + + /** The fully qualified class name of a TableRow view */ + public static final String FQCN_TABLE_ROW = "android.widget.TableRow"; //$NON-NLS-1$ + + /** The fully qualified class name of a TableLayout view */ + public static final String FQCN_TABLE_LAYOUT = "android.widget.TableLayout"; //$NON-NLS-1$ + + /** The fully qualified class name of a GridView view */ + public static final String FQCN_GRID_VIEW = "android.widget.GridView"; //$NON-NLS-1$ + + /** The fully qualified class name of a TabWidget view */ + public static final String FQCN_TAB_WIDGET = "android.widget.TabWidget"; //$NON-NLS-1$ + + /** The fully qualified class name of a Button view */ + public static final String FQCN_BUTTON = "android.widget.Button"; //$NON-NLS-1$ + + /** The fully qualified class name of a RadioButton view */ + public static final String FQCN_RADIO_BUTTON = "android.widget.RadioButton"; //$NON-NLS-1$ + + /** The fully qualified class name of a ToggleButton view */ + public static final String FQCN_TOGGLE_BUTTON = "android.widget.ToggleButton"; //$NON-NLS-1$ + + /** The fully qualified class name of a Spinner view */ + public static final String FQCN_SPINNER = "android.widget.Spinner"; //$NON-NLS-1$ + + /** The fully qualified class name of an AdapterView */ + public static final String FQCN_ADAPTER_VIEW = "android.widget.AdapterView"; //$NON-NLS-1$ + + /** The fully qualified class name of a ListView */ + public static final String FQCN_LIST_VIEW = "android.widget.ListView"; //$NON-NLS-1$ + + /** The fully qualified class name of an ExpandableListView */ + public static final String FQCN_EXPANDABLE_LIST_VIEW = "android.widget.ExpandableListView"; //$NON-NLS-1$ + + /** The fully qualified class name of a GestureOverlayView */ + public static final String FQCN_GESTURE_OVERLAY_VIEW = "android.gesture.GestureOverlayView"; //$NON-NLS-1$ + + /** The fully qualified class name of a DatePicker */ + public static final String FQCN_DATE_PICKER = "android.widget.DatePicker"; //$NON-NLS-1$ + + /** The fully qualified class name of a TimePicker */ + public static final String FQCN_TIME_PICKER = "android.widget.TimePicker"; //$NON-NLS-1$ + + /** The fully qualified class name of a RadioGroup */ + public static final String FQCN_RADIO_GROUP = "android.widgets.RadioGroup"; //$NON-NLS-1$ + + /** The fully qualified class name of a Space */ + public static final String FQCN_SPACE = "android.widget.Space"; //$NON-NLS-1$ + public static final String FQCN_SPACE_V7 = "android.support.v7.widget.Space"; //$NON-NLS-1$ + + /** The fully qualified class name of a TextView view */ + public static final String FQCN_TEXT_VIEW = "android.widget.TextView"; //$NON-NLS-1$ + + /** The fully qualified class name of an ImageView view */ + public static final String FQCN_IMAGE_VIEW = "android.widget.ImageView"; //$NON-NLS-1$ + + public static final String ATTR_SRC = "src"; //$NON-NLS-1$ + + public static final String ATTR_GRAVITY = "gravity"; //$NON-NLS-1$ + + public static final String ATTR_WEIGHT_SUM = "weightSum"; //$NON-NLS-1$ + public static final String ATTR_EMS = "ems"; //$NON-NLS-1$ + + public static final String VALUE_HORIZONTAL = "horizontal"; //$NON-NLS-1$ + + public static final String GRADLE_PLUGIN_NAME = "com.android.tools.build:gradle:"; + public static final String GRADLE_MINIMUM_VERSION = "1.12"; + public static final String GRADLE_LATEST_VERSION = "1.12"; + public static final String GRADLE_PLUGIN_MINIMUM_VERSION = "0.12.0"; + public static final String GRADLE_PLUGIN_LATEST_VERSION = "0.12.+"; + public static final String GRADLE_PLUGIN_RECOMMENDED_VERSION = "0.12.2"; + public static final String MIN_BUILD_TOOLS_VERSION = "19.1.0"; + public static final String SUPPORT_LIB_ARTIFACT = "com.android.support:support-v4"; + public static final String APPCOMPAT_LIB_ARTIFACT = "com.android.support:appcompat-v7"; + + // Annotations + public static final String SUPPORT_ANNOTATIONS_PREFIX = "android.support.annotation."; + public static final String INT_DEF_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "IntDef"; + public static final String STRING_DEF_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "StringDef"; + public static final String TYPE_DEF_VALUE_ATTRIBUTE = "value"; + public static final String TYPE_DEF_FLAG_ATTRIBUTE = "flag"; + public static final String FN_ANNOTATIONS_ZIP = "annotations.zip"; +} diff --git a/third_party/java/apkbuilder/java/com/android/annotations/NonNull.java b/third_party/java/apkbuilder/java/com/android/annotations/NonNull.java new file mode 100644 index 00000000000000..973ebb654bf884 --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/annotations/NonNull.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 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.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Denotes that a parameter, field or method return value can never be null. + *

+ * This is a marker annotation and it has no specific attributes. + */ +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({METHOD,PARAMETER,LOCAL_VARIABLE,FIELD}) +public @interface NonNull { +} diff --git a/third_party/java/apkbuilder/java/com/android/annotations/Nullable.java b/third_party/java/apkbuilder/java/com/android/annotations/Nullable.java new file mode 100644 index 00000000000000..d9c3861efe11de --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/annotations/Nullable.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2011 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.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Denotes that a parameter, field or method return value can be null. + * Note: this is the default assumption for most Java APIs and the + * default assumption made by most static code checking tools, so usually you + * don't need to use this annotation; its primary use is to override a default + * wider annotation like {@link NonNullByDefault}. + *

+ * When decorating a method call parameter, this denotes the parameter can + * legitimately be null and the method will gracefully deal with it. Typically + * used on optional parameters. + *

+ * When decorating a method, this denotes the method might legitimately return + * null. + *

+ * This is a marker annotation and it has no specific attributes. + */ +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({METHOD, PARAMETER, LOCAL_VARIABLE, FIELD}) +public @interface Nullable { +} diff --git a/third_party/java/apkbuilder/java/com/android/prefs/AndroidLocation.java b/third_party/java/apkbuilder/java/com/android/prefs/AndroidLocation.java new file mode 100644 index 00000000000000..1444cc8279428e --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/prefs/AndroidLocation.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2008 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.prefs; + +import com.android.annotations.NonNull; + +import java.io.File; + +/** + * Manages the location of the android files (including emulator files, ddms config, debug keystore) + */ +public final class AndroidLocation { + + /** + * The name of the .android folder returned by {@link #getFolder()}. + */ + public static final String FOLDER_DOT_ANDROID = ".android"; + + /** + * Virtual Device folder inside the path returned by {@link #getFolder()} + */ + public static final String FOLDER_AVD = "avd"; + + /** + * Throw when the location of the android folder couldn't be found. + */ + public static final class AndroidLocationException extends Exception { + private static final long serialVersionUID = 1L; + + public AndroidLocationException(String string) { + super(string); + } + } + + private static String sPrefsLocation = null; + + /** + * Enum describing which variables to check and whether they should + * be checked via {@link System#getProperty(String)} or {@link System#getenv()} or both. + */ + public enum EnvVar { + ANDROID_SDK_HOME("ANDROID_SDK_HOME", true, true), // both sys prop and env var + USER_HOME ("user.home", true, false), // sys prop only + HOME ("HOME", false, true); // env var only + + final String mName; + final boolean mIsSysProp; + final boolean mIsEnvVar; + + private EnvVar(String name, boolean isSysProp, boolean isEnvVar) { + mName = name; + mIsSysProp = isSysProp; + mIsEnvVar = isEnvVar; + } + + public String getName() { + return mName; + } + } + + /** + * Returns the folder used to store android related files. + * @return an OS specific path, terminated by a separator. + * @throws AndroidLocationException + */ + @NonNull + public static final String getFolder() throws AndroidLocationException { + if (sPrefsLocation == null) { + String home = findValidPath(new EnvVar[] { EnvVar.ANDROID_SDK_HOME, + EnvVar.USER_HOME, + EnvVar.HOME }); + + // if the above failed, we throw an exception. + if (home == null) { + throw new AndroidLocationException( + "Unable to get the Android SDK home directory.\n" + + "Make sure the environment variable ANDROID_SDK_HOME is set up."); + } else { + sPrefsLocation = home; + if (!sPrefsLocation.endsWith(File.separator)) { + sPrefsLocation += File.separator; + } + sPrefsLocation += FOLDER_DOT_ANDROID + File.separator; + } + } + + // make sure the folder exists! + File f = new File(sPrefsLocation); + if (f.exists() == false) { + try { + f.mkdir(); + } catch (SecurityException e) { + AndroidLocationException e2 = new AndroidLocationException(String.format( + "Unable to create folder '%1$s'. " + + "This is the path of preference folder expected by the Android tools.", + sPrefsLocation)); + e2.initCause(e); + throw e2; + } + } else if (f.isFile()) { + throw new AndroidLocationException(sPrefsLocation + + " is not a directory! " + + "This is the path of preference folder expected by the Android tools."); + } + + return sPrefsLocation; + } + + /** + * Resets the folder used to store android related files. For testing. + */ + public static final void resetFolder() { + sPrefsLocation = null; + } + + /** + * Checks a list of system properties and/or system environment variables for validity, and + * existing director, and returns the first one. + * @param vars The variables to check. Order does matter. + * @return the content of the first property/variable that is a valid directory. + */ + private static String findValidPath(EnvVar... vars) { + for (EnvVar var : vars) { + String path; + if (var.mIsSysProp) { + path = checkPath(System.getProperty(var.mName)); + if (path != null) { + return path; + } + } + + if (var.mIsEnvVar) { + path = checkPath(System.getenv(var.mName)); + if (path != null) { + return path; + } + } + } + + return null; + } + + private static String checkPath(String path) { + if (path != null) { + File f = new File(path); + if (f.isDirectory()) { + return path; + } + } + return null; + } +} diff --git a/third_party/java/apkbuilder/java/com/android/sdklib/build/ApkBuilder.java b/third_party/java/apkbuilder/java/com/android/sdklib/build/ApkBuilder.java new file mode 100644 index 00000000000000..4a69e2efd18b5c --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/sdklib/build/ApkBuilder.java @@ -0,0 +1,1005 @@ +/* + * Copyright (C) 2010 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.sdklib.build; + +import com.android.SdkConstants; +import com.android.sdklib.internal.build.DebugKeyProvider; +import com.android.sdklib.internal.build.DebugKeyProvider.IKeyGenOutput; +import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException; +import com.android.sdklib.internal.build.SignedJarBuilder; +import com.android.sdklib.internal.build.SignedJarBuilder.IZipEntryFilter; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.regex.Pattern; + +/** + * Class making the final apk packaging. + * The inputs are: + * - packaged resources (output of aapt) + * - code file (output of dx) + * - Java resources coming from the project, its libraries, and its jar files + * - Native libraries from the project or its library. + * + */ +public final class ApkBuilder implements IArchiveBuilder { + + private static final Pattern PATTERN_NATIVELIB_EXT = Pattern.compile("^.+\\.so$", + Pattern.CASE_INSENSITIVE); + private static final Pattern PATTERN_BITCODELIB_EXT = Pattern.compile("^.+\\.bc$", + Pattern.CASE_INSENSITIVE); + + /** + * A No-op zip filter. It's used to detect conflicts. + * + */ + private final class NullZipFilter implements IZipEntryFilter { + private File mInputFile; + + void reset(File inputFile) { + mInputFile = inputFile; + } + + @Override + public boolean checkEntry(String archivePath) throws ZipAbortException { + verbosePrintln("=> %s", archivePath); + + File duplicate = checkFileForDuplicate(archivePath); + if (duplicate != null) { + throw new DuplicateFileException(archivePath, duplicate, mInputFile); + } else { + mAddedFiles.put(archivePath, mInputFile); + } + + return true; + } + } + + /** + * Custom {@link IZipEntryFilter} to filter out everything that is not a standard java + * resources, and also record whether the zip file contains native libraries. + *

Used in {@link SignedJarBuilder#writeZip(java.io.InputStream, IZipEntryFilter)} when + * we only want the java resources from external jars. + */ + private final class JavaAndNativeResourceFilter implements IZipEntryFilter { + private final List mNativeLibs = new ArrayList(); + private boolean mNativeLibsConflict = false; + private File mInputFile; + + @Override + public boolean checkEntry(String archivePath) throws ZipAbortException { + // split the path into segments. + String[] segments = archivePath.split("/"); + + // empty path? skip to next entry. + if (segments.length == 0) { + return false; + } + + // Check each folders to make sure they should be included. + // Folders like CVS, .svn, etc.. should already have been excluded from the + // jar file, but we need to exclude some other folder (like /META-INF) so + // we check anyway. + for (int i = 0 ; i < segments.length - 1; i++) { + if (!checkFolderForPackaging(segments[i])) { + return false; + } + } + + // get the file name from the path + String fileName = segments[segments.length-1]; + + boolean check = checkFileForPackaging(fileName); + + // only do additional checks if the file passes the default checks. + if (check) { + verbosePrintln("=> %s", archivePath); + + File duplicate = checkFileForDuplicate(archivePath); + if (duplicate != null) { + throw new DuplicateFileException(archivePath, duplicate, mInputFile); + } else { + mAddedFiles.put(archivePath, mInputFile); + } + + if (archivePath.endsWith(".so") || archivePath.endsWith(".bc")) { + mNativeLibs.add(archivePath); + + // only .so located in lib/ will interfere with the installation + if (archivePath.startsWith(SdkConstants.FD_APK_NATIVE_LIBS + "/")) { + mNativeLibsConflict = true; + } + } else if (archivePath.endsWith(".jnilib")) { + mNativeLibs.add(archivePath); + } + } + + return check; + } + + List getNativeLibs() { + return mNativeLibs; + } + + boolean getNativeLibsConflict() { + return mNativeLibsConflict; + } + + void reset(File inputFile) { + mInputFile = inputFile; + mNativeLibs.clear(); + mNativeLibsConflict = false; + } + } + + private File mApkFile; + private File mResFile; + private File mDexFile; + private PrintStream mVerboseStream; + private SignedJarBuilder mBuilder; + private boolean mDebugMode = false; + private boolean mIsSealed = false; + + private final NullZipFilter mNullFilter = new NullZipFilter(); + private final JavaAndNativeResourceFilter mFilter = new JavaAndNativeResourceFilter(); + private final HashMap mAddedFiles = new HashMap(); + + /** + * Status for the addition of a jar file resources into the APK. + * This indicates possible issues with native library inside the jar file. + */ + public interface JarStatus { + /** + * Returns the list of native libraries found in the jar file. + */ + List getNativeLibs(); + + /** + * Returns whether some of those libraries were located in the location that Android + * expects its native libraries. + */ + boolean hasNativeLibsConflicts(); + + } + + /** Internal implementation of {@link JarStatus}. */ + private static final class JarStatusImpl implements JarStatus { + public final List mLibs; + public final boolean mNativeLibsConflict; + + private JarStatusImpl(List libs, boolean nativeLibsConflict) { + mLibs = libs; + mNativeLibsConflict = nativeLibsConflict; + } + + @Override + public List getNativeLibs() { + return mLibs; + } + + @Override + public boolean hasNativeLibsConflicts() { + return mNativeLibsConflict; + } + } + + /** + * Signing information. + * + * Both the {@link PrivateKey} and the {@link X509Certificate} are guaranteed to be non-null. + * + */ + public static final class SigningInfo { + public final PrivateKey key; + public final X509Certificate certificate; + + private SigningInfo(PrivateKey key, X509Certificate certificate) { + if (key == null || certificate == null) { + throw new IllegalArgumentException("key and certificate cannot be null"); + } + this.key = key; + this.certificate = certificate; + } + } + + /** + * Returns the key and certificate from a given debug store. + * + * It is expected that the store password is 'android' and the key alias and password are + * 'androiddebugkey' and 'android' respectively. + * + * @param storeOsPath the OS path to the debug store. + * @param verboseStream an option {@link PrintStream} to display verbose information + * @return they key and certificate in a {@link SigningInfo} object or null. + * @throws ApkCreationException + */ + public static SigningInfo getDebugKey(String storeOsPath, final PrintStream verboseStream) + throws ApkCreationException { + try { + if (storeOsPath != null) { + File storeFile = new File(storeOsPath); + try { + checkInputFile(storeFile); + } catch (FileNotFoundException e) { + // ignore these since the debug store can be created on the fly anyway. + } + + // get the debug key + if (verboseStream != null) { + verboseStream.println(String.format("Using keystore: %s", storeOsPath)); + } + + IKeyGenOutput keygenOutput = null; + if (verboseStream != null) { + keygenOutput = new IKeyGenOutput() { + @Override + public void out(String message) { + verboseStream.println(message); + } + + @Override + public void err(String message) { + verboseStream.println(message); + } + }; + } + + DebugKeyProvider keyProvider = new DebugKeyProvider( + storeOsPath, null /*store type*/, keygenOutput); + + PrivateKey key = keyProvider.getDebugKey(); + X509Certificate certificate = (X509Certificate)keyProvider.getCertificate(); + + if (key == null) { + throw new ApkCreationException("Unable to get debug signature key"); + } + + // compare the certificate expiration date + if (certificate != null && certificate.getNotAfter().compareTo(new Date()) < 0) { + // TODO, regenerate a new one. + throw new ApkCreationException("Debug Certificate expired on " + + DateFormat.getInstance().format(certificate.getNotAfter())); + } + + return new SigningInfo(key, certificate); + } else { + return null; + } + } catch (KeytoolException e) { + if (e.getJavaHome() == null) { + throw new ApkCreationException(e.getMessage() + + "\nJAVA_HOME seems undefined, setting it will help locating keytool automatically\n" + + "You can also manually execute the following command\n:" + + e.getCommandLine(), e); + } else { + throw new ApkCreationException(e.getMessage() + + "\nJAVA_HOME is set to: " + e.getJavaHome() + + "\nUpdate it if necessary, or manually execute the following command:\n" + + e.getCommandLine(), e); + } + } catch (ApkCreationException e) { + throw e; + } catch (Exception e) { + throw new ApkCreationException(e); + } + } + + /** + * Creates a new instance. + * + * This creates a new builder that will create the specified output file, using the two + * mandatory given input files. + * + * An optional debug keystore can be provided. If set, it is expected that the store password + * is 'android' and the key alias and password are 'androiddebugkey' and 'android'. + * + * An optional {@link PrintStream} can also be provided for verbose output. If null, there will + * be no output. + * + * @param apkOsPath the OS path of the file to create. + * @param resOsPath the OS path of the packaged resource file. + * @param dexOsPath the OS path of the dex file. This can be null for apk with no code. + * @param verboseStream the stream to which verbose output should go. If null, verbose mode + * is not enabled. + * @throws ApkCreationException + */ + public ApkBuilder(String apkOsPath, String resOsPath, String dexOsPath, String storeOsPath, + PrintStream verboseStream) throws ApkCreationException { + this(new File(apkOsPath), + new File(resOsPath), + dexOsPath != null ? new File(dexOsPath) : null, + storeOsPath, + verboseStream); + } + + /** + * Creates a new instance. + * + * This creates a new builder that will create the specified output file, using the two + * mandatory given input files. + * + * Optional {@link PrivateKey} and {@link X509Certificate} can be provided to sign the APK. + * + * An optional {@link PrintStream} can also be provided for verbose output. If null, there will + * be no output. + * + * @param apkOsPath the OS path of the file to create. + * @param resOsPath the OS path of the packaged resource file. + * @param dexOsPath the OS path of the dex file. This can be null for apk with no code. + * @param key the private key used to sign the package. Can be null. + * @param certificate the certificate used to sign the package. Can be null. + * @param verboseStream the stream to which verbose output should go. If null, verbose mode + * is not enabled. + * @throws ApkCreationException + */ + public ApkBuilder(String apkOsPath, String resOsPath, String dexOsPath, PrivateKey key, + X509Certificate certificate, PrintStream verboseStream) throws ApkCreationException { + this(new File(apkOsPath), + new File(resOsPath), + dexOsPath != null ? new File(dexOsPath) : null, + key, certificate, + verboseStream); + } + + /** + * Creates a new instance. + * + * This creates a new builder that will create the specified output file, using the two + * mandatory given input files. + * + * An optional debug keystore can be provided. If set, it is expected that the store password + * is 'android' and the key alias and password are 'androiddebugkey' and 'android'. + * + * An optional {@link PrintStream} can also be provided for verbose output. If null, there will + * be no output. + * + * @param apkFile the file to create + * @param resFile the file representing the packaged resource file. + * @param dexFile the file representing the dex file. This can be null for apk with no code. + * @param debugStoreOsPath the OS path to the debug keystore, if needed or null. + * @param verboseStream the stream to which verbose output should go. If null, verbose mode + * is not enabled. + * @throws ApkCreationException + */ + public ApkBuilder(File apkFile, File resFile, File dexFile, String debugStoreOsPath, + final PrintStream verboseStream) throws ApkCreationException { + + SigningInfo info = getDebugKey(debugStoreOsPath, verboseStream); + if (info != null) { + init(apkFile, resFile, dexFile, info.key, info.certificate, verboseStream); + } else { + init(apkFile, resFile, dexFile, null /*key*/, null/*certificate*/, verboseStream); + } + } + + /** + * Creates a new instance. + * + * This creates a new builder that will create the specified output file, using the two + * mandatory given input files. + * + * Optional {@link PrivateKey} and {@link X509Certificate} can be provided to sign the APK. + * + * An optional {@link PrintStream} can also be provided for verbose output. If null, there will + * be no output. + * + * @param apkFile the file to create + * @param resFile the file representing the packaged resource file. + * @param dexFile the file representing the dex file. This can be null for apk with no code. + * @param key the private key used to sign the package. Can be null. + * @param certificate the certificate used to sign the package. Can be null. + * @param verboseStream the stream to which verbose output should go. If null, verbose mode + * is not enabled. + * @throws ApkCreationException + */ + public ApkBuilder(File apkFile, File resFile, File dexFile, PrivateKey key, + X509Certificate certificate, PrintStream verboseStream) throws ApkCreationException { + init(apkFile, resFile, dexFile, key, certificate, verboseStream); + } + + + /** + * Constructor init method. + * + * @see #ApkBuilder(File, File, File, String, PrintStream) + * @see #ApkBuilder(String, String, String, String, PrintStream) + * @see #ApkBuilder(File, File, File, PrivateKey, X509Certificate, PrintStream) + */ + private void init(File apkFile, File resFile, File dexFile, PrivateKey key, + X509Certificate certificate, PrintStream verboseStream) throws ApkCreationException { + + try { + checkOutputFile(mApkFile = apkFile); + checkInputFile(mResFile = resFile); + if (dexFile != null) { + checkInputFile(mDexFile = dexFile); + } else { + mDexFile = null; + } + mVerboseStream = verboseStream; + + mBuilder = new SignedJarBuilder( + new FileOutputStream(mApkFile, false /* append */), key, + certificate); + + verbosePrintln("Packaging %s", mApkFile.getName()); + + // add the resources + addZipFile(mResFile); + + // add the class dex file at the root of the apk + if (mDexFile != null) { + addFile(mDexFile, SdkConstants.FN_APK_CLASSES_DEX); + } + + } catch (ApkCreationException e) { + if (mBuilder != null) { + mBuilder.cleanUp(); + } + throw e; + } catch (Exception e) { + if (mBuilder != null) { + mBuilder.cleanUp(); + } + throw new ApkCreationException(e); + } + } + + /** + * Sets the debug mode. In debug mode, when native libraries are present, the packaging + * will also include one or more copies of gdbserver in the final APK file. + * + * These are used for debugging native code, to ensure that gdbserver is accessible to the + * application. + * + * There will be one version of gdbserver for each ABI supported by the application. + * + * the gbdserver files are placed in the libs/abi/ folders automatically by the NDK. + * + * @param debugMode the debug mode flag. + */ + public void setDebugMode(boolean debugMode) { + mDebugMode = debugMode; + } + + /** + * Adds a file to the APK at a given path + * @param file the file to add + * @param archivePath the path of the file inside the APK archive. + * @throws ApkCreationException if an error occurred + * @throws SealedApkException if the APK is already sealed. + * @throws DuplicateFileException if a file conflicts with another already added to the APK + * at the same location inside the APK archive. + */ + @Override + public void addFile(File file, String archivePath) throws ApkCreationException, + SealedApkException, DuplicateFileException { + if (mIsSealed) { + throw new SealedApkException("APK is already sealed"); + } + + try { + doAddFile(file, archivePath); + } catch (DuplicateFileException e) { + mBuilder.cleanUp(); + throw e; + } catch (Exception e) { + mBuilder.cleanUp(); + throw new ApkCreationException(e, "Failed to add %s", file); + } + } + + /** + * Adds the content from a zip file. + * All file keep the same path inside the archive. + * @param zipFile the zip File. + * @throws ApkCreationException if an error occurred + * @throws SealedApkException if the APK is already sealed. + * @throws DuplicateFileException if a file conflicts with another already added to the APK + * at the same location inside the APK archive. + */ + public void addZipFile(File zipFile) throws ApkCreationException, SealedApkException, + DuplicateFileException { + if (mIsSealed) { + throw new SealedApkException("APK is already sealed"); + } + + try { + verbosePrintln("%s:", zipFile); + + // reset the filter with this input. + mNullFilter.reset(zipFile); + + // ask the builder to add the content of the file. + FileInputStream fis = new FileInputStream(zipFile); + mBuilder.writeZip(fis, mNullFilter); + fis.close(); + } catch (DuplicateFileException e) { + mBuilder.cleanUp(); + throw e; + } catch (Exception e) { + mBuilder.cleanUp(); + throw new ApkCreationException(e, "Failed to add %s", zipFile); + } + } + + /** + * Adds the resources from a jar file. + * @param jarFile the jar File. + * @return a {@link JarStatus} object indicating if native libraries where found in + * the jar file. + * @throws ApkCreationException if an error occurred + * @throws SealedApkException if the APK is already sealed. + * @throws DuplicateFileException if a file conflicts with another already added to the APK + * at the same location inside the APK archive. + */ + public JarStatus addResourcesFromJar(File jarFile) throws ApkCreationException, + SealedApkException, DuplicateFileException { + if (mIsSealed) { + throw new SealedApkException("APK is already sealed"); + } + + try { + verbosePrintln("%s:", jarFile); + + // reset the filter with this input. + mFilter.reset(jarFile); + + // ask the builder to add the content of the file, filtered to only let through + // the java resources. + FileInputStream fis = new FileInputStream(jarFile); + mBuilder.writeZip(fis, mFilter); + fis.close(); + + // check if native libraries were found in the external library. This should + // constitutes an error or warning depending on if they are in lib/ + return new JarStatusImpl(mFilter.getNativeLibs(), mFilter.getNativeLibsConflict()); + } catch (DuplicateFileException e) { + mBuilder.cleanUp(); + throw e; + } catch (Exception e) { + mBuilder.cleanUp(); + throw new ApkCreationException(e, "Failed to add %s", jarFile); + } + } + + /** + * Adds the resources from a source folder. + * @param sourceFolder the source folder. + * @throws ApkCreationException if an error occurred + * @throws SealedApkException if the APK is already sealed. + * @throws DuplicateFileException if a file conflicts with another already added to the APK + * at the same location inside the APK archive. + */ + public void addSourceFolder(File sourceFolder) throws ApkCreationException, SealedApkException, + DuplicateFileException { + if (mIsSealed) { + throw new SealedApkException("APK is already sealed"); + } + + addSourceFolder(this, sourceFolder); + } + + /** + * Adds the resources from a source folder to a given {@link IArchiveBuilder} + * @param sourceFolder the source folder. + * @throws ApkCreationException if an error occurred + * @throws DuplicateFileException if a file conflicts with another already added to the APK + * at the same location inside the APK archive. + */ + public static void addSourceFolder(IArchiveBuilder builder, File sourceFolder) + throws ApkCreationException, DuplicateFileException { + if (sourceFolder.isDirectory()) { + try { + // file is a directory, process its content. + File[] files = sourceFolder.listFiles(); + if (files != null) { + for (File file : files) { + processFileForResource(builder, file, null); + } + } + } catch (DuplicateFileException e) { + throw e; + } catch (Exception e) { + throw new ApkCreationException(e, "Failed to add %s", sourceFolder); + } + } else { + // not a directory? check if it's a file or doesn't exist + if (sourceFolder.exists()) { + throw new ApkCreationException("%s is not a folder", sourceFolder); + } else { + throw new ApkCreationException("%s does not exist", sourceFolder); + } + } + } + + /** + * Adds the native libraries from the top native folder. + * The content of this folder must be the various ABI folders. + * + * This may or may not copy gdbserver into the apk based on whether the debug mode is set. + * + * @param nativeFolder the native folder. + * + * @throws ApkCreationException if an error occurred + * @throws SealedApkException if the APK is already sealed. + * @throws DuplicateFileException if a file conflicts with another already added to the APK + * at the same location inside the APK archive. + * + * @see #setDebugMode(boolean) + */ + public void addNativeLibraries(File nativeFolder) + throws ApkCreationException, SealedApkException, DuplicateFileException { + if (mIsSealed) { + throw new SealedApkException("APK is already sealed"); + } + + if (!nativeFolder.isDirectory()) { + // not a directory? check if it's a file or doesn't exist + if (nativeFolder.exists()) { + throw new ApkCreationException("%s is not a folder", nativeFolder); + } else { + throw new ApkCreationException("%s does not exist", nativeFolder); + } + } + + File[] abiList = nativeFolder.listFiles(); + + verbosePrintln("Native folder: %s", nativeFolder); + + if (abiList != null) { + for (File abi : abiList) { + if (abi.isDirectory()) { // ignore files + + File[] libs = abi.listFiles(); + if (libs != null) { + for (File lib : libs) { + // only consider files that are .so or, if in debug mode, that + // are gdbserver executables + if (lib.isFile() && + (PATTERN_NATIVELIB_EXT.matcher(lib.getName()).matches() || + PATTERN_BITCODELIB_EXT.matcher(lib.getName()).matches() || + (mDebugMode && + SdkConstants.FN_GDBSERVER.equals( + lib.getName())))) { + String path = + SdkConstants.FD_APK_NATIVE_LIBS + "/" + + abi.getName() + "/" + lib.getName(); + + try { + doAddFile(lib, path); + } catch (IOException e) { + mBuilder.cleanUp(); + throw new ApkCreationException(e, "Failed to add %s", lib); + } + } + } + } + } + } + } + } + + public void addNativeLibraries(List entries) throws SealedApkException, + DuplicateFileException, ApkCreationException { + if (mIsSealed) { + throw new SealedApkException("APK is already sealed"); + } + + for (FileEntry entry : entries) { + try { + doAddFile(entry.mFile, entry.mPath); + } catch (IOException e) { + mBuilder.cleanUp(); + throw new ApkCreationException(e, "Failed to add %s", entry.mFile); + } + } + } + + public static final class FileEntry { + public final File mFile; + public final String mPath; + + FileEntry(File file, String path) { + mFile = file; + mPath = path; + } + } + + public static List getNativeFiles(File nativeFolder, boolean debugMode) + throws ApkCreationException { + + if (!nativeFolder.isDirectory()) { + // not a directory? check if it's a file or doesn't exist + if (nativeFolder.exists()) { + throw new ApkCreationException("%s is not a folder", nativeFolder); + } else { + throw new ApkCreationException("%s does not exist", nativeFolder); + } + } + + List files = new ArrayList(); + + File[] abiList = nativeFolder.listFiles(); + + if (abiList != null) { + for (File abi : abiList) { + if (abi.isDirectory()) { // ignore files + + File[] libs = abi.listFiles(); + if (libs != null) { + for (File lib : libs) { + // only consider files that are .so or, if in debug mode, that + // are gdbserver executables + if (lib.isFile() && + (PATTERN_NATIVELIB_EXT.matcher(lib.getName()).matches() || + PATTERN_BITCODELIB_EXT.matcher(lib.getName()).matches() || + (debugMode && + SdkConstants.FN_GDBSERVER.equals( + lib.getName())))) { + String path = + SdkConstants.FD_APK_NATIVE_LIBS + "/" + + abi.getName() + "/" + lib.getName(); + + files.add(new FileEntry(lib, path)); + } + } + } + } + } + } + + return files; + } + + + + /** + * Seals the APK, and signs it if necessary. + * @throws ApkCreationException + * @throws ApkCreationException if an error occurred + * @throws SealedApkException if the APK is already sealed. + */ + public void sealApk() throws ApkCreationException, SealedApkException { + if (mIsSealed) { + throw new SealedApkException("APK is already sealed"); + } + + // close and sign the application package. + try { + mBuilder.close(); + mIsSealed = true; + } catch (Exception e) { + throw new ApkCreationException(e, "Failed to seal APK"); + } finally { + mBuilder.cleanUp(); + } + } + + /** + * Output a given message if the verbose mode is enabled. + * @param format the format string for {@link String#format(String, Object...)} + * @param args the string arguments + */ + private void verbosePrintln(String format, Object... args) { + if (mVerboseStream != null) { + mVerboseStream.println(String.format(format, args)); + } + } + + private void doAddFile(File file, String archivePath) throws DuplicateFileException, + IOException { + verbosePrintln("%1$s => %2$s", file, archivePath); + + File duplicate = checkFileForDuplicate(archivePath); + if (duplicate != null) { + throw new DuplicateFileException(archivePath, duplicate, file); + } + + mAddedFiles.put(archivePath, file); + mBuilder.writeFile(file, archivePath); + } + + /** + * Processes a {@link File} that could be an APK {@link File}, or a folder containing + * java resources. + * + * @param file the {@link File} to process. + * @param path the relative path of this file to the source folder. + * Can be null to identify a root file. + * @throws IOException + * @throws DuplicateFileException if a file conflicts with another already added + * to the APK at the same location inside the APK archive. + * @throws SealedApkException if the APK is already sealed. + * @throws ApkCreationException if an error occurred + */ + private static void processFileForResource(IArchiveBuilder builder, File file, String path) + throws IOException, DuplicateFileException, ApkCreationException, SealedApkException { + if (file.isDirectory()) { + // a directory? we check it + if (checkFolderForPackaging(file.getName())) { + // if it's valid, we append its name to the current path. + if (path == null) { + path = file.getName(); + } else { + path = path + "/" + file.getName(); + } + + // and process its content. + File[] files = file.listFiles(); + if (files != null) { + for (File contentFile : files) { + processFileForResource(builder, contentFile, path); + } + } + } + } else { + // a file? we check it to make sure it should be added + if (checkFileForPackaging(file.getName())) { + // we append its name to the current path + if (path == null) { + path = file.getName(); + } else { + path = path + "/" + file.getName(); + } + + // and add it to the apk + builder.addFile(file, path); + } + } + } + + /** + * Checks if the given path in the APK archive has not already been used and if it has been, + * then returns a {@link File} object for the source of the duplicate + * @param archivePath the archive path to test. + * @return A File object of either a file at the same location or an archive that contains a + * file that was put at the same location. + */ + private File checkFileForDuplicate(String archivePath) { + return mAddedFiles.get(archivePath); + } + + /** + * Checks an output {@link File} object. + * This checks the following: + * - the file is not an existing directory. + * - if the file exists, that it can be modified. + * - if it doesn't exists, that a new file can be created. + * @param file the File to check + * @throws ApkCreationException If the check fails + */ + private void checkOutputFile(File file) throws ApkCreationException { + if (file.isDirectory()) { + throw new ApkCreationException("%s is a directory!", file); + } + + if (file.exists()) { // will be a file in this case. + if (!file.canWrite()) { + throw new ApkCreationException("Cannot write %s", file); + } + } else { + try { + if (!file.createNewFile()) { + throw new ApkCreationException("Failed to create %s", file); + } + } catch (IOException e) { + throw new ApkCreationException( + "Failed to create '%1$ss': %2$s", file, e.getMessage()); + } + } + } + + /** + * Checks an input {@link File} object. + * This checks the following: + * - the file is not an existing directory. + * - that the file exists (if throwIfDoesntExist is false) and can + * be read. + * @param file the File to check + * @throws FileNotFoundException if the file is not here. + * @throws ApkCreationException If the file is a folder or a file that cannot be read. + */ + private static void checkInputFile(File file) throws FileNotFoundException, ApkCreationException { + if (file.isDirectory()) { + throw new ApkCreationException("%s is a directory!", file); + } + + if (file.exists()) { + if (!file.canRead()) { + throw new ApkCreationException("Cannot read %s", file); + } + } else { + throw new FileNotFoundException(String.format("%s does not exist", file)); + } + } + + public static String getDebugKeystore() throws ApkCreationException { + try { + return DebugKeyProvider.getDefaultKeyStoreOsPath(); + } catch (Exception e) { + throw new ApkCreationException(e, e.getMessage()); + } + } + + /** + * Checks whether a folder and its content is valid for packaging into the .apk as + * standard Java resource. + * @param folderName the name of the folder. + */ + public static boolean checkFolderForPackaging(String folderName) { + return !folderName.equalsIgnoreCase("CVS") && + !folderName.equalsIgnoreCase(".svn") && + !folderName.equalsIgnoreCase("SCCS") && + !folderName.equalsIgnoreCase("META-INF") && + !folderName.startsWith("_"); + } + + /** + * Checks a file to make sure it should be packaged as standard resources. + * @param fileName the name of the file (including extension) + * @return true if the file should be packaged as standard java resources. + */ + public static boolean checkFileForPackaging(String fileName) { + String[] fileSegments = fileName.split("\\."); + String fileExt = ""; + if (fileSegments.length > 1) { + fileExt = fileSegments[fileSegments.length-1]; + } + + return checkFileForPackaging(fileName, fileExt); + } + + /** + * Checks a file to make sure it should be packaged as standard resources. + * @param fileName the name of the file (including extension) + * @param extension the extension of the file (excluding '.') + * @return true if the file should be packaged as standard java resources. + */ + public static boolean checkFileForPackaging(String fileName, String extension) { + // ignore hidden files and backup files + if (fileName.charAt(0) == '.' || fileName.charAt(fileName.length()-1) == '~') { + return false; + } + + return !"aidl".equalsIgnoreCase(extension) && // Aidl files + !"rs".equalsIgnoreCase(extension) && // RenderScript files + !"fs".equalsIgnoreCase(extension) && // FilterScript files + !"rsh".equalsIgnoreCase(extension) && // RenderScript header files + !"d".equalsIgnoreCase(extension) && // Dependency files + !"java".equalsIgnoreCase(extension) && // Java files + !"scala".equalsIgnoreCase(extension) && // Scala files + !"class".equalsIgnoreCase(extension) && // Java class files + !"scc".equalsIgnoreCase(extension) && // VisualSourceSafe + !"swp".equalsIgnoreCase(extension) && // vi swap file + !"thumbs.db".equalsIgnoreCase(fileName) && // image index file + !"picasa.ini".equalsIgnoreCase(fileName) && // image index file + !"package.html".equalsIgnoreCase(fileName) && // Javadoc + !"overview.html".equalsIgnoreCase(fileName); // Javadoc + } +} diff --git a/third_party/java/apkbuilder/java/com/android/sdklib/build/ApkBuilderMain.java b/third_party/java/apkbuilder/java/com/android/sdklib/build/ApkBuilderMain.java new file mode 100644 index 00000000000000..304385bed6882e --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/sdklib/build/ApkBuilderMain.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2008 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.sdklib.build; + +import com.google.devtools.build.singlejar.ZipCombiner; +import com.google.devtools.build.singlejar.ZipEntryFilter; + +import com.android.SdkConstants; +import com.android.sdklib.build.ApkBuilder.FileEntry; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.regex.Pattern; + +/** + * Command line APK builder with signing support. + */ +public final class ApkBuilderMain { + + private final static Pattern PATTERN_JAR_EXT = Pattern.compile("^.+\\.jar$", + Pattern.CASE_INSENSITIVE); + + /** + * Main method. This is meant to be called from the command line through an exec. + *

WARNING: this will call {@link System#exit(int)} if anything goes wrong. + * @param args command line arguments. + */ + public static void main(String[] args) { + if (args.length < 1) { + printUsageAndQuit(); + } + + try { + File outApk = new File(args[0]); + + File dexFile = null; + ArrayList zipArchives = new ArrayList(); + ArrayList sourceFolders = new ArrayList(); + ArrayList jarFiles = new ArrayList(); + ArrayList nativeFolders = new ArrayList(); + + boolean verbose = false; + boolean signed = true; + boolean debug = false; + String keystorePath = null; + + int index = 1; + do { + String argument = args[index++]; + + if ("-v".equals(argument)) { + verbose = true; + + } else if ("-d".equals(argument)) { + debug = true; + + } else if ("-u".equals(argument)) { + signed = false; + + } else if ("-ks".equals(argument)) { + // bazel-specific option + if (index == args.length) { + printAndExit("Missing value for -ks"); + } + + keystorePath = args[index++]; + } else if ("-z".equals(argument)) { + // quick check on the next argument. + if (index == args.length) { + printAndExit("Missing value for -z"); + } + + zipArchives.add(new File(args[index++])); + } else if ("-f". equals(argument)) { + if (dexFile != null) { + // can't have more than one dex file. + printAndExit("Can't have more than one dex file (-f)"); + } + // quick check on the next argument. + if (index == args.length) { + printAndExit("Missing value for -f"); + } + + dexFile = new File(args[index++]); + } else if ("-rf". equals(argument)) { + // quick check on the next argument. + if (index == args.length) { + printAndExit("Missing value for -rf"); + } + + sourceFolders.add(new File(args[index++])); + } else if ("-rj". equals(argument)) { + // quick check on the next argument. + if (index == args.length) { + printAndExit("Missing value for -rj"); + } + + jarFiles.add(new File(args[index++])); + } else if ("-nf".equals(argument)) { + // quick check on the next argument. + if (index == args.length) { + printAndExit("Missing value for -nf"); + } + + nativeFolders.add(new File(args[index++])); + } else if ("-storetype".equals(argument)) { + // quick check on the next argument. + if (index == args.length) { + printAndExit("Missing value for -storetype"); + } + + // FIXME + } else { + printAndExit("Unknown argument: " + argument); + } + } while (index < args.length); + + if (zipArchives.size() == 0) { + printAndExit("No zip archive, there must be one for the resources"); + } + + if (signed && keystorePath == null) { + keystorePath = ApkBuilder.getDebugKeystore(); + } + + // create the builder with the basic files. + ApkBuilder builder = new ApkBuilder(outApk, zipArchives.get(0), dexFile, + signed ? keystorePath : null, + verbose ? System.out : null); + builder.setDebugMode(debug); + + // add the rest of the files. + // first zip Archive was used in the constructor. + for (int i = 1 ; i < zipArchives.size() ; i++) { + builder.addZipFile(zipArchives.get(i)); + } + + for (File sourceFolder : sourceFolders) { + builder.addSourceFolder(sourceFolder); + } + + for (File jarFile : jarFiles) { + if (jarFile.isDirectory()) { + String[] filenames = jarFile.list(new FilenameFilter() { + public boolean accept(File dir, String name) { + return PATTERN_JAR_EXT.matcher(name).matches(); + } + }); + + for (String filename : filenames) { + builder.addResourcesFromJar(new File(jarFile, filename)); + } + } else { + builder.addResourcesFromJar(jarFile); + } + } + + for (File nativeFolder : nativeFolders) { + builder.addNativeLibraries(nativeFolder); + } + + // seal the apk + builder.sealApk(); + + // ensure hermeticity, bazel specific + clearTimeStamps(outApk); + } catch (ApkCreationException e) { + printAndExit(e.getMessage()); + } catch (DuplicateFileException e) { + printAndExit(String.format( + "Found duplicate file for APK: %1$s\nOrigin 1: %2$s\nOrigin 2: %3$s", + e.getArchivePath(), e.getFile1(), e.getFile2())); + } catch (SealedApkException e) { + printAndExit(e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + + private static void clearTimeStamps(File outApk) throws IOException { + File renamed = new File(outApk.getPath() + ".nonhermetic"); + if (!outApk.renameTo(renamed)) { + throw new IOException("could not rename: " + outApk); + } + + OutputStream out = new FileOutputStream(outApk); + ZipCombiner combiner = new ZipCombiner( + new ZipEntryFilter() { + @Override + public void accept(String filename, StrategyCallback callback) + throws IOException { + callback.copy(ZipCombiner.DOS_EPOCH); + } + }, out); + combiner.addZip(renamed); + combiner.close(); // closes its outstream. + renamed.deleteOnExit(); + } + + private static void printUsageAndQuit() { + // 80 cols marker: 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + System.err.println("A command line tool to package an Android application from various sources."); + System.err.println("Usage: apkbuilder [-v][-u][-storetype STORE_TYPE] [-z inputzip]"); + System.err.println(" [-f inputfile] [-rf input-folder] [-rj -input-path]"); + System.err.println(" [-nf native-folder] [-rj -input-path]"); + System.err.println(""); + System.err.println("NOTE: This is a version of the ApkBuilder tool that comes " + + "with the Android sdk modified for Bazel."); + System.err.println(""); + System.err.println(" -v Verbose."); + System.err.println(" -d Debug Mode: Includes debug files in the APK file."); + System.err.println(" -u Creates an unsigned package."); + System.err.println(" -storetype Forces the KeyStore type. If ommited the default is used."); + System.err.println(""); + System.err.println(" -z Followed by the path to a zip archive."); + System.err.println(" Adds the content of the application package."); + System.err.println(""); + System.err.println(" -f Followed by the path to a file."); + System.err.println(" Adds the file to the application package."); + System.err.println(""); + System.err.println(" -rf Followed by the path to a source folder."); + System.err.println(" Adds the java resources found in that folder to the application"); + System.err.println(" package, while keeping their path relative to the source folder."); + System.err.println(""); + System.err.println(" -rj Followed by the path to a jar file or a folder containing"); + System.err.println(" jar files."); + System.err.println(" Adds the java resources found in the jar file(s) to the application"); + System.err.println(" package."); + System.err.println(""); + System.err.println(" -nf Followed by the root folder containing native libraries to"); + System.err.println(" include in the application package."); + + System.exit(1); + } + + private static void printAndExit(String... messages) { + for (String message : messages) { + System.err.println(message); + } + System.exit(1); + } + + private ApkBuilderMain() { + } +} diff --git a/third_party/java/apkbuilder/java/com/android/sdklib/build/ApkCreationException.java b/third_party/java/apkbuilder/java/com/android/sdklib/build/ApkCreationException.java new file mode 100644 index 00000000000000..2379915dc8f8ea --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/sdklib/build/ApkCreationException.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 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.sdklib.build; + +/** + * An exception thrown during packaging of an APK file. + */ +public final class ApkCreationException extends Exception { + private static final long serialVersionUID = 1L; + + public ApkCreationException(String format, Object... args) { + super(String.format(format, args)); + } + + public ApkCreationException(Throwable cause, String format, Object... args) { + super(String.format(format, args), cause); + } + + public ApkCreationException(Throwable cause) { + super(cause); + } +} \ No newline at end of file diff --git a/third_party/java/apkbuilder/java/com/android/sdklib/build/DuplicateFileException.java b/third_party/java/apkbuilder/java/com/android/sdklib/build/DuplicateFileException.java new file mode 100644 index 00000000000000..ba53ba37ce5a00 --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/sdklib/build/DuplicateFileException.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 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.sdklib.build; + +import com.android.sdklib.internal.build.SignedJarBuilder.IZipEntryFilter.ZipAbortException; + +import java.io.File; + +/** + * An exception thrown during packaging of an APK file. + */ +public final class DuplicateFileException extends ZipAbortException { + private static final long serialVersionUID = 1L; + private final String mArchivePath; + private final File mFile1; + private final File mFile2; + + public DuplicateFileException(String archivePath, File file1, File file2) { + super(); + mArchivePath = archivePath; + mFile1 = file1; + mFile2 = file2; + } + + public String getArchivePath() { + return mArchivePath; + } + + public File getFile1() { + return mFile1; + } + + public File getFile2() { + return mFile2; + } + + @Override + public String getMessage() { + return "Duplicate files at the same path inside the APK"; + } +} \ No newline at end of file diff --git a/third_party/java/apkbuilder/java/com/android/sdklib/build/IArchiveBuilder.java b/third_party/java/apkbuilder/java/com/android/sdklib/build/IArchiveBuilder.java new file mode 100644 index 00000000000000..e2230e9ee76b4f --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/sdklib/build/IArchiveBuilder.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 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.sdklib.build; + +import java.io.File; + +public interface IArchiveBuilder { + + /** + * Adds a file to the archive at a given path + * @param file the file to add + * @param archivePath the path of the file inside the APK archive. + * @throws ApkCreationException if an error occurred + * @throws SealedApkException if the APK is already sealed. + * @throws DuplicateFileException if a file conflicts with another already added to the APK + * at the same location inside the APK archive. + */ + void addFile(File file, String archivePath) throws ApkCreationException, + SealedApkException, DuplicateFileException; + +} diff --git a/third_party/java/apkbuilder/java/com/android/sdklib/build/SealedApkException.java b/third_party/java/apkbuilder/java/com/android/sdklib/build/SealedApkException.java new file mode 100644 index 00000000000000..97f03bdece085a --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/sdklib/build/SealedApkException.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 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.sdklib.build; + +/** + * An exception thrown when trying to add files to a sealed APK. + */ +public final class SealedApkException extends Exception { + private static final long serialVersionUID = 1L; + + public SealedApkException(String format, Object... args) { + super(String.format(format, args)); + } + + public SealedApkException(Throwable cause, String format, Object... args) { + super(String.format(format, args), cause); + } + + public SealedApkException(Throwable cause) { + super(cause); + } +} \ No newline at end of file diff --git a/third_party/java/apkbuilder/java/com/android/sdklib/internal/build/DebugKeyProvider.java b/third_party/java/apkbuilder/java/com/android/sdklib/internal/build/DebugKeyProvider.java new file mode 100644 index 00000000000000..8d4ba37aa3501c --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/sdklib/internal/build/DebugKeyProvider.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2008 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.sdklib.internal.build; + +import com.android.prefs.AndroidLocation; +import com.android.prefs.AndroidLocation.AndroidLocationException; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.UnrecoverableEntryException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; + +/** + * A provider of a dummy key to sign Android application for debugging purpose. + *

This provider uses a custom keystore to create and store a key with a known password. + * + * @deprecated Use Android-Builder instead + */ +@Deprecated +public class DebugKeyProvider { + + public interface IKeyGenOutput { + public void out(String message); + public void err(String message); + } + + private static final String PASSWORD_STRING = "android"; + private static final char[] PASSWORD_CHAR = PASSWORD_STRING.toCharArray(); + private static final String DEBUG_ALIAS = "AndroidDebugKey"; + + // Certificate CN value. This is a hard-coded value for the debug key. + // Android Market checks against this value in order to refuse applications signed with + // debug keys. + private static final String CERTIFICATE_DESC = "CN=Android Debug,O=Android,C=US"; + + private KeyStore.PrivateKeyEntry mEntry; + + public static class KeytoolException extends Exception { + /** default serial uid */ + private static final long serialVersionUID = 1L; + private String mJavaHome = null; + private String mCommandLine = null; + + KeytoolException(String message) { + super(message); + } + + KeytoolException(String message, String javaHome, String commandLine) { + super(message); + + mJavaHome = javaHome; + mCommandLine = commandLine; + } + + public String getJavaHome() { + return mJavaHome; + } + + public String getCommandLine() { + return mCommandLine; + } + } + + /** + * Creates a provider using a keystore at the given location. + *

The keystore, and a new random android debug key are created if they do not yet exist. + *

Password for the store/key is android, and the key alias is + * AndroidDebugKey. + * @param osKeyStorePath the OS path to the keystore, or null if the default one + * is to be used. + * @param storeType an optional keystore type, or null if the default is to + * be used. + * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr + * of the keytool process call. + * @throws KeytoolException If the creation of the debug key failed. + * @throws AndroidLocationException + */ + public DebugKeyProvider(String osKeyStorePath, String storeType, IKeyGenOutput output) + throws KeyStoreException, NoSuchAlgorithmException, CertificateException, + UnrecoverableEntryException, IOException, KeytoolException, AndroidLocationException { + + if (osKeyStorePath == null) { + osKeyStorePath = getDefaultKeyStoreOsPath(); + } + + if (loadKeyEntry(osKeyStorePath, storeType) == false) { + // create the store with the key + createNewStore(osKeyStorePath, storeType, output); + } + } + + /** + * Returns the OS path to the default debug keystore. + * + * @return The OS path to the default debug keystore. + * @throws KeytoolException + * @throws AndroidLocationException + */ + public static String getDefaultKeyStoreOsPath() + throws KeytoolException, AndroidLocationException { + String folder = AndroidLocation.getFolder(); + if (folder == null) { + throw new KeytoolException("Failed to get HOME directory!\n"); + } + String osKeyStorePath = folder + "debug.keystore"; + + return osKeyStorePath; + } + + /** + * Returns the debug {@link PrivateKey} to use to sign applications for debug purpose. + * @return the private key or null if its creation failed. + */ + @SuppressWarnings("unused") // the thrown Exceptions are not actually thrown + public PrivateKey getDebugKey() throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException, UnrecoverableEntryException { + if (mEntry != null) { + return mEntry.getPrivateKey(); + } + + return null; + } + + /** + * Returns the debug {@link Certificate} to use to sign applications for debug purpose. + * @return the certificate or null if its creation failed. + */ + @SuppressWarnings("unused") // the thrown Exceptions are not actually thrown + public Certificate getCertificate() throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException, UnrecoverableEntryException { + if (mEntry != null) { + return mEntry.getCertificate(); + } + + return null; + } + + /** + * Loads the debug key from the keystore. + * @param osKeyStorePath the OS path to the keystore. + * @param storeType an optional keystore type, or null if the default is to + * be used. + * @return true if success, false if the keystore does not exist. + */ + private boolean loadKeyEntry(String osKeyStorePath, String storeType) throws KeyStoreException, + NoSuchAlgorithmException, CertificateException, IOException, + UnrecoverableEntryException { + FileInputStream fis = null; + try { + KeyStore keyStore = KeyStore.getInstance( + storeType != null ? storeType : KeyStore.getDefaultType()); + fis = new FileInputStream(osKeyStorePath); + keyStore.load(fis, PASSWORD_CHAR); + mEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry( + DEBUG_ALIAS, new KeyStore.PasswordProtection(PASSWORD_CHAR)); + } catch (FileNotFoundException e) { + return false; + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + // pass + } + } + } + + return true; + } + + /** + * Creates a new store + * @param osKeyStorePath the location of the store + * @param storeType an optional keystore type, or null if the default is to + * be used. + * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr + * of the keytool process call. + * @throws KeyStoreException + * @throws NoSuchAlgorithmException + * @throws CertificateException + * @throws UnrecoverableEntryException + * @throws IOException + * @throws KeytoolException + */ + private void createNewStore(String osKeyStorePath, String storeType, IKeyGenOutput output) + throws KeyStoreException, NoSuchAlgorithmException, CertificateException, + UnrecoverableEntryException, IOException, KeytoolException { + + if (KeystoreHelper.createNewStore(osKeyStorePath, storeType, PASSWORD_STRING, DEBUG_ALIAS, + PASSWORD_STRING, CERTIFICATE_DESC, 30 /* validity*/, output)) { + loadKeyEntry(osKeyStorePath, storeType); + } + } +} diff --git a/third_party/java/apkbuilder/java/com/android/sdklib/internal/build/KeystoreHelper.java b/third_party/java/apkbuilder/java/com/android/sdklib/internal/build/KeystoreHelper.java new file mode 100644 index 00000000000000..d339ea52aff496 --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/sdklib/internal/build/KeystoreHelper.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2008 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.sdklib.internal.build; + +import com.android.annotations.Nullable; +import com.android.sdklib.internal.build.DebugKeyProvider.IKeyGenOutput; +import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException; +import com.android.utils.GrabProcessOutput; +import com.android.utils.GrabProcessOutput.IProcessOutput; +import com.android.utils.GrabProcessOutput.Wait; + +import java.io.File; +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableEntryException; +import java.security.cert.CertificateException; +import java.util.ArrayList; + +/** + * A Helper to create new keystore/key. + * + * @deprecated Use Android-Builder instead + */ +@Deprecated +public final class KeystoreHelper { + + /** + * Creates a new store + * @param osKeyStorePath the location of the store + * @param storeType an optional keystore type, or null if the default is to + * be used. + * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr + * of the keytool process call. + * @throws KeyStoreException + * @throws NoSuchAlgorithmException + * @throws CertificateException + * @throws UnrecoverableEntryException + * @throws IOException + * @throws KeytoolException + */ + public static boolean createNewStore( + String osKeyStorePath, + String storeType, + String storePassword, + String alias, + String keyPassword, + String description, + int validityYears, + final IKeyGenOutput output) + throws KeyStoreException, NoSuchAlgorithmException, CertificateException, + UnrecoverableEntryException, IOException, KeytoolException { + + // get the executable name of keytool depending on the platform. + String os = System.getProperty("os.name"); + + String keytoolCommand; + if (os.startsWith("Windows")) { + keytoolCommand = "keytool.exe"; + } else { + keytoolCommand = "keytool"; + } + + String javaHome = System.getProperty("java.home"); + + if (javaHome != null && javaHome.length() > 0) { + keytoolCommand = javaHome + File.separator + "bin" + File.separator + keytoolCommand; + } + + // create the command line to call key tool to build the key with no user input. + ArrayList commandList = new ArrayList(); + commandList.add(keytoolCommand); + commandList.add("-genkey"); + commandList.add("-alias"); + commandList.add(alias); + commandList.add("-keyalg"); + commandList.add("RSA"); + commandList.add("-dname"); + commandList.add(description); + commandList.add("-validity"); + commandList.add(Integer.toString(validityYears * 365)); + commandList.add("-keypass"); + commandList.add(keyPassword); + commandList.add("-keystore"); + commandList.add(osKeyStorePath); + commandList.add("-storepass"); + commandList.add(storePassword); + if (storeType != null) { + commandList.add("-storetype"); + commandList.add(storeType); + } + + String[] commandArray = commandList.toArray(new String[commandList.size()]); + + // launch the command line process + int result = 0; + try { + Process process = Runtime.getRuntime().exec(commandArray); + result = GrabProcessOutput.grabProcessOutput( + process, + Wait.WAIT_FOR_READERS, + new IProcessOutput() { + @Override + public void out(@Nullable String line) { + if (line != null) { + if (output != null) { + output.out(line); + } else { + System.out.println(line); + } + } + } + + @Override + public void err(@Nullable String line) { + if (line != null) { + if (output != null) { + output.err(line); + } else { + System.err.println(line); + } + } + } + }); + } catch (Exception e) { + // create the command line as one string for debugging purposes + StringBuilder builder = new StringBuilder(); + boolean firstArg = true; + for (String arg : commandArray) { + boolean hasSpace = arg.indexOf(' ') != -1; + + if (firstArg == true) { + firstArg = false; + } else { + builder.append(' '); + } + + if (hasSpace) { + builder.append('"'); + } + + builder.append(arg); + + if (hasSpace) { + builder.append('"'); + } + } + + throw new KeytoolException("Failed to create key: " + e.getMessage(), + javaHome, builder.toString()); + } + + if (result != 0) { + return false; + } + + return true; + } +} diff --git a/third_party/java/apkbuilder/java/com/android/sdklib/internal/build/SignedJarBuilder.java b/third_party/java/apkbuilder/java/com/android/sdklib/internal/build/SignedJarBuilder.java new file mode 100644 index 00000000000000..2faba6ae0256be --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/sdklib/internal/build/SignedJarBuilder.java @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2008 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.sdklib.internal.build; + +import com.android.SdkConstants; +import com.android.sdklib.internal.build.SignedJarBuilder.IZipEntryFilter.ZipAbortException; + +import sun.misc.BASE64Encoder; +import sun.security.pkcs.ContentInfo; +import sun.security.pkcs.PKCS7; +import sun.security.pkcs.SignerInfo; +import sun.security.x509.AlgorithmId; +import sun.security.x509.X500Name; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.security.DigestOutputStream; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.X509Certificate; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * A Jar file builder with signature support. + * + * @deprecated Use Android-Builder instead + */ +@Deprecated +public class SignedJarBuilder { + private static final String DIGEST_ALGORITHM = "SHA1"; + private static final String DIGEST_ATTR = "SHA1-Digest"; + private static final String DIGEST_MANIFEST_ATTR = "SHA1-Digest-Manifest"; + + /** Write to another stream and also feed it to the Signature object. */ + private static class SignatureOutputStream extends FilterOutputStream { + private Signature mSignature; + private int mCount = 0; + + public SignatureOutputStream(OutputStream out, Signature sig) { + super(out); + mSignature = sig; + } + + @Override + public void write(int b) throws IOException { + try { + mSignature.update((byte) b); + } catch (SignatureException e) { + throw new IOException("SignatureException: " + e); + } + super.write(b); + mCount++; + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + try { + mSignature.update(b, off, len); + } catch (SignatureException e) { + throw new IOException("SignatureException: " + e); + } + super.write(b, off, len); + mCount += len; + } + + public int size() { + return mCount; + } + } + + private JarOutputStream mOutputJar; + private PrivateKey mKey; + private X509Certificate mCertificate; + private Manifest mManifest; + private BASE64Encoder mBase64Encoder; + private MessageDigest mMessageDigest; + + private byte[] mBuffer = new byte[4096]; + + /** + * Classes which implement this interface provides a method to check whether a file should + * be added to a Jar file. + */ + public interface IZipEntryFilter { + + /** + * An exception thrown during packaging of a zip file into APK file. + * This is typically thrown by implementations of + * {@link IZipEntryFilter#checkEntry(String)}. + */ + public static class ZipAbortException extends Exception { + private static final long serialVersionUID = 1L; + + public ZipAbortException() { + super(); + } + + public ZipAbortException(String format, Object... args) { + super(String.format(format, args)); + } + + public ZipAbortException(Throwable cause, String format, Object... args) { + super(String.format(format, args), cause); + } + + public ZipAbortException(Throwable cause) { + super(cause); + } + } + + + /** + * Checks a file for inclusion in a Jar archive. + * @param archivePath the archive file path of the entry + * @return true if the file should be included. + * @throws ZipAbortException if writing the file should be aborted. + */ + public boolean checkEntry(String archivePath) throws ZipAbortException; + } + + /** + * Creates a {@link SignedJarBuilder} with a given output stream, and signing information. + *

If either key or certificate is null then + * the archive will not be signed. + * @param out the {@link OutputStream} where to write the Jar archive. + * @param key the {@link PrivateKey} used to sign the archive, or null. + * @param certificate the {@link X509Certificate} used to sign the archive, or + * null. + * @throws IOException + * @throws NoSuchAlgorithmException + */ + public SignedJarBuilder(OutputStream out, PrivateKey key, X509Certificate certificate) + throws IOException, NoSuchAlgorithmException { + mOutputJar = new JarOutputStream(new BufferedOutputStream(out)); + mOutputJar.setLevel(9); + mKey = key; + mCertificate = certificate; + + if (mKey != null && mCertificate != null) { + mManifest = new Manifest(); + Attributes main = mManifest.getMainAttributes(); + main.putValue("Manifest-Version", "1.0"); + main.putValue("Created-By", "1.0 (Android)"); + + mBase64Encoder = new BASE64Encoder(); + mMessageDigest = MessageDigest.getInstance(DIGEST_ALGORITHM); + } + } + + /** + * Writes a new {@link File} into the archive. + * @param inputFile the {@link File} to write. + * @param jarPath the filepath inside the archive. + * @throws IOException + */ + public void writeFile(File inputFile, String jarPath) throws IOException { + // Get an input stream on the file. + FileInputStream fis = new FileInputStream(inputFile); + try { + + // create the zip entry + JarEntry entry = new JarEntry(jarPath); + entry.setTime(inputFile.lastModified()); + + writeEntry(fis, entry); + } finally { + // close the file stream used to read the file + fis.close(); + } + } + + /** + * Copies the content of a Jar/Zip archive into the receiver archive. + *

An optional {@link IZipEntryFilter} allows to selectively choose which files + * to copy over. + * @param input the {@link InputStream} for the Jar/Zip to copy. + * @param filter the filter or null + * @throws IOException + * @throws ZipAbortException if the {@link IZipEntryFilter} filter indicated that the write + * must be aborted. + */ + public void writeZip(InputStream input, IZipEntryFilter filter) + throws IOException, ZipAbortException { + ZipInputStream zis = new ZipInputStream(input); + + try { + // loop on the entries of the intermediary package and put them in the final package. + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + String name = entry.getName(); + + // do not take directories or anything inside a potential META-INF folder. + if (entry.isDirectory() || name.startsWith("META-INF/")) { + continue; + } + + // if we have a filter, we check the entry against it + if (filter != null && filter.checkEntry(name) == false) { + continue; + } + + JarEntry newEntry; + + // Preserve the STORED method of the input entry. + if (entry.getMethod() == JarEntry.STORED) { + newEntry = new JarEntry(entry); + } else { + // Create a new entry so that the compressed len is recomputed. + newEntry = new JarEntry(name); + } + + writeEntry(zis, newEntry); + + zis.closeEntry(); + } + } finally { + zis.close(); + } + } + + /** + * Closes the Jar archive by creating the manifest, and signing the archive. + * @throws IOException + * @throws GeneralSecurityException + */ + public void close() throws IOException, GeneralSecurityException { + if (mManifest != null) { + // write the manifest to the jar file + mOutputJar.putNextEntry(new JarEntry(JarFile.MANIFEST_NAME)); + mManifest.write(mOutputJar); + + // CERT.SF + Signature signature = Signature.getInstance("SHA1with" + mKey.getAlgorithm()); + signature.initSign(mKey); + mOutputJar.putNextEntry(new JarEntry("META-INF/CERT.SF")); + SignatureOutputStream out = new SignatureOutputStream(mOutputJar, signature); + writeSignatureFile(out); + + // CERT.* + mOutputJar.putNextEntry(new JarEntry("META-INF/CERT." + mKey.getAlgorithm())); + writeSignatureBlock(signature, mCertificate, mKey); + + // close out at the end because it can also close mOutputJar. + // (there's some timing issue here I think, because it's worked before with out + // being closed after writing CERT.SF). + out.close(); + } + + mOutputJar.close(); + mOutputJar = null; + } + + /** + * Clean up of the builder for interrupted workflow. + * This does nothing if {@link #close()} was called successfully. + */ + public void cleanUp() { + if (mOutputJar != null) { + try { + mOutputJar.close(); + } catch (IOException e) { + // pass + } + } + } + + /** + * Adds an entry to the output jar, and write its content from the {@link InputStream} + * @param input The input stream from where to write the entry content. + * @param entry the entry to write in the jar. + * @throws IOException + */ + private void writeEntry(InputStream input, JarEntry entry) throws IOException { + // add the entry to the jar archive + mOutputJar.putNextEntry(entry); + + // read the content of the entry from the input stream, and write it into the archive. + int count; + while ((count = input.read(mBuffer)) != -1) { + mOutputJar.write(mBuffer, 0, count); + + // update the digest + if (mMessageDigest != null) { + mMessageDigest.update(mBuffer, 0, count); + } + } + + // close the entry for this file + mOutputJar.closeEntry(); + + if (mManifest != null) { + // update the manifest for this entry. + Attributes attr = mManifest.getAttributes(entry.getName()); + if (attr == null) { + attr = new Attributes(); + mManifest.getEntries().put(entry.getName(), attr); + } + attr.putValue(DIGEST_ATTR, mBase64Encoder.encode(mMessageDigest.digest())); + } + } + + /** Writes a .SF file with a digest to the manifest. */ + private void writeSignatureFile(SignatureOutputStream out) + throws IOException, GeneralSecurityException { + Manifest sf = new Manifest(); + Attributes main = sf.getMainAttributes(); + main.putValue("Signature-Version", "1.0"); + main.putValue("Created-By", "1.0 (Android)"); + + BASE64Encoder base64 = new BASE64Encoder(); + MessageDigest md = MessageDigest.getInstance(DIGEST_ALGORITHM); + PrintStream print = new PrintStream( + new DigestOutputStream(new ByteArrayOutputStream(), md), + true, SdkConstants.UTF_8); + + // Digest of the entire manifest + mManifest.write(print); + print.flush(); + main.putValue(DIGEST_MANIFEST_ATTR, base64.encode(md.digest())); + + Map entries = mManifest.getEntries(); + for (Map.Entry entry : entries.entrySet()) { + // Digest of the manifest stanza for this entry. + print.print("Name: " + entry.getKey() + "\r\n"); + for (Map.Entry att : entry.getValue().entrySet()) { + print.print(att.getKey() + ": " + att.getValue() + "\r\n"); + } + print.print("\r\n"); + print.flush(); + + Attributes sfAttr = new Attributes(); + sfAttr.putValue(DIGEST_ATTR, base64.encode(md.digest())); + sf.getEntries().put(entry.getKey(), sfAttr); + } + + sf.write(out); + + // A bug in the java.util.jar implementation of Android platforms + // up to version 1.6 will cause a spurious IOException to be thrown + // if the length of the signature file is a multiple of 1024 bytes. + // As a workaround, add an extra CRLF in this case. + if ((out.size() % 1024) == 0) { + out.write('\r'); + out.write('\n'); + } + } + + /** Write the certificate file with a digital signature. */ + private void writeSignatureBlock(Signature signature, X509Certificate publicKey, + PrivateKey privateKey) + throws IOException, GeneralSecurityException { + SignerInfo signerInfo = new SignerInfo( + new X500Name(publicKey.getIssuerX500Principal().getName()), + publicKey.getSerialNumber(), + AlgorithmId.get(DIGEST_ALGORITHM), + AlgorithmId.get(privateKey.getAlgorithm()), + signature.sign()); + + PKCS7 pkcs7 = new PKCS7( + new AlgorithmId[] { AlgorithmId.get(DIGEST_ALGORITHM) }, + new ContentInfo(ContentInfo.DATA_OID, null), + new X509Certificate[] { publicKey }, + new SignerInfo[] { signerInfo }); + + pkcs7.encodeSignedData(mOutputJar); + } +} diff --git a/third_party/java/apkbuilder/java/com/android/utils/GrabProcessOutput.java b/third_party/java/apkbuilder/java/com/android/utils/GrabProcessOutput.java new file mode 100644 index 00000000000000..d5cd78b4ad79c8 --- /dev/null +++ b/third_party/java/apkbuilder/java/com/android/utils/GrabProcessOutput.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2012 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.utils; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.google.common.io.Closeables; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class GrabProcessOutput { + + public enum Wait { + /** + * Doesn't wait for the exec to complete. + * This still monitors the output but does not wait for the process to finish. + * In this mode the process return code is unknown and always 0. + */ + ASYNC, + /** + * This waits for the process to finish. + * In this mode, {@link GrabProcessOutput#grabProcessOutput} returns the + * error code from the process. + * In some rare cases and depending on the OS, the process might not have + * finished dumping data into stdout/stderr. + *

+ * Use this when you don't particularly care for the output but instead + * care for the return code of the executed process. + */ + WAIT_FOR_PROCESS, + /** + * This waits for the process to finish and for the stdout/stderr + * threads to complete. + * In this mode, {@link GrabProcessOutput#grabProcessOutput} returns the + * error code from the process. + *

+ * Use this one when capturing all the output from the process is important. + */ + WAIT_FOR_READERS, + } + + public interface IProcessOutput { + /** + * Processes an stdout message line. + * @param line The stdout message line. Null when the reader reached the end of stdout. + */ + public void out(@Nullable String line); + /** + * Processes an stderr message line. + * @param line The stderr message line. Null when the reader reached the end of stderr. + */ + public void err(@Nullable String line); + } + + /** + * Get the stderr/stdout outputs of a process and return when the process is done. + * Both must be read or the process will block on windows. + * + * @param process The process to get the output from. + * @param output Optional object to capture stdout/stderr. + * Note that on Windows capturing the output is not optional. If output is null + * the stdout/stderr will be captured and discarded. + * @param waitMode Whether to wait for the process and/or the readers to finish. + * @return the process return code. + * @throws InterruptedException if {@link Process#waitFor()} was interrupted. + */ + public static int grabProcessOutput( + @NonNull final Process process, + Wait waitMode, + @Nullable final IProcessOutput output) throws InterruptedException { + // read the lines as they come. if null is returned, it's + // because the process finished + Thread threadErr = new Thread("stderr") { + @Override + public void run() { + // create a buffer to read the stderr output + InputStream is = process.getErrorStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader errReader = new BufferedReader(isr); + + try { + while (true) { + String line = errReader.readLine(); + if (output != null) { + output.err(line); + } + if (line == null) { + break; + } + } + } catch (IOException e) { + // do nothing. + } finally { + try { + Closeables.close(is, true /* swallowIOException */); + } catch (IOException e) { + // cannot happen + } + try { + Closeables.close(isr, true /* swallowIOException */); + } catch (IOException e) { + // cannot happen + } + try { + Closeables.close(errReader, true /* swallowIOException */); + } catch (IOException e) { + // cannot happen + } + } + } + }; + + Thread threadOut = new Thread("stdout") { + @Override + public void run() { + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader outReader = new BufferedReader(isr); + + try { + while (true) { + String line = outReader.readLine(); + if (output != null) { + output.out(line); + } + if (line == null) { + break; + } + } + } catch (IOException e) { + // do nothing. + } finally { + try { + Closeables.close(is, true /* swallowIOException */); + } catch (IOException e) { + // cannot happen + } + try { + Closeables.close(isr, true /* swallowIOException */); + } catch (IOException e) { + // cannot happen + } + try { + Closeables.close(outReader, true /* swallowIOException */); + } catch (IOException e) { + // cannot happen + } + } + } + }; + + threadErr.start(); + threadOut.start(); + + if (waitMode == Wait.ASYNC) { + return 0; + } + + // it looks like on windows process#waitFor() can return + // before the thread have filled the arrays, so we wait for both threads and the + // process itself. + if (waitMode == Wait.WAIT_FOR_READERS) { + try { + threadErr.join(); + } catch (InterruptedException e) { + } + try { + threadOut.join(); + } catch (InterruptedException e) { + } + } + + // get the return code from the process + return process.waitFor(); + } +}