Merge "Correct some dependencies around zip packages"
diff --git a/core/Makefile b/core/Makefile
index 5c8153a..83a008c 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1875,6 +1875,17 @@
@# Include the build type in META/misc_info.txt so the server can easily differentiate production builds.
$(hide) echo "build_type=$(TARGET_BUILD_VARIANT)" >> $(zip_root)/META/misc_info.txt
$(hide) echo "ab_update=true" >> $(zip_root)/META/misc_info.txt
+ifdef BRILLO_VENDOR_PARTITIONS
+ $(hide) mkdir -p $(zip_root)/VENDOR_IMAGES
+ $(hide) for f in $(BRILLO_VENDOR_PARTITIONS); do \
+ pair1="$$(echo $$f | awk -F':' '{print $$1}')"; \
+ pair2="$$(echo $$f | awk -F':' '{print $$2}')"; \
+ src=$${pair1}/$${pair2}; \
+ dest=$(zip_root)/VENDOR_IMAGES/$${pair2}; \
+ mkdir -p $$(dirname "$${dest}"); \
+ $(ACP) $${src} $${dest}; \
+ done;
+endif
ifdef OSRELEASED_DIRECTORY
$(hide) $(ACP) $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/product_id $(zip_root)/META/product_id.txt
$(hide) $(ACP) $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/product_version $(zip_root)/META/product_version.txt
diff --git a/core/clang/tidy.mk b/core/clang/tidy.mk
index e61b878..019e6f0 100644
--- a/core/clang/tidy.mk
+++ b/core/clang/tidy.mk
@@ -15,22 +15,25 @@
#
# Most Android source files are not clang-tidy clean yet.
-# Global tidy checks include only google* minus google-readability*.
+# Global tidy checks include only google* and misc-macro-parentheses,
+# but not google-readability*.
DEFAULT_GLOBAL_TIDY_CHECKS := \
- -*,google*,-google-readability*
+ -*,google*,-google-readability*,misc-macro-parentheses
-# Disable google style rules usually not followed by external projects.
+# Disable style rules usually not followed by external projects.
# Every word in DEFAULT_LOCAL_TIDY_CHECKS list has the following format:
# <local_path_prefix>:,<tidy-check-pattern>
# The tidy-check-patterns of all matching local_path_prefixes will be used.
# For example, external/google* projects will have:
# ,-google-build-using-namespace,-google-explicit-constructor
-# ,-google-runtime-int,google-runtime-int
-# where google-runtime-int is enabled at the end.
+# ,-google-runtime-int,-misc-macro-parentheses,
+# ,google-runtime-int,misc-macro-parentheses
+# where google-runtime-int and misc-macro-parentheses are enabled at the end.
DEFAULT_LOCAL_TIDY_CHECKS := \
external/:,-google-build-using-namespace \
external/:,-google-explicit-constructor,-google-runtime-int \
- external/google:,google-runtime-int \
+ external/:,-misc-macro-parentheses \
+ external/google:,google-runtime-int,misc-macro-parentheses \
external/webrtc/:,google-runtime-int \
hardware/qcom:,-google-build-using-namespace \
hardware/qcom:,-google-explicit-constructor,-google-runtime-int \
diff --git a/core/combo/arch/arm/armv7-a-neon.mk b/core/combo/arch/arm/armv7-a-neon.mk
index 5d5b050..5517a79 100644
--- a/core/combo/arch/arm/armv7-a-neon.mk
+++ b/core/combo/arch/arm/armv7-a-neon.mk
@@ -31,6 +31,11 @@
arch_variant_ldflags := \
-Wl,--no-fix-cortex-a8
else
+ifeq ($(strip $(TARGET_$(combo_2nd_arch_prefix)CPU_VARIANT)),cortex-a9)
+ arch_variant_cflags := -march=armv7-a
+ arch_variant_ldflags := \
+ -Wl,--no-fix-cortex-a8
+else
arch_variant_cflags := -march=armv7-a
# Generic ARM might be a Cortex A8 -- better safe than sorry
arch_variant_ldflags := \
@@ -38,6 +43,7 @@
endif
endif
endif
+endif
ifeq (true,$(local_arch_has_lpae))
# Fake an ARM compiler flag as these processors support LPAE which GCC/clang
diff --git a/core/combo/arch/x86/x86_64.mk b/core/combo/arch/x86/x86_64.mk
new file mode 100644
index 0000000..620fbd8
--- /dev/null
+++ b/core/combo/arch/x86/x86_64.mk
@@ -0,0 +1,18 @@
+# This file is used as the second (32-bit) architecture when building a generic
+# x86_64 64-bit platform image. (full_x86_64-eng / sdk_x86_64-eng)
+#
+# The generic 'x86' variant cannot be used, since it resets some flags used
+# by the 'x86_64' variant.
+
+ARCH_X86_HAVE_SSSE3 := true
+ARCH_X86_HAVE_MOVBE := false # Only supported on Atom.
+ARCH_X86_HAVE_POPCNT := true
+ARCH_X86_HAVE_SSE4 := true
+ARCH_X86_HAVE_SSE4_1 := true
+ARCH_X86_HAVE_SSE4_2 := true
+
+
+# Some intrinsic functions used by libcxx only exist for prescott or newer CPUs.
+arch_variant_cflags := \
+ -march=prescott \
+
diff --git a/target/board/generic_x86_64/BoardConfig.mk b/target/board/generic_x86_64/BoardConfig.mk
index 783fc77..0946243 100755
--- a/target/board/generic_x86_64/BoardConfig.mk
+++ b/target/board/generic_x86_64/BoardConfig.mk
@@ -13,7 +13,7 @@
TARGET_2ND_CPU_ABI := x86
TARGET_2ND_ARCH := x86
-TARGET_2ND_ARCH_VARIANT := x86
+TARGET_2ND_ARCH_VARIANT := x86_64
TARGET_USES_64_BIT_BINDER := true
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/ApkSignerEngine.java b/tools/apksigner/core/src/com/android/apksigner/core/ApkSignerEngine.java
new file mode 100644
index 0000000..36f2a08
--- /dev/null
+++ b/tools/apksigner/core/src/com/android/apksigner/core/ApkSignerEngine.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apksigner.core;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.SignatureException;
+import java.util.List;
+
+import com.android.apksigner.core.util.DataSink;
+import com.android.apksigner.core.util.DataSource;
+
+/**
+ * APK signing logic which is independent of how input and output APKs are stored, parsed, and
+ * generated.
+ *
+ * <p><h3>Operating Model</h3>
+ *
+ * The abstract operating model is that there is an input APK which is being signed, thus producing
+ * an output APK. In reality, there may be just an output APK being built from scratch, or the input APK and
+ * the output APK may be the same file. Because this engine does not deal with reading and writing
+ * files, it can handle all of these scenarios.
+ *
+ * <p>The engine is stateful and thus cannot be used for signing multiple APKs. However, once
+ * the engine signed an APK, the engine can be used to re-sign the APK after it has been modified.
+ * This may be more efficient than signing the APK using a new instance of the engine. See
+ * <a href="#incremental">Incremental Operation</a>.
+ *
+ * <p>In the engine's operating model, a signed APK is produced as follows.
+ * <ol>
+ * <li>JAR entries to be signed are output,</li>
+ * <li>JAR archive is signed using JAR signing, thus adding the so-called v1 signature to the
+ * output,</li>
+ * <li>JAR archive is signed using APK Signature Scheme v2, thus adding the so-called v2 signature
+ * to the output.</li>
+ * </ol>
+ *
+ * <p>The input APK may contain JAR entries which, depending on the engine's configuration, may or
+ * may not be output (e.g., existing signatures may need to be preserved or stripped) or which the
+ * engine will overwrite as part of signing. The engine thus offers {@link #inputJarEntry(String)}
+ * which tells the client whether the input JAR entry needs to be output. This avoids the need for
+ * the client to hard-code the aspects of APK signing which determine which parts of input must be
+ * ignored. Similarly, the engine offers {@link #inputApkSigningBlock(DataSource)} to help the
+ * client avoid dealing with preserving or stripping APK Signature Scheme v2 signature of the input
+ * APK.
+ *
+ * <p>To use the engine to sign an input APK (or a collection of JAR entries), follow these
+ * steps:
+ * <ol>
+ * <li>Obtain a new instance of the engine -- engine instances are stateful and thus cannot be used
+ * for signing multiple APKs.</li>
+ * <li>Locate the input APK's APK Signing Block and provide it to
+ * {@link #inputApkSigningBlock(DataSource)}.</li>
+ * <li>For each JAR entry in the input APK, invoke {@link #inputJarEntry(String)} to determine
+ * whether this entry should be output. The engine may request to inspect the entry.</li>
+ * <li>For each output JAR entry, invoke {@link #outputJarEntry(String)} which may request to
+ * inspect the entry.</li>
+ * <li>Once all JAR entries have been output, invoke {@link #outputJarEntries()} which may request
+ * that additional JAR entries are output. These entries comprise the output APK's JAR
+ * signature.</li>
+ * <li>Locate the ZIP Central Directory and ZIP End of Central Directory sections in the output and
+ * invoke {@link #outputZipSections(DataSource, DataSource, DataSource)} which may request that
+ * an APK Signature Block is inserted before the ZIP Central Directory. The block contains the
+ * output APK's APK Signature Scheme v2 signature.</li>
+ * <li>Invoke {@link #outputDone()} to signal that the APK was output in full. The engine will
+ * confirm that the output APK is signed.</li>
+ * <li>Invoke {@link #close()} to signal that the engine will no longer be used. This lets the
+ * engine free any resources it no longer needs.
+ * </ol>
+ *
+ * <p>Some invocations of the engine may provide the client with a task to perform. The client is
+ * expected to perform all requested tasks before proceeding to the next stage of signing. See
+ * documentation of each method about the deadlines for performing the tasks requested by the
+ * method.
+ *
+ * <p><h3 id="incremental">Incremental Operation</h3></a>
+ *
+ * The engine supports incremental operation where a signed APK is produced, then modified and
+ * re-signed. This may be useful for IDEs, where an app is frequently re-signed after small changes
+ * by the developer. Re-signing may be more efficient than signing from scratch.
+ *
+ * <p>To use the engine in incremental mode, keep notifying the engine of changes to the APK through
+ * {@link #inputApkSigningBlock(DataSource)}, {@link #inputJarEntry(String)},
+ * {@link #inputJarEntryRemoved(String)}, {@link #outputJarEntry(String)},
+ * and {@link #outputJarEntryRemoved(String)}, perform the tasks requested by the engine through
+ * these methods, and, when a new signed APK is desired, run through steps 5 onwards to re-sign the
+ * APK.
+ *
+ * <p><h3>Output-only Operation</h3>
+ *
+ * The engine's abstract operating model consists of an input APK and an output APK. However, it is
+ * possible to use the engine in output-only mode where the engine's {@code input...} methods are
+ * not invoked. In this mode, the engine has less control over output because it cannot request that
+ * some JAR entries are not output. Nevertheless, the engine will attempt to make the output APK
+ * signed and will report an error if cannot do so.
+ */
+public interface ApkSignerEngine extends Closeable {
+
+ /**
+ * Indicates to this engine that the input APK contains the provided APK Signing Block. The
+ * block may contain signatures of the input APK, such as APK Signature Scheme v2 signatures.
+ *
+ * @param apkSigningBlock APK signing block of the input APK. The provided data source is
+ * guaranteed to not be used by the engine after this method terminates.
+ *
+ * @throws IllegalStateException if this engine is closed
+ */
+ void inputApkSigningBlock(DataSource apkSigningBlock) throws IllegalStateException;
+
+ /**
+ * Indicates to this engine that the specified JAR entry was encountered in the input APK.
+ *
+ * <p>When an input entry is updated/changed, it's OK to not invoke
+ * {@link #inputJarEntryRemoved(String)} before invoking this method.
+ *
+ * @return instructions about how to proceed with this entry
+ *
+ * @throws IllegalStateException if this engine is closed
+ */
+ InputJarEntryInstructions inputJarEntry(String entryName) throws IllegalStateException;
+
+ /**
+ * Indicates to this engine that the specified JAR entry was output.
+ *
+ * <p>It is unnecessary to invoke this method for entries added to output by this engine (e.g.,
+ * requested by {@link #outputJarEntries()}) provided the entries were output with exactly the
+ * data requested by the engine.
+ *
+ * <p>When an already output entry is updated/changed, it's OK to not invoke
+ * {@link #outputJarEntryRemoved(String)} before invoking this method.
+ *
+ * @return request to inspect the entry or {@code null} if the engine does not need to inspect
+ * the entry. The request must be fulfilled before {@link #outputJarEntries()} is
+ * invoked.
+ *
+ * @throws IllegalStateException if this engine is closed
+ */
+ InspectJarEntryRequest outputJarEntry(String entryName) throws IllegalStateException;
+
+ /**
+ * Indicates to this engine that the specified JAR entry was removed from the input. It's safe
+ * to invoke this for entries for which {@link #inputJarEntry(String)} hasn't been invoked.
+ *
+ * @return output policy of this JAR entry. The policy indicates how this input entry affects
+ * the output APK. The client of this engine should use this information to determine
+ * how the removal of this input APK's JAR entry affects the output APK.
+ *
+ * @throws IllegalStateException if this engine is closed
+ */
+ InputJarEntryInstructions.OutputPolicy inputJarEntryRemoved(String entryName)
+ throws IllegalStateException;
+
+ /**
+ * Indicates to this engine that the specified JAR entry was removed from the output. It's safe
+ * to invoke this for entries for which {@link #outputJarEntry(String)} hasn't been invoked.
+ *
+ * @throws IllegalStateException if this engine is closed
+ */
+ void outputJarEntryRemoved(String entryName) throws IllegalStateException;
+
+ /**
+ * Indicates to this engine that all JAR entries have been output.
+ *
+ *
+ * @return request to add JAR signature to the output or {@code null} if there is no need to add
+ * a JAR signature. The request will contain additional JAR entries to be output. The
+ * request must be fulfilled before
+ * {@link #outputZipSections(DataSource, DataSource, DataSource)} is invoked.
+ *
+ * @throws InvalidKeyException if a signature could not be generated because a signing key is
+ * not suitable for generating the signature
+ * @throws SignatureException if an error occurred while generating the JAR signature
+ * @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR
+ * entries, or if the engine is closed
+ */
+ OutputJarSignatureRequest outputJarEntries() throws InvalidKeyException, SignatureException;
+
+ /**
+ * Indicates to this engine that the ZIP sections comprising the output APK have been output.
+ *
+ * <p>The provided data sources are guaranteed to not be used by the engine after this method
+ * terminates.
+ *
+ * @param zipEntries the section of ZIP archive containing Local File Header records and data of
+ * the ZIP entries. In a well-formed archive, this section starts at the start of the
+ * archive and extends all the way to the ZIP Central Directory.
+ * @param zipCentralDirectory ZIP Central Directory section
+ * @param zipEocd ZIP End of Central Directory (EoCD) record
+ *
+ * @return request to add an APK Signing Block to the output or {@code null} if the output must
+ * not contain an APK Signing Block. The request must be fulfilled before
+ * {@link #outputDone()} is invoked.
+ *
+ * @throws IOException if an I/O error occurs while reading the provided ZIP sections
+ * @throws InvalidKeyException if a signature could not be generated because a signing key is
+ * not suitable for generating the signature
+ * @throws SignatureException if an error occurred while generating the APK's signature
+ * @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR
+ * entries or to output JAR signature, or if the engine is closed
+ */
+ OutputApkSigningBlockRequest outputZipSections(
+ DataSource zipEntries,
+ DataSource zipCentralDirectory,
+ DataSource zipEocd) throws IOException, InvalidKeyException, SignatureException;
+
+ /**
+ * Indicates to this engine that the signed APK was output.
+ *
+ * <p>This does not change the output APK. The method helps the client confirm that the current
+ * output is signed.
+ *
+ * @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR
+ * entries or to output signatures, or if the engine is closed
+ */
+ void outputDone() throws IllegalStateException;
+
+ /**
+ * Indicates to this engine that it will no longer be used. Invoking this on an already closed
+ * engine is OK.
+ *
+ * <p>This does not change the output APK. For example, if the output APK is not yet fully
+ * signed, it will remain so after this method terminates.
+ */
+ @Override
+ void close();
+
+ /**
+ * Instructions about how to handle an input APK's JAR entry.
+ *
+ * <p>The instructions indicate whether to output the entry (see {@link #getOutputPolicy()}) and
+ * may contain a request to inspect the entry (see {@link #getInspectJarEntryRequest()}), in
+ * which case the request must be fulfilled before {@link ApkSignerEngine#outputJarEntries()} is
+ * invoked.
+ */
+ public static class InputJarEntryInstructions {
+ private final OutputPolicy mOutputPolicy;
+ private final InspectJarEntryRequest mInspectJarEntryRequest;
+
+ /**
+ * Constructs a new {@code InputJarEntryInstructions} instance with the provided entry
+ * output policy and without a request to inspect the entry.
+ */
+ public InputJarEntryInstructions(OutputPolicy outputPolicy) {
+ this(outputPolicy, null);
+ }
+
+ /**
+ * Constructs a new {@code InputJarEntryInstructions} instance with the provided entry
+ * output mode and with the provided request to inspect the entry.
+ *
+ * @param inspectJarEntryRequest request to inspect the entry or {@code null} if there's no
+ * need to inspect the entry.
+ */
+ public InputJarEntryInstructions(
+ OutputPolicy outputPolicy,
+ InspectJarEntryRequest inspectJarEntryRequest) {
+ mOutputPolicy = outputPolicy;
+ mInspectJarEntryRequest = inspectJarEntryRequest;
+ }
+
+ /**
+ * Returns the output policy for this entry.
+ */
+ public OutputPolicy getOutputPolicy() {
+ return mOutputPolicy;
+ }
+
+ /**
+ * Returns the request to inspect the JAR entry or {@code null} if there is no need to
+ * inspect the entry.
+ */
+ public InspectJarEntryRequest getInspectJarEntryRequest() {
+ return mInspectJarEntryRequest;
+ }
+
+ /**
+ * Output policy for an input APK's JAR entry.
+ */
+ public static enum OutputPolicy {
+ /** Entry must not be output. */
+ SKIP,
+
+ /** Entry should be output. */
+ OUTPUT,
+
+ /** Entry will be output by the engine. The client can thus ignore this input entry. */
+ OUTPUT_BY_ENGINE,
+ }
+ }
+
+ /**
+ * Request to inspect the specified JAR entry.
+ *
+ * <p>The entry's uncompressed data must be provided to the data sink returned by
+ * {@link #getDataSink()}. Once the entry's data has been provided to the sink, {@link #done()}
+ * must be invoked.
+ */
+ interface InspectJarEntryRequest {
+
+ /**
+ * Returns the data sink into which the entry's uncompressed data should be sent.
+ */
+ DataSink getDataSink();
+
+ /**
+ * Indicates that entry's data has been provided in full.
+ */
+ void done();
+
+ /**
+ * Returns the name of the JAR entry.
+ */
+ String getEntryName();
+ }
+
+ /**
+ * Request to add JAR signature (aka v1 signature) to the output APK.
+ *
+ * <p>Entries listed in {@link #getAdditionalJarEntries()} must be added to the output APK after
+ * which {@link #done()} must be invoked.
+ */
+ interface OutputJarSignatureRequest {
+
+ /**
+ * Returns JAR entries that must be added to the output APK.
+ */
+ List<JarEntry> getAdditionalJarEntries();
+
+ /**
+ * Indicates that the JAR entries contained in this request were added to the output APK.
+ */
+ void done();
+
+ /**
+ * JAR entry.
+ */
+ public static class JarEntry {
+ private final String mName;
+ private final byte[] mData;
+
+ /**
+ * Constructs a new {@code JarEntry} with the provided name and data.
+ *
+ * @param data uncompressed data of the entry. Changes to this array will not be
+ * reflected in {@link #getData()}.
+ */
+ public JarEntry(String name, byte[] data) {
+ mName = name;
+ mData = data.clone();
+ }
+
+ /**
+ * Returns the name of this ZIP entry.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the uncompressed data of this JAR entry.
+ */
+ public byte[] getData() {
+ return mData.clone();
+ }
+ }
+ }
+
+ /**
+ * Request to add the specified APK Signing Block to the output APK. APK Signature Scheme v2
+ * signature(s) of the APK are contained in this block.
+ *
+ * <p>The APK Signing Block returned by {@link #getApkSigningBlock()} must be placed into the
+ * output APK such that the block is immediately before the ZIP Central Directory, the offset of
+ * ZIP Central Directory in the ZIP End of Central Directory record must be adjusted
+ * accordingly, and then {@link #done()} must be invoked.
+ *
+ * <p>If the output contains an APK Signing Block, that block must be replaced by the block
+ * contained in this request.
+ */
+ interface OutputApkSigningBlockRequest {
+
+ /**
+ * Returns the APK Signing Block.
+ */
+ byte[] getApkSigningBlock();
+
+ /**
+ * Indicates that the APK Signing Block was output as requested.
+ */
+ void done();
+ }
+}
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index f98a281..9e44263 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -395,8 +395,9 @@
banner("partition-table")
AddPartitionTable(output_zip)
- # For devices using A/B update, copy over images from RADIO/ to IMAGES/ and
- # make sure we have all the needed images ready under IMAGES/.
+ # For devices using A/B update, copy over images from RADIO/ and/or
+ # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed
+ # images ready under IMAGES/. All images should have '.img' as extension.
ab_partitions = os.path.join(OPTIONS.input_tmp, "META", "ab_partitions.txt")
if os.path.exists(ab_partitions):
with open(ab_partitions, 'r') as f:
@@ -404,9 +405,17 @@
for line in lines:
img_name = line.strip() + ".img"
img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
+ img_vendor_dir = os.path.join(
+ OPTIONS.input_tmp, "VENDOR_IMAGES")
if os.path.exists(img_radio_path):
common.ZipWrite(output_zip, img_radio_path,
os.path.join("IMAGES", img_name))
+ else:
+ for root, _, files in os.walk(img_vendor_dir):
+ if img_name in files:
+ common.ZipWrite(output_zip, os.path.join(root, img_name),
+ os.path.join("IMAGES", img_name))
+ break
# Zip spec says: All slashes MUST be forward slashes.
img_path = 'IMAGES/' + img_name
diff --git a/tools/rgb2565/to565.c b/tools/rgb2565/to565.c
index abf9cdb..94d62ef 100644
--- a/tools/rgb2565/to565.c
+++ b/tools/rgb2565/to565.c
@@ -65,11 +65,11 @@
out = to565(rb, gb, bb);
write(1, &out, 2);
-#define apply_error(ch) { \
- next_error[(i-1)*3+ch] += e * 3 / 16; \
- next_error[(i)*3+ch] += e * 5 / 16; \
- next_error[(i+1)*3+ch] += e * 1 / 16; \
- error[(i+1)*3+ch] += e - ((e*1/16) + (e*3/16) + (e*5/16)); \
+#define apply_error(ch) { \
+ next_error[(i-1)*3+(ch)] += e * 3 / 16; \
+ next_error[(i)*3+(ch)] += e * 5 / 16; \
+ next_error[(i+1)*3+(ch)] += e * 1 / 16; \
+ error[(i+1)*3+(ch)] += e - ((e*1/16) + (e*3/16) + (e*5/16)); \
}
e = r - from565_r(out);
diff --git a/tools/warn.py b/tools/warn.py
index 14b3f48..135cd51 100755
--- a/tools/warn.py
+++ b/tools/warn.py
@@ -1,12 +1,20 @@
#!/usr/bin/env python
# This file uses the following encoding: utf-8
+import argparse
import sys
import re
-if len(sys.argv) == 1:
- print 'usage: ' + sys.argv[0] + ' <build.log>'
- sys.exit()
+parser = argparse.ArgumentParser(description='Convert a build log into HTML')
+parser.add_argument('--url',
+ help='Root URL of an Android source code tree prefixed '
+ 'before files in warnings')
+parser.add_argument('--separator',
+ help='Separator between the end of a URL and the line '
+ 'number argument. e.g. #')
+parser.add_argument(dest='buildlog', metavar='build.log',
+ help='Path to build.log file')
+args = parser.parse_args()
# if you add another level, don't forget to give it a color below
class severity:
@@ -303,6 +311,89 @@
{ 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
'description':'Java: Unchecked conversion',
'patterns':[r".*: warning: \[unchecked\] unchecked conversion"] },
+
+ # Warnings from error prone.
+ { 'category':'java', 'severity':severity.LOW, 'members':[], 'option':'',
+ 'description':'Java: Long literal suffix',
+ 'patterns':[r".*: warning: \[LongLiteralLowerCaseSuffix\] Prefer 'L' to 'l' for the suffix to long literal"] },
+ { 'category':'java', 'severity':severity.LOW, 'members':[], 'option':'',
+ 'description':'Java: Missing @Deprecated',
+ 'patterns':[r".*: warning: \[DepAnn\] Deprecated item is not annotated with @Deprecated"] },
+ { 'category':'java', 'severity':severity.LOW, 'members':[], 'option':'',
+ 'description':'Java: Use of deprecated member',
+ 'patterns':[r".*: warning: \[deprecation\] .+ in .+ has been deprecated"] },
+ { 'category':'java', 'severity':severity.LOW, 'members':[], 'option':'',
+ 'description':'Java: Missing hashCode method',
+ 'patterns':[r".*: warning: \[EqualsHashCode\] Classes that override equals should also override hashCode."] },
+ { 'category':'java', 'severity':severity.LOW, 'members':[], 'option':'',
+ 'description':'Java: Hashtable contains is a legacy method',
+ 'patterns':[r".*: warning: \[HashtableContains\] contains\(\) is a legacy method that is equivalent to containsValue\(\)"] },
+ { 'category':'java', 'severity':severity.LOW, 'members':[], 'option':'',
+ 'description':'Java: Type parameter used only for return type',
+ 'patterns':[r".*: warning: \[TypeParameterUnusedInFormals\] Declaring a type parameter that is only used in the return type is a misuse of generics: operations on the type parameter are unchecked, it hides unsafe casts at invocations of the method, and it interacts badly with method overload resolution."] },
+
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: reference equality used on arrays',
+ 'patterns':[r".*: warning: \[ArrayEquals\] Reference equality used to compare arrays"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: hashcode used on array',
+ 'patterns':[r".*: warning: \[ArrayHashCode\] hashcode method on array does not hash array contents"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: toString used on an array',
+ 'patterns':[r".*: warning: \[ArrayToStringConcatenation\] Implicit toString used on an array \(String \+ Array\)",
+ r".*: warning: \[ArrayToString\] Calling toString on an array does not provide useful information"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Exception created but not thrown',
+ 'patterns':[r".*: warning: \[DeadException\] Exception created but not thrown"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Return or throw from a finally',
+ 'patterns':[r".*: warning: \[Finally\] If you return or throw from a finally, then values returned or thrown from the try-catch block will be ignored. Consider using try-with-resources instead."] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Erroneous use of @GuardedBy',
+ 'patterns':[r".*: warning: \[GuardedByChecker\] This access should be guarded by '.+'; instead found: '.+'",
+ r".*: warning: \[GuardedByChecker\] This access should be guarded by '.+', which is not currently held"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Mislabeled Android string',
+ 'patterns':[r".*: warning: \[MislabeledAndroidString\] .+ is not \".+\" but \".+\"; prefer .+ for clarity"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Missing cases in enum switch',
+ 'patterns':[r".*: warning: \[MissingCasesInEnumSwitch\] Non-exhaustive switch, expected cases for: .+"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Multiple top-level classes (inhibits bug analysis)',
+ 'patterns':[r".*: warning: \[MultipleTopLevelClasses\] Expected at most one top-level class declaration, instead found: .+"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: equals method doesn\'t override Object.equals',
+ 'patterns':[r".*: warning: \[NonOverridingEquals\] equals method doesn't override Object\.equals.*"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Update of a volatile variable is non-atomic',
+ 'patterns':[r".*: warning: \[NonAtomicVolatileUpdate\] This update of a volatile variable is non-atomic"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Return value ignored',
+ 'patterns':[r".*: warning: \[ReturnValueIgnored\] Return value of this method must be used",
+ r".*: warning: \[RectIntersectReturnValueIgnored\] Return value of android.graphics.Rect.intersect\(\) must be checked"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Static variable accessed from an object instance',
+ 'patterns':[r".*: warning: \[StaticAccessedFromInstance\] Static (method|variable) .+ should not be accessed from an object instance; instead use .+"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Static guarded by instance',
+ 'patterns':[r".*: warning: \[StaticGuardedByInstance\] Write to static variable should not be guarded by instance lock '.+'"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: String reference equality',
+ 'patterns':[r".*: warning: \[StringEquality\] String comparison using reference equality instead of value equality"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Synchronization on non-final field',
+ 'patterns':[r".*: warning: \[SynchronizeOnNonFinalField\] Synchronizing on non-final fields is not safe: if the field is ever updated, different threads may end up locking on different objects."] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Catch masks fail or assert',
+ 'patterns':[r".*: warning: \[TryFailThrowable\] Catching Throwable/Error masks failures from fail\(\) or assert\*\(\) in the try block"] },
+ { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'',
+ 'description':'Java: Wait not in a loop',
+ 'patterns':[r".*: warning: \[WaitNotInLoop\] Because of spurious wakeups, a?wait.*\(.*\) must always be called in a loop"] },
+
+ { 'category':'java', 'severity':severity.UNKNOWN, 'members':[], 'option':'',
+ 'description':'Java: Unclassified/unrecognized warnings',
+ 'patterns':[r".*: warning: \[.+\] .+"] },
+
{ 'category':'aapt', 'severity':severity.MEDIUM, 'members':[], 'option':'',
'description':'aapt: No default translation',
'patterns':[r".*: warning: string '.+' has no default translation in .*"] },
@@ -698,7 +789,7 @@
output('<tr bgcolor="' + row_colors[cur_row_color] + '"><td colspan="2">',)
cur_row_color = 1 - cur_row_color
output(text,)
- output('</td></tr>')
+ output('</td></tr>\n')
def begintable(text, backgroundcolor, extraanchor):
global anchor
@@ -796,6 +887,19 @@
if tablestarted:
endtable()
+def warningwithurl(line):
+ if not args.url:
+ return line
+ m = re.search( r'^([^ :]+):(\d+):(.+)', line, re.M|re.I)
+ if not m:
+ return line
+ filepath = m.group(1)
+ linenumber = m.group(2)
+ warning = m.group(3)
+ if args.separator:
+ return '<a href="' + args.url + '/' + filepath + args.separator + linenumber + '">' + filepath + ':' + linenumber + '</a>:' + warning
+ else:
+ return '<a href="' + args.url + '/' + filepath + '">' + filepath + '</a>:' + linenumber + ':' + warning
# dump a category, provided it is not marked as 'SKIP' and has more than 0 occurrences
def dumpcategory(cat):
@@ -805,7 +909,7 @@
header[1:1] = [' (related option: ' + cat['option'] +')']
begintable(header, colorforseverity(cat['severity']), cat['anchor'])
for i in cat['members']:
- tablerow(i)
+ tablerow(warningwithurl(i))
endtable()
@@ -835,7 +939,7 @@
for pat in i['patterns']:
i['compiledpatterns'].append(re.compile(pat))
-infile = open(sys.argv[1], 'r')
+infile = open(args.buildlog, 'r')
warnings = []
platformversion = 'unknown'
@@ -874,6 +978,9 @@
# dump the html output to stdout
dumphtmlprologue('Warnings for ' + platformversion + ' - ' + targetproduct + ' - ' + targetvariant)
dumpstats()
+# sort table based on number of members once dumpstats has deduplicated the
+# members.
+warnpatterns.sort(reverse=True, key=lambda i: len(i['members']))
dumptoc()
dumpseverity(severity.FIXMENOW)
dumpseverity(severity.HIGH)
@@ -883,4 +990,3 @@
dumpseverity(severity.HARMLESS)
dumpseverity(severity.UNKNOWN)
dumpfixed()
-