diff options
95 files changed, 5774 insertions, 1181 deletions
diff --git a/buildspec.mk.default b/buildspec.mk.default index 659b9f6..6303efc 100644 --- a/buildspec.mk.default +++ b/buildspec.mk.default @@ -21,6 +21,30 @@ # "buildspec.mk" should never be checked in to source control. ###################################################################### +# Choose a product to build for. Look in the products directory for ones +# that work. +ifndef TARGET_PRODUCT +#TARGET_PRODUCT:=generic +endif + +# Choose a variant to build. If you don't pick one, the default is eng. +# User is what we ship. Userdebug is that, with a few flags turned on +# for debugging. Eng has lots of extra tools for development. +ifndef TARGET_BUILD_VARIANT +#TARGET_BUILD_VARIANT:=user +#TARGET_BUILD_VARIANT:=userdebug +#TARGET_BUILD_VARIANT:=eng +endif + +# Choose additional targets to always install, even when building +# minimal targets like "make droid". This takes simple target names +# like "Browser" or "MyApp", the names used by LOCAL_MODULE or +# LOCAL_PACKAGE_NAME. Modules listed here will always be installed in +# /system, even if they'd usually go in /data. +ifndef CUSTOM_MODULES +#CUSTOM_MODULES:= +endif + # Uncomment this if you want the simulator, otherwise, build for arm ifndef TARGET_SIMULATOR #TARGET_SIMULATOR:=true @@ -53,21 +77,6 @@ endif #HOST_CUSTOM_DEBUG_CFLAGS:= #TARGET_CUSTOM_DEBUG_CFLAGS:= -# Choose a product to build for. Look in the products directory for ones -# that work. -ifndef TARGET_PRODUCT -#TARGET_PRODUCT:=generic -endif - -# Choose additional targets to always install, even when building -# minimal targets like "make droid". This takes simple target names -# like "Browser" or "MyApp", the names used by LOCAL_MODULE or -# LOCAL_PACKAGE_NAME. Modules listed here will always be installed in -# /system, even if they'd usually go in /data. -ifndef CUSTOM_MODULES -#CUSTOM_MODULES:= -endif - # Choose additional locales, like "en_US" or "it_IT", to add to any # built product. Any locales that appear in CUSTOM_LOCALES but not in # the locale list for the selected product will be added to the end @@ -91,10 +100,16 @@ ifndef NO_FALLBACK_FONT #NO_FALLBACK_FONT:=true endif -# To enabled instrumentation in webcore based apps like gmail and +# To enable instrumentation in webcore based apps like gmail and # the browser, define WEBCORE_INSTRUMENTATION:=true +ifndef WEBCORE_INSTRUMENTATION #WEBCORE_INSTRUMENTATION:=true -#endif +endif + +# To enable SVG in webcore define ENABLE_SVG:=true +ifndef ENABLE_SVG +#ENABLE_SVG:=true +endif # when the build system changes such that this file must be updated, this # variable will be changed. After you have modified this file with the new diff --git a/cleanspec.mk b/cleanspec.mk index c244c46..7cf24de 100644 --- a/cleanspec.mk +++ b/cleanspec.mk @@ -18,7 +18,7 @@ # WHEN DOING SO, DELETE ANY "add-clean-step" ENTRIES THAT HAVE PILED UP. # ********************************************************************** # -INTERNAL_CLEAN_BUILD_VERSION := 2 +INTERNAL_CLEAN_BUILD_VERSION := 3 # # *********************************************************************** # Do not touch INTERNAL_CLEAN_BUILD_VERSION if you've added a clean step! @@ -31,7 +31,7 @@ INTERNAL_CLEAN_BUILD_VERSION := 2 # # E.g.: # $(call add-clean-step, touch -c external/sqlite/sqlite3.h) -# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/external/zlib/) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) # # Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with # files that are missing or have been moved. @@ -54,38 +54,61 @@ INTERNAL_CLEAN_BUILD_VERSION := 2 #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) - -$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/etc/NOTICE.html) -# Remove generated java files after CL 126153 -$(call add-clean-step, find $(OUT_DIR) -type f -name "*.java" -print0 | xargs -0 rm -f) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.google.android.datamessaging_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sholes/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/pvasflocal.cfg) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sholes) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sapphire/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates/led) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/mountd) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/mountd.conf) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Browser_intermediates) -$(call add-clean-step, rm -f vendor/google/apps/Talk/res/drawable/%*) -$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/SHARED_LIBRARIES/libandroid_runtime_intermediates/android_os_NetStat.o) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/Silence.ogg) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/notifications/Silence.ogg) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/passion/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.google.android.datamessaging_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libcrypto_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libssl_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/openssl_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sholes/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libjni_andpyime_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/share) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/bugreport) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/obj/target/common/obj/APPS/VoiceSearch_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/VoiceSearch_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libgps-rpc_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/pdsm_atl_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgps_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/system/app/Launcher.apk) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/bluetooth/) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sholes/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/com.amazon.mp3_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/com.amazon.mp3.apk) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PinyinIME_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.inputmethod.pinyin.lib_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PinyinIMEGoogleService_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.inputmethod.pinyin.lib_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PinyinIMEGoogleService_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj) -$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/tcpdump) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/location) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/openssl_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libv8_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/SHARED_LIBRARIES/lib?camera_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/STATIC_LIBRARIES/lib?camera_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libwebcore_intermediates) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/Silence.ogg) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/notifications/Silence.ogg) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/core/Makefile b/core/Makefile index 2c4d133..e704748 100644 --- a/core/Makefile +++ b/core/Makefile @@ -118,6 +118,7 @@ $(INSTALLED_BUILD_PROP_TARGET): $(BUILDINFO_SH) $(INTERNAL_BUILD_ID_MAKEFILE) PRODUCT_BRAND="$(PRODUCT_BRAND)" \ PRODUCT_DEFAULT_LANGUAGE="$(call default-locale-language,$(PRODUCT_LOCALES))" \ PRODUCT_DEFAULT_REGION="$(call default-locale-region,$(PRODUCT_LOCALES))" \ + PRODUCT_DEFAULT_WIFI_CHANNELS="$(PRODUCT_DEFAULT_WIFI_CHANNELS)" \ PRODUCT_MODEL="$(PRODUCT_MODEL)" \ PRODUCT_MANUFACTURER="$(PRODUCT_MANUFACTURER)" \ PRIVATE_BUILD_DESC="$(PRIVATE_BUILD_DESC)" \ @@ -299,7 +300,7 @@ else # TARGET_BOOTIMAGE_USE_EXT2 != true $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(call pretty,"Target boot image: $@") $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) --output $@ - $(hide) $(call assert-max-file-size,$@,$(BOARD_BOOTIMAGE_MAX_SIZE),raw) + $(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE),raw) endif # TARGET_BOOTIMAGE_USE_EXT2 else # TARGET_NO_KERNEL @@ -501,7 +502,7 @@ endif # Recovery image # If neither TARGET_NO_KERNEL nor TARGET_NO_RECOVERY are true -ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY))) +ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY) $(BUILD_TINY_ANDROID))) INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img @@ -577,7 +578,7 @@ $(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $(MINIGZIP) \ $(MKBOOTFS) $(TARGET_RECOVERY_ROOT_OUT) | $(MINIGZIP) > $(recovery_ramdisk) $(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) --output $@ @echo ----- Made recovery image -------- $@ - $(hide) $(call assert-max-file-size,$@,$(BOARD_RECOVERYIMAGE_MAX_SIZE),raw) + $(hide) $(call assert-max-image-size,$@,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE),raw) else INSTALLED_RECOVERYIMAGE_TARGET := @@ -662,7 +663,7 @@ endif $(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP) @echo "Install system fs image: $@" $(copy-file-to-target) - $(hide) $(call assert-max-file-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_MAX_SIZE),yaffs) + $(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE),yaffs) systemimage: $(INSTALLED_SYSTEMIMAGE) @@ -671,7 +672,7 @@ systemimage-nodeps snod: $(filter-out systemimage-nodeps snod,$(MAKECMDGOALS)) \ | $(INTERNAL_MKUSERFS) @echo "make $@: ignoring dependencies" $(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE)) - $(hide) $(call assert-max-file-size,$(INSTALLED_SYSTEMIMAGE),$(BOARD_SYSTEMIMAGE_MAX_SIZE),yaffs) + $(hide) $(call assert-max-image-size,$(INSTALLED_SYSTEMIMAGE),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE),yaffs) ####### ## system tarball @@ -708,7 +709,7 @@ define build-userdataimage-target $(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_DATA) $(call build-userimage-ext2-target,$(TARGET_OUT_DATA),$(INSTALLED_USERDATAIMAGE_TARGET),userdata,) - $(hide) $(call assert-max-file-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_MAX_SIZE),yaffs) + $(hide) $(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE),yaffs) endef else # TARGET_USERIMAGES_USE_EXT2 != true @@ -718,7 +719,7 @@ define build-userdataimage-target $(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)") @mkdir -p $(TARGET_OUT_DATA) $(hide) $(MKYAFFS2) -f $(TARGET_OUT_DATA) $(INSTALLED_USERDATAIMAGE_TARGET) - $(hide) $(call assert-max-file-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_MAX_SIZE),yaffs) + $(hide) $(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE),yaffs) endef endif # TARGET_USERIMAGES_USE_EXT2 @@ -813,6 +814,13 @@ $(BUILT_TARGET_FILES_PACKAGE): PRIVATE_OTA_TOOLS := $(built_ota_tools) $(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_API_VERSION := $(RECOVERY_API_VERSION) +ifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),) +# default to common dir for device vendor +$(BUILT_TARGET_FILES_PACKAGE): tool_extensions := $(TARGET_DEVICE_DIR)/../common +else +$(BUILT_TARGET_FILES_PACKAGE): tool_extensions := $(TARGET_RELEASETOOLS_EXTENSIONS) +endif + # Depending on the various images guarantees that the underlying # directories are up-to-date. $(BUILT_TARGET_FILES_PACKAGE): \ @@ -842,6 +850,9 @@ endif ifdef BOARD_KERNEL_CMDLINE $(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/RECOVERY/cmdline endif +ifdef BOARD_KERNEL_BASE + $(hide) echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/RECOVERY/base +endif @# Components of the boot image $(hide) mkdir -p $(zip_root)/BOOT $(hide) $(call package_files-copy-root, \ @@ -856,11 +867,12 @@ endif ifdef BOARD_KERNEL_CMDLINE $(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline endif -ifdef INSTALLED_RADIOIMAGE_TARGET - @# The radio image - $(hide) mkdir -p $(zip_root)/RADIO - $(hide) $(ACP) $(INSTALLED_RADIOIMAGE_TARGET) $(zip_root)/RADIO/image +ifdef BOARD_KERNEL_BASE + $(hide) echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/BOOT/base endif + $(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),\ + mkdir -p $(zip_root)/RADIO; \ + $(ACP) $(t) $(zip_root)/RADIO/$(notdir $(t));) @# Contents of the system image $(hide) $(call package_files-copy-root, \ $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM) @@ -878,10 +890,11 @@ endif $(hide) echo "$(PRODUCT_OTA_PUBLIC_KEYS)" > $(zip_root)/META/otakeys.txt $(hide) echo "$(PRIVATE_RECOVERY_API_VERSION)" > $(zip_root)/META/recovery-api-version.txt $(hide) echo "blocksize $(BOARD_FLASH_BLOCK_SIZE)" > $(zip_root)/META/imagesizes.txt - $(hide) echo "boot $(BOARD_BOOTIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt - $(hide) echo "recovery $(BOARD_RECOVERYIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt - $(hide) echo "system $(BOARD_SYSTEMIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt - $(hide) echo "userdata $(BOARD_USERDATAIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt + $(hide) echo "boot $(call image-size-from-data-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE))" >> $(zip_root)/META/imagesizes.txt + $(hide) echo "recovery $(call image-size-from-data-size,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE))" >> $(zip_root)/META/imagesizes.txt + $(hide) echo "system $(call image-size-from-data-size,$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))" >> $(zip_root)/META/imagesizes.txt + $(hide) echo "userdata $(call image-size-from-data-size,$(BOARD_USERDATAIMAGE_PARTITION_SIZE))" >> $(zip_root)/META/imagesizes.txt + $(hide) echo "$(tool_extensions)" > $(zip_root)/META/tool-extensions.txt @# Zip everything up, preserving symlinks $(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .) @@ -904,9 +917,17 @@ INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip $(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR) +ifeq ($(TARGET_OTA_SCRIPT_MODE),) +# default to "auto" +$(INTERNAL_OTA_PACKAGE_TARGET): scriptmode := auto +else +$(INTERNAL_OTA_PACKAGE_TARGET): scriptmode := $(TARGET_OTA_SCRIPT_MODE) +endif + $(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) otatools @echo "Package OTA: $@" $(hide) ./build/tools/releasetools/ota_from_target_files \ + -m $(scriptmode) \ -p $(HOST_OUT) \ -k $(KEY_CERT_PAIR) \ $(BUILT_TARGET_FILES_PACKAGE) $@ @@ -1030,9 +1051,17 @@ name := $(name)-img-$(FILE_NAME_TAG) INTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip +ifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),) +# default to common dir for device vendor +$(INTERNAL_UPDATE_PACKAGE_TARGET): extensions := $(TARGET_DEVICE_DIR)/../common +else +$(INTERNAL_UPDATE_PACKAGE_TARGET): extensions := $(TARGET_RELEASETOOLS_EXTENSIONS) +endif + $(INTERNAL_UPDATE_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) otatools @echo "Package: $@" $(hide) ./build/tools/releasetools/img_from_target_files \ + -s $(extensions) \ -p $(HOST_OUT) \ $(BUILT_TARGET_FILES_PACKAGE) $@ diff --git a/core/base_rules.mk b/core/base_rules.mk index a6bf504..64b74e7 100644 --- a/core/base_rules.mk +++ b/core/base_rules.mk @@ -272,8 +272,10 @@ $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SOURCE_INTERMEDIATES_DIR := $(intermediat $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_SOURCES := $(all_java_sources) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_OBJECTS := $(patsubst %.java,%.class,$(LOCAL_SRC_FILES)) ifeq ($(my_prefix),TARGET_) +ifeq ($(LOCAL_SDK_VERSION),) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_BOOTCLASSPATH := -bootclasspath $(call java-lib-files,core) endif +endif $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EXTRA_JAR_ARGS := $(extra_jar_args) $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASSET_DIR := $(LOCAL_ASSET_DIR) @@ -350,7 +352,6 @@ $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_PATH:=$(LOCAL_PATH) $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_POST_PROCESS_COMMAND:= $(LOCAL_POST_PROCESS_COMMAND) $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_AAPT_FLAGS:= $(LOCAL_AAPT_FLAGS) $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_JAVA_LIBRARIES:= $(LOCAL_JAVA_LIBRARIES) -#TODO: add this: $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_ADDITIONAL_DEPENDENCIES:= $(LOCAL_ADDITIONAL_DEPENDENCIES) $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_ALL_JAVA_LIBRARIES:= $(full_java_libs) $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_IS_HOST_MODULE := $(LOCAL_IS_HOST_MODULE) diff --git a/core/binary.mk b/core/binary.mk index ddcdc6f..4413d47 100644 --- a/core/binary.mk +++ b/core/binary.mk @@ -87,7 +87,7 @@ yacc_objects := $(yacc_cpps:$(LOCAL_CPP_EXTENSION)=.o) ifneq ($(strip $(yacc_cpps)),) $(yacc_cpps): $(intermediates)/%$(LOCAL_CPP_EXTENSION): \ $(TOPDIR)$(LOCAL_PATH)/%.y \ - $(lex_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES) + $(lex_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES) $(call transform-y-to-cpp,$(PRIVATE_CPP_EXTENSION)) $(yacc_headers): $(intermediates)/%.h: $(intermediates)/%$(LOCAL_CPP_EXTENSION) @@ -115,7 +115,7 @@ $(lex_objects): PRIVATE_ARM_MODE := $(normal_objects_mode) $(lex_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) $(lex_objects): $(intermediates)/%.o: \ $(intermediates)/%$(LOCAL_CPP_EXTENSION) \ - $(PRIVATE_ADDITIONAL_DEPENDENCIES) \ + $(LOCAL_ADDITIONAL_DEPENDENCIES) \ $(yacc_headers) $(transform-$(PRIVATE_HOST)cpp-to-o) endif @@ -142,7 +142,7 @@ cpp_objects := $(cpp_arm_objects) $(cpp_normal_objects) ifneq ($(strip $(cpp_objects)),) $(cpp_objects): $(intermediates)/%.o: \ $(TOPDIR)$(LOCAL_PATH)/%$(LOCAL_CPP_EXTENSION) \ - $(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES) + $(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES) $(transform-$(PRIVATE_HOST)cpp-to-o) -include $(cpp_objects:%.o=%.P) endif @@ -159,7 +159,7 @@ ifneq ($(strip $(gen_cpp_objects)),) # TODO: support compiling certain generated files as arm. $(gen_cpp_objects): PRIVATE_ARM_MODE := $(normal_objects_mode) $(gen_cpp_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) -$(gen_cpp_objects): $(intermediates)/%.o: $(intermediates)/%$(LOCAL_CPP_EXTENSION) $(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES) +$(gen_cpp_objects): $(intermediates)/%.o: $(intermediates)/%$(LOCAL_CPP_EXTENSION) $(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES) $(transform-$(PRIVATE_HOST)cpp-to-o) -include $(gen_cpp_objects:%.o=%.P) endif @@ -172,7 +172,7 @@ gen_S_sources := $(filter %.S,$(LOCAL_GENERATED_SOURCES)) gen_S_objects := $(gen_S_sources:%.S=%.o) ifneq ($(strip $(gen_S_sources)),) -$(gen_S_objects): $(intermediates)/%.o: $(intermediates)/%.S $(PRIVATE_ADDITIONAL_DEPENDENCIES) +$(gen_S_objects): $(intermediates)/%.o: $(intermediates)/%.S $(LOCAL_ADDITIONAL_DEPENDENCIES) $(transform-$(PRIVATE_HOST)s-to-o) -include $(gen_S_objects:%.o=%.P) endif @@ -181,7 +181,7 @@ gen_s_sources := $(filter %.s,$(LOCAL_GENERATED_SOURCES)) gen_s_objects := $(gen_s_sources:%.s=%.o) ifneq ($(strip $(gen_s_objects)),) -$(gen_s_objects): $(intermediates)/%.o: $(intermediates)/%.s $(PRIVATE_ADDITIONAL_DEPENDENCIES) +$(gen_s_objects): $(intermediates)/%.o: $(intermediates)/%.s $(LOCAL_ADDITIONAL_DEPENDENCIES) $(transform-$(PRIVATE_HOST)s-to-o-no-deps) -include $(gen_s_objects:%.o=%.P) endif @@ -206,12 +206,29 @@ $(c_normal_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) c_objects := $(c_arm_objects) $(c_normal_objects) ifneq ($(strip $(c_objects)),) -$(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c $(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES) +$(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c $(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES) $(transform-$(PRIVATE_HOST)c-to-o) -include $(c_objects:%.o=%.P) endif ########################################################### +## C: Compile generated .c files to .o. +########################################################### + +gen_c_sources := $(filter %.c,$(LOCAL_GENERATED_SOURCES)) +gen_c_objects := $(gen_c_sources:%.c=%.o) + +ifneq ($(strip $(gen_c_objects)),) +# Compile all generated files as thumb. +# TODO: support compiling certain generated files as arm. +$(gen_c_objects): PRIVATE_ARM_MODE := $(normal_objects_mode) +$(gen_c_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags) +$(gen_c_objects): $(intermediates)/%.o: $(intermediates)/%.c $(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES) + $(transform-$(PRIVATE_HOST)c-to-o) +-include $(gen_c_objects:%.o=%.P) +endif + +########################################################### ## ObjC: Compile .m files to .o ########################################################### @@ -232,7 +249,7 @@ asm_sources_S := $(filter %.S,$(LOCAL_SRC_FILES)) asm_objects_S := $(addprefix $(intermediates)/,$(asm_sources_S:.S=.o)) ifneq ($(strip $(asm_objects_S)),) -$(asm_objects_S): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.S $(PRIVATE_ADDITIONAL_DEPENDENCIES) +$(asm_objects_S): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.S $(LOCAL_ADDITIONAL_DEPENDENCIES) $(transform-$(PRIVATE_HOST)s-to-o) -include $(asm_objects_S:%.o=%.P) endif @@ -241,7 +258,7 @@ asm_sources_s := $(filter %.s,$(LOCAL_SRC_FILES)) asm_objects_s := $(addprefix $(intermediates)/,$(asm_sources_s:.s=.o)) ifneq ($(strip $(asm_objects_s)),) -$(asm_objects_s): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.s $(PRIVATE_ADDITIONAL_DEPENDENCIES) +$(asm_objects_s): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.s $(LOCAL_ADDITIONAL_DEPENDENCIES) $(transform-$(PRIVATE_HOST)s-to-o-no-deps) -include $(asm_objects_s:%.o=%.P) endif @@ -261,6 +278,7 @@ all_objects := \ $(gen_cpp_objects) \ $(gen_asm_objects) \ $(c_objects) \ + $(gen_c_objects) \ $(yacc_objects) \ $(lex_objects) \ $(addprefix $(TOPDIR)$(LOCAL_PATH)/,$(LOCAL_PREBUILT_OBJ_FILES)) diff --git a/core/build_id.mk b/core/build_id.mk index 060c9b5..64ee0e1 100644 --- a/core/build_id.mk +++ b/core/build_id.mk @@ -23,7 +23,7 @@ # (like "TC1-RC5"). It must be a single word, and is # capitalized by convention. # -BUILD_ID := Donut +BUILD_ID := ECLAIR-MR2 # DISPLAY_BUILD_NUMBER should only be set for development branches, # If set, the BUILD_NUMBER (cl) is appended to the BUILD_ID for diff --git a/core/clear_vars.mk b/core/clear_vars.mk index 28044c4..a7eba3f 100644 --- a/core/clear_vars.mk +++ b/core/clear_vars.mk @@ -87,6 +87,8 @@ LOCAL_CERTIFICATE:= LOCAL_SDK_VERSION:= LOCAL_NO_EMMA_INSTRUMENT:= LOCAL_NO_EMMA_COMPILE:= +LOCAL_PROGUARD_ENABLED:= # '',optonly,full,custom +LOCAL_PROGUARD_FLAGS:= # Trim MAKEFILE_LIST so that $(call my-dir) doesn't need to # iterate over thousands of entries every time. diff --git a/core/combo/arch/arm/armv4t.mk b/core/combo/arch/arm/armv4t.mk new file mode 100644 index 0000000..abc8fa2 --- /dev/null +++ b/core/combo/arch/arm/armv4t.mk @@ -0,0 +1,23 @@ +# Configuration for Linux on ARM. +# Generating binaries for the ARMv4T architecture and higher +# +# Supporting armv4 (without thumb) does not make much sense since +# it's mostly an obsoleted instruction set architecture (only available +# in StrongArm and arm8). Supporting armv4 will require a lot of conditional +# code in assembler source since the bx (branch and exchange) instruction is +# not supported. +# +$(warning ARMv4t support is currently a work in progress. It does not work right now!) +ARCH_ARM_HAVE_THUMB_SUPPORT := false +ARCH_ARM_HAVE_THUMB_INTERWORKING := false +ARCH_ARM_HAVE_64BIT_DATA := false +ARCH_ARM_HAVE_HALFWORD_MULTIPLY := false +ARCH_ARM_HAVE_CLZ := false +ARCH_ARM_HAVE_FFS := false + +DEFAULT_TARGET_CPU := arm920t + +# Note: Hard coding the 'tune' value here is probably not ideal, +# and a better solution should be found in the future. +# +arch_variant_cflags := -march=armv4t -mtune=arm920t -D__ARM_ARCH_4T__ diff --git a/core/combo/arch/arm/armv5te-vfp.mk b/core/combo/arch/arm/armv5te-vfp.mk new file mode 100644 index 0000000..75299ac --- /dev/null +++ b/core/combo/arch/arm/armv5te-vfp.mk @@ -0,0 +1,7 @@ +# At the moment, use the same settings than the one +# for armv5te, since TARGET_ARCH_VARIANT := armv5te-vfp +# will only be used to select an optimized VFP-capable assembly +# interpreter loop for Dalvik. +# +include $(BUILD_COMBOS)/arch/arm/armv5te.mk + diff --git a/core/combo/arch/arm/armv5te.mk b/core/combo/arch/arm/armv5te.mk new file mode 100644 index 0000000..29aada6 --- /dev/null +++ b/core/combo/arch/arm/armv5te.mk @@ -0,0 +1,21 @@ +# Configuration for Linux on ARM. +# Generating binaries for the ARMv5TE architecture and higher +# +ARCH_ARM_HAVE_THUMB_SUPPORT := true +ARCH_ARM_HAVE_FAST_INTERWORKING := true +ARCH_ARM_HAVE_64BIT_DATA := true +ARCH_ARM_HAVE_HALFWORD_MULTIPLY := true +ARCH_ARM_HAVE_CLZ := true +ARCH_ARM_HAVE_FFS := true + +# Note: Hard coding the 'tune' value here is probably not ideal, +# and a better solution should be found in the future. +# +arch_variant_cflags := \ + -march=armv5te \ + -mtune=xscale \ + -D__ARM_ARCH_5__ \ + -D__ARM_ARCH_5T__ \ + -D__ARM_ARCH_5E__ \ + -D__ARM_ARCH_5TE__ + diff --git a/core/combo/arch/arm/armv7-a.mk b/core/combo/arch/arm/armv7-a.mk new file mode 100644 index 0000000..7a3ecc4 --- /dev/null +++ b/core/combo/arch/arm/armv7-a.mk @@ -0,0 +1,22 @@ +# Configuration for Linux on ARM. +# Generating binaries for the ARMv7-a architecture and higher +# +ARCH_ARM_HAVE_THUMB_SUPPORT := true +ARCH_ARM_HAVE_FAST_INTERWORKING := true +ARCH_ARM_HAVE_64BIT_DATA := true +ARCH_ARM_HAVE_HALFWORD_MULTIPLY := true +ARCH_ARM_HAVE_CLZ := true +ARCH_ARM_HAVE_FFS := true +ARCH_ARM_HAVE_VFP := true +ARCH_ARM_HAVE_NEON := true + +# Note: Hard coding the 'tune' value here is probably not ideal, +# and a better solution should be found in the future. +# +arch_variant_cflags := \ + -march=armv7-a \ + -mfloat-abi=softfp \ + -mfpu=neon + +arch_variant_ldflags := \ + -Wl,--fix-cortex-a8
\ No newline at end of file diff --git a/core/combo/darwin-x86.mk b/core/combo/darwin-x86.mk index 2150960..65d6375 100644 --- a/core/combo/darwin-x86.mk +++ b/core/combo/darwin-x86.mk @@ -14,6 +14,7 @@ $(combo_target)JNILIB_SUFFIX := .jnilib $(combo_target)GLOBAL_CFLAGS += \ -include $(call select-android-config-h,darwin-x86) $(combo_target)RUN_RANLIB_AFTER_COPYING := true +$(combo_target)GLOBAL_ARFLAGS := cqs ifeq ($(combo_target),TARGET_) $(combo_target)CUSTOM_LD_COMMAND := true diff --git a/core/combo/linux-arm.mk b/core/combo/linux-arm.mk index 11a8ac7..6529106 100644 --- a/core/combo/linux-arm.mk +++ b/core/combo/linux-arm.mk @@ -1,68 +1,44 @@ # Configuration for Linux on ARM. # Included by combo/select.make -# You can set TARGET_ARCH_VERSION to use an arch version other -# than ARMv5TE -ifeq ($(strip $(TARGET_ARCH_VERSION)),) -TARGET_ARCH_VERSION := armv5te -endif - -# This set of if blocks sets makefile variables similar to preprocesser +# You can set TARGET_ARCH_VARIANT to use an arch version other +# than ARMv5TE. Each value should correspond to a file named +# $(BUILD_COMBOS)/arch/<name>.mk which must contain +# makefile variable definitions similar to the preprocessor # defines in system/core/include/arch/<combo>/AndroidConfig.h. Their -# purpose is to allow module Android.mk files to selctively compile -# different versions of code based upon the funtionality and +# purpose is to allow module Android.mk files to selectively compile +# different versions of code based upon the funtionality and # instructions available in a given architecture version. # -# The blocks also define specific arch_version_cflags, which +# The blocks also define specific arch_variant_cflags, which # include defines, and compiler settings for the given architecture # version. # -# Note: Hard coding the 'tune' value here is probably not ideal, -# and a better solution should be found in the future. -# -# With two or three different versions this if block approach is -# fine. If/when this becomes large, please change this to include -# architecture versions specific Makefiles which define these -# variables. -# -# Supporting armv4 (without thumb) does not make much sense since -# it's mostly an obsoleted instruction set architecture (only available -# in StrongArm and arm8). Supporting armv4 will require a lot of conditional -# code in assembler source since the bx (branch and exchange) instruction is -# not supported. +ifeq ($(strip $(TARGET_ARCH_VARIANT)),) +TARGET_ARCH_VARIANT := armv5te +endif + +# TARGET_ARCH_VARIANT used to be called TARGET_ARCH_VERSION +# to avoid any weirdness, issue an error message if the latter +# is defined. # -ifeq ($(TARGET_ARCH_VERSION),armv5te) -ARCH_ARM_HAVE_THUMB_SUPPORT := true -ARCH_ARM_HAVE_FAST_INTERWORKING := true -ARCH_ARM_HAVE_64BIT_DATA := true -ARCH_ARM_HAVE_HALFWORD_MULTIPLY := true -ARCH_ARM_HAVE_CLZ := true -ARCH_ARM_HAVE_FFS := true - -arch_version_cflags := -march=armv5te -mtune=xscale -D__ARM_ARCH_5__ \ - -D__ARM_ARCH_5T__ -D__ARM_ARCH_5TE__ -else -ifeq ($(TARGET_ARCH_VERSION),armv4t) -$(warning ARMv4t support is currently a work in progress. It does not work right now!) -ARCH_ARM_HAVE_THUMB_SUPPORT := false -ARCH_ARM_HAVE_THUMB_INTERWORKING := false -ARCH_ARM_HAVE_64BIT_DATA := false -ARCH_ARM_HAVE_HALFWORD_MULTIPLY := false -ARCH_ARM_HAVE_CLZ := false -ARCH_ARM_HAVE_FFS := false - -DEFAULT_TARGET_CPU := arm920t - -arch_version_cflags := -march=armv4t -mtune=arm920t -D__ARM_ARCH_4T__ -else -$(error Unknown ARM architecture version: $(TARGET_ARCH_VERSION)) +ifneq ($(strip $(TARGET_ARCH_VERSION)),) +$(info Definition for TARGET_ARCH_VERSION encountered !) +$(info This variable has been renamed TARGET_ARCH_VARIANT, please update your build files !!) +$(error Aborting the build.) endif + +TARGET_ARCH_SPECIFIC_MAKEFILE := $(BUILD_COMBOS)/arch/$(TARGET_ARCH)/$(TARGET_ARCH_VARIANT).mk +ifeq ($(strip $(wildcard $(TARGET_ARCH_SPECIFIC_MAKEFILE))),) +$(error Unknown ARM architecture version: $(TARGET_ARCH_VARIANT)) endif +include $(TARGET_ARCH_SPECIFIC_MAKEFILE) + # You can set TARGET_TOOLS_PREFIX to get gcc from somewhere else ifeq ($(strip $($(combo_target)TOOLS_PREFIX)),) $(combo_target)TOOLS_PREFIX := \ - prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.2.1/bin/arm-eabi- + prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.4.0/bin/arm-eabi- endif $(combo_target)CC := $($(combo_target)TOOLS_PREFIX)gcc$(HOST_EXECUTABLE_SUFFIX) @@ -79,7 +55,7 @@ TARGET_arm_CFLAGS := -O2 \ -funswitch-loops \ -finline-limit=300 -# Modules can choose to compile some source as thumb. As +# Modules can choose to compile some source as thumb. As # non-thumb enabled targets are supported, this is treated # as a 'hint'. If thumb is not enabled, these files are just # compiled as ARM. @@ -103,7 +79,7 @@ endif # with -mlong-calls. When built at -O0, those libraries are # too big for a thumb "BL <label>" to go from one end to the other. ifeq ($(FORCE_ARM_DEBUGGING),true) - TARGET_arm_CFLAGS += -fno-omit-frame-pointer + TARGET_arm_CFLAGS += -fno-omit-frame-pointer -fno-strict-aliasing TARGET_thumb_CFLAGS += -marm -fno-omit-frame-pointer endif @@ -116,10 +92,13 @@ $(combo_target)GLOBAL_CFLAGS += \ -funwind-tables \ -fstack-protector \ -fno-short-enums \ - $(arch_version_cflags) \ + $(arch_variant_cflags) \ -include $(android_config_h) \ -I $(arch_include_dir) +$(combo_target)GLOBAL_LDFLAGS += \ + $(arch_variant_ldflags) + # We only need thumb interworking in cases where thumb support # is available in the architecture, and just to be sure, (and # since sometimes thumb-interwork appears to be default), we @@ -133,7 +112,7 @@ endif $(combo_target)GLOBAL_CPPFLAGS += -fvisibility-inlines-hidden $(combo_target)RELEASE_CFLAGS := \ - -DSK_RELEASE -DNDEBUG \ + -DNDEBUG \ -g \ -Wstrict-aliasing=2 \ -finline-functions \ @@ -150,7 +129,7 @@ libthread_db_root := bionic/libthread_db ## on some hosts, the target cross-compiler is not available so do not run this command ifneq ($(wildcard $($(combo_target)CC)),) -# We compile with the global cflags to ensure that +# We compile with the global cflags to ensure that # any flags which affect libgcc are correctly taken # into account. $(combo_target)LIBGCC := $(shell $($(combo_target)CC) $($(combo_target)GLOBAL_CFLAGS) -print-libgcc-file-name) @@ -201,6 +180,7 @@ $(TARGET_CXX) \ $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \ -o $@ \ $(PRIVATE_LDFLAGS) \ + $(TARGET_GLOBAL_LDFLAGS) \ $(TARGET_LIBGCC) endef @@ -217,6 +197,7 @@ $(TARGET_CXX) -nostdlib -Bdynamic -Wl,-T,$(BUILD_SYSTEM)/armelf.x \ $(PRIVATE_ALL_OBJECTS) \ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \ $(PRIVATE_LDFLAGS) \ + $(TARGET_GLOBAL_LDFLAGS) \ $(TARGET_LIBGCC) \ $(TARGET_CRTEND_O) endef @@ -228,6 +209,7 @@ $(TARGET_CXX) -nostdlib -Bstatic -Wl,-T,$(BUILD_SYSTEM)/armelf.x \ $(TARGET_GLOBAL_LD_DIRS) \ $(TARGET_CRTBEGIN_STATIC_O) \ $(PRIVATE_LDFLAGS) \ + $(TARGET_GLOBAL_LDFLAGS) \ $(PRIVATE_ALL_OBJECTS) \ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \ $(TARGET_LIBGCC) \ diff --git a/core/combo/select.mk b/core/combo/select.mk index 273b660..ccdf1fb 100644 --- a/core/combo/select.mk +++ b/core/combo/select.mk @@ -39,7 +39,8 @@ $(combo_target)HAVE_KERNEL_MODULES := 0 # These flags might (will) be overridden by the target makefiles $(combo_target)GLOBAL_CFLAGS := -fno-exceptions -Wno-multichar $(combo_target)RELEASE_CFLAGS := -O2 -g -fno-strict-aliasing -$(combo_target)GLOBAL_ARFLAGS := crs +$(combo_target)GLOBAL_LDFLAGS := +$(combo_target)GLOBAL_ARFLAGS := crsP $(combo_target)EXECUTABLE_SUFFIX := $(combo_target)SHLIB_SUFFIX := .so diff --git a/core/config.mk b/core/config.mk index b705de5..37ca404 100644 --- a/core/config.mk +++ b/core/config.mk @@ -75,11 +75,11 @@ SHOW_COMMANDS:= $(filter showcommands,$(MAKECMDGOALS)) # ############################################################### # These can be changed to modify both host and device modules. -COMMON_GLOBAL_CFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused +COMMON_GLOBAL_CFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -Winit-self -Wpointer-arith COMMON_RELEASE_CFLAGS:= -DNDEBUG -UDEBUG -COMMON_GLOBAL_CPPFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -Wnon-virtual-dtor -COMMON_RELEASE_CPPFLAGS:= -DNDEBUG -UDEBUG +COMMON_GLOBAL_CPPFLAGS:= $(COMMON_GLOBAL_CFLAGS) -Wsign-promo +COMMON_RELEASE_CPPFLAGS:= $(COMMON_RELEASE_CFLAGS) # Set the extensions used for various packages COMMON_PACKAGE_SUFFIX := .zip @@ -87,7 +87,13 @@ COMMON_JAVA_PACKAGE_SUFFIX := .jar COMMON_ANDROID_PACKAGE_SUFFIX := .apk # list of flags to turn specific warnings in to errors -TARGET_ERROR_FLAGS := -Werror=return-type +TARGET_ERROR_FLAGS := -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point + +# TODO: do symbol compression +TARGET_COMPRESS_MODULE_SYMBOLS := false + +# Default is to prelink modules. +TARGET_PRELINK_MODULE := true # ############################################################### # Include sub-configuration files @@ -105,6 +111,32 @@ TARGET_ERROR_FLAGS := -Werror=return-type # are specific to the user's build configuration. include $(BUILD_SYSTEM)/envsetup.mk +# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE) +# or under vendor/*/$(TARGET_DEVICE). Search in both places, but +# make sure only one exists. +# Real boards should always be associated with an OEM vendor. +board_config_mk := \ + $(strip $(wildcard \ + $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \ + vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \ + )) +ifeq ($(board_config_mk),) + $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE)) +endif +ifneq ($(words $(board_config_mk)),1) + $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk)) +endif +include $(board_config_mk) +TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk))) +board_config_mk := + +# Clean up/verify variables defined by the board config file. +TARGET_BOOTLOADER_BOARD_NAME := $(strip $(TARGET_BOOTLOADER_BOARD_NAME)) +TARGET_CPU_ABI := $(strip $(TARGET_CPU_ABI)) +ifeq ($(TARGET_CPU_ABI),) + $(error No TARGET_CPU_ABI defined by board config: $(board_config_mk)) +endif + # $(1): os/arch define select-android-config-h system/core/include/arch/$(1)/AndroidConfig.h @@ -167,6 +199,7 @@ MKTARBALL := build/tools/mktarball.sh TUNE2FS := tune2fs E2FSCK := e2fsck JARJAR := java -jar $(HOST_OUT_JAVA_LIBRARIES)/jarjar.jar +PROGUARD := external/proguard/bin/proguard.sh # dx is java behind a shell script; no .exe necessary. DX := $(HOST_OUT_EXECUTABLES)/dx @@ -259,10 +292,6 @@ HOST_GLOBAL_CPPFLAGS += $(HOST_RELEASE_CPPFLAGS) TARGET_GLOBAL_CFLAGS += $(TARGET_RELEASE_CFLAGS) TARGET_GLOBAL_CPPFLAGS += $(TARGET_RELEASE_CPPFLAGS) -# TODO: do symbol compression -TARGET_COMPRESS_MODULE_SYMBOLS := false -TARGET_PRELINK_MODULE := true - PREBUILT_IS_PRESENT := $(if $(wildcard prebuilt/Android.mk),true) diff --git a/core/definitions.mk b/core/definitions.mk index 9b3a03d..06b6201 100644 --- a/core/definitions.mk +++ b/core/definitions.mk @@ -109,7 +109,14 @@ endef # Figure out where we are. define my-dir -$(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST),$(MAKEFILE_LIST)))) +$(strip \ + $(eval md_file_ := $$(lastword $$(MAKEFILE_LIST))) \ + $(if $(filter $(CLEAR_VARS),$(md_file_)), \ + $(error LOCAL_PATH must be set before including $$(CLEAR_VARS)) \ + , \ + $(patsubst %/,%,$(dir $(md_file_))) \ + ) \ + ) endef ########################################################### @@ -128,7 +135,8 @@ endef # $(1): directory to search under # Ignores $(1)/Android.mk define first-makefiles-under -$(shell build/tools/findleaves.sh --mindepth=2 $(1) Android.mk) +$(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git \ + --mindepth=2 $(1) Android.mk) endef ########################################################### @@ -923,7 +931,7 @@ define transform-o-to-static-lib @rm -f $@ $(extract-and-include-whole-static-libs) @echo "target StaticLib: $(PRIVATE_MODULE) ($@)" -$(hide) $(TARGET_AR) $(TARGET_GLOBAL_ARFLAGS) $(PRIVATE_ARFLAGS) $@ $^ +$(hide) echo $^ | xargs $(TARGET_AR) $(TARGET_GLOBAL_ARFLAGS) $(PRIVATE_ARFLAGS) $@ endef ########################################################### @@ -936,7 +944,7 @@ define transform-host-o-to-static-lib @mkdir -p $(dir $@) @echo "host StaticLib: $(PRIVATE_MODULE) ($@)" @rm -f $@ -$(HOST_AR) $(HOST_GLOBAL_ARFLAGS) $(PRIVATE_ARFLAGS) $@ $^ +echo $^ | xargs $(HOST_AR) $(HOST_GLOBAL_ARFLAGS) $(PRIVATE_ARFLAGS) $@ endef @@ -1166,6 +1174,7 @@ $(hide) $(AAPT) package $(PRIVATE_AAPT_FLAGS) -m -z \ $(addprefix -S , $(PRIVATE_RESOURCE_DIR)) \ $(addprefix -A , $(PRIVATE_ASSET_DIR)) \ $(addprefix -I , $(PRIVATE_AAPT_INCLUDES)) \ + $(addprefix -G , $(PRIVATE_PROGUARD_OPTIONS_FILE)) \ $(addprefix --min-sdk-version , $(DEFAULT_APP_TARGET_SDK)) \ $(addprefix --target-sdk-version , $(DEFAULT_APP_TARGET_SDK)) \ $(addprefix --version-code , $(PLATFORM_SDK_VERSION)) \ @@ -1264,11 +1273,12 @@ endef #TODO: use a smaller -Xmx value for most libraries; # only core.jar and framework.jar need a heap this big. +# Avoid the memory arguments on Windows, dx fails to load for some reason with them. define transform-classes.jar-to-dex @echo "target Dex: $(PRIVATE_MODULE)" @mkdir -p $(dir $@) -$(hide) $(DX) -JXms16M \ - -JXmx1536M \ +$(hide) $(DX) \ + $(if $(findstring windows,$(HOST_OS)),,-JXms16M -JXmx1536M) \ --dex --output=$@ \ $(if $(NO_OPTIMIZE_DX), \ --no-optimize) \ @@ -1324,11 +1334,9 @@ $(hide) (cd $(dir $@) && zip -r $(notdir $@) lib) $(hide) rm -rf $(dir $@)lib endef -#TODO: use aapt instead of zip, once it supports junking the path -# (so adding "xxx/yyy/classes.dex" appears as "classes.dex") #TODO: update the manifest to point to the dex file define add-dex-to-package -$(hide) zip -qj $@ $(PRIVATE_DEX_FILE) +$(hide) $(AAPT) add -k $@ $(PRIVATE_DEX_FILE) endef define add-java-resources-to-package @@ -1587,7 +1595,7 @@ $(if $(2), \ echo "$$printname total size is $$total"; \ img_blocksize=$(call image-size-from-data-size,$(BOARD_FLASH_BLOCK_SIZE)); \ if [ "$(3)" == "yaffs" ]; then \ - reservedblocks=5; \ + reservedblocks=8; \ else \ reservedblocks=0; \ fi; \ @@ -1607,6 +1615,37 @@ $(if $(2), \ ) endef +# Like assert-max-file-size, but the second argument is a partition +# size, which we'll convert to a max image size before checking it +# against the files. +# +# $(1): The file(s) to check (often $@) +# $(2): The partition size. +define assert-max-image-size +$(if $(2), \ + $(call assert-max-file-size,$(1),$(call image-size-from-data-size,$(2))), \ + true) +endef + + +########################################################### +## Define device-specific radio files +########################################################### + +# Copy a radio image file to the output location, and add it to +# INSTALLED_RADIOIMAGE_TARGET. +# $(1): filename +define add-radio-file + $(eval $(call add-radio-file-internal,$(1))) +endef +define add-radio-file-internal +INSTALLED_RADIOIMAGE_TARGET += $$(PRODUCT_OUT)/$(1) +ALL_PREBUILT += $$(PRODUCT_OUT)/$(1) +$$(PRODUCT_OUT)/$(1) : $$(LOCAL_PATH)/$(1) | $$(ACP) + $$(transform-prebuilt-to-target) +endef + + ########################################################### ## Other includes ########################################################### diff --git a/core/envsetup.mk b/core/envsetup.mk index 31901e9..6d14753 100644 --- a/core/envsetup.mk +++ b/core/envsetup.mk @@ -173,7 +173,7 @@ ifeq ($(TARGET_SIMULATOR),true) # under product/) are actually host-dependent. # But, the debug type is controlled by TARGET_BUILD_TYPE and not # HOST_BUILD_TYPE. - TARGET_PRODUCT_OUT_ROOT := $(HOST_OUT_$(TARGET_BUILD_TYPE))/product + TARGET_PRODUCT_OUT_ROOT := $(HOST_OUT_$(TARGET_BUILD_TYPE))/pr else TARGET_PRODUCT_OUT_ROOT := $(TARGET_OUT_ROOT)/product endif @@ -270,7 +270,7 @@ ifeq ($(TARGET_SIMULATOR),true) ABP:=$(ABP):$(TARGET_OUT_EXECUTABLES) else # this should be copied to HOST_OUT_EXECUTABLES instead - ABP:=$(ABP):$(PWD)/prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.2.1/bin + ABP:=$(ABP):$(PWD)/prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.4.0/bin endif ANDROID_BUILD_PATHS := $(ABP) ANDROID_PREBUILTS := prebuilt/$(HOST_PREBUILT_TAG) @@ -335,5 +335,3 @@ $(info HOST_BUILD_TYPE=$(HOST_BUILD_TYPE)) $(info BUILD_ID=$(BUILD_ID)) $(info ============================================) endif - - diff --git a/core/java.mk b/core/java.mk index 658b173..19b4d63 100644 --- a/core/java.mk +++ b/core/java.mk @@ -68,7 +68,8 @@ emma_intermediates_dir := $(intermediates.COMMON)/emma_out # the emma tool full_classes_emma_jar := $(emma_intermediates_dir)/lib/$(full_classes_compiled_jar_leaf) full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar -full_classes_jarjar_jar := $(full_classes_jar) +full_classes_jarjar_jar := $(intermediates.COMMON)/classes-jarjar.jar +full_classes_proguard_jar := $(full_classes_jar) built_dex := $(intermediates.COMMON)/classes.dex LOCAL_INTERMEDIATE_TARGETS += \ @@ -174,15 +175,14 @@ $(full_classes_emma_jar): $(full_classes_compiled_jar) $(PRIVATE_EMMA_COVERAGE_FILE): $(full_classes_emma_jar) else $(full_classes_emma_jar): $(full_classes_compiled_jar) | $(ACP) - @echo Copying $< + @echo Copying: $< $(copy-file-to-target) endif -# Run jarjar if necessary, otherwise just copy the file. This is the last -# part of this step, so the output of this command is full_classes_jar. +# Run jarjar if necessary, otherwise just copy the file. ifneq ($(strip $(LOCAL_JARJAR_RULES)),) $(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES) -$(full_classes_jarjar_jar): $(full_classes_emma_jar) | jarjar +$(full_classes_jarjar_jar): $(full_classes_emma_jar) | $(JARJAR) @echo JarJar: $@ $(hide) $(JARJAR) process $(PRIVATE_JARJAR_RULES) $< $@ else @@ -191,6 +191,40 @@ $(full_classes_jarjar_jar): $(full_classes_emma_jar) | $(ACP) $(hide) $(ACP) $< $@ endif +# Run proguard if necessary, otherwise just copy the file. This is the last +# part of this step, so the output of this command is full_classes_jar. +ifneq ($(strip $(LOCAL_PROGUARD_ENABLED)),) +proguard_dictionary := $(intermediates.COMMON)/proguard_dictionary +proguard_flags := $(addprefix -libraryjars ,$(full_java_libs)) \ + -include $(BUILD_SYSTEM)/proguard.flags \ + -forceprocessing \ + -printmapping $(proguard_dictionary) +ifeq ($(strip $(LOCAL_PROGUARD_ENABLED)),full) + # full +else +ifeq ($(strip $(LOCAL_PROGUARD_ENABLED)),optonly) + # optonly + proguard_flags += -dontobfuscate +else +ifeq ($(strip $(LOCAL_PROGUARD_ENABLED)),custom) + # custom +else + $(warning while processing: $(LOCAL_MODULE)) + $(error invalid value for LOCAL_PROGUARD_ENABLED: $(LOCAL_PROGUARD_ENABLED)) +endif +endif +endif + +$(full_classes_proguard_jar): PRIVATE_PROGUARD_FLAGS := $(proguard_flags) $(LOCAL_PROGUARD_FLAGS) +$(full_classes_proguard_jar): $(full_classes_emma_jar) | $(PROGUARD) + @echo Proguard: $@ + $(hide) $(PROGUARD) -injars $< -outjars $@ $(PRIVATE_PROGUARD_FLAGS) +else +$(full_classes_proguard_jar): $(full_classes_emma_jar) | $(ACP) + @echo Copying: $@ + $(hide) $(ACP) $< $@ +endif + # Override PRIVATE_INTERMEDIATES_DIR so that install-dex-debug # will work even when intermediates != intermediates.COMMON. $(built_dex): PRIVATE_INTERMEDIATES_DIR := $(intermediates.COMMON) diff --git a/core/main.mk b/core/main.mk index 97e49f4..9986f94 100644 --- a/core/main.mk +++ b/core/main.mk @@ -54,6 +54,12 @@ include $(BUILD_SYSTEM)/config.mk # be generated correctly include $(BUILD_SYSTEM)/cleanbuild.mk +VERSION_CHECK_SEQUENCE_NUMBER := 1 +-include $(OUT_DIR)/versions_checked.mk +ifneq ($(VERSION_CHECK_SEQUENCE_NUMBER),$(VERSIONS_CHECKED)) + +$(info Checking build tools versions...) + ifneq ($(HOST_OS),windows) ifneq ($(HOST_OS)-$(HOST_ARCH),darwin-ppc) # check for a case sensitive file system @@ -123,6 +129,10 @@ endif endif # windows +$(shell echo 'VERSIONS_CHECKED := $(VERSION_CHECK_SEQUENCE_NUMBER)' \ + > $(OUT_DIR)/versions_checked.mk) +endif + # These are the modifier targets that don't do anything themselves, but # change the behavior of the build. # (must be defined before including definitions.make) @@ -220,7 +230,8 @@ ifeq ($(TARGET_BUILD_VARIANT),eng) tags_to_install := user debug eng # Don't require the setup wizard on eng builds ADDITIONAL_BUILD_PROPERTIES := $(filter-out ro.setupwizard.mode=%,\ - $(call collapse-pairs, $(ADDITIONAL_BUILD_PROPERTIES))) + $(call collapse-pairs, $(ADDITIONAL_BUILD_PROPERTIES))) \ + ro.setupwizard.mode=OPTIONAL endif ## tests ## @@ -241,8 +252,16 @@ tags_to_install := user debug eng ADDITIONAL_BUILD_PROPERTIES += xmpp.auto-presence=true ADDITIONAL_BUILD_PROPERTIES += ro.config.nocheckin=yes else # !sdk -# Enable sync for non-sdk builds only (sdk builds lack SubscribedFeedsProvider). -ADDITIONAL_BUILD_PROPERTIES += ro.config.sync=yes +endif + +## precise GC ## + +ifneq ($(filter dalvik.gc.type-precise,$(PRODUCT_TAGS)),) + # Enabling type-precise GC results in larger optimized DEX files. The + # additional storage requirements for ".odex" files can cause /system + # to overflow on some devices, so this is configured separately for + # each product. + ADDITIONAL_BUILD_PROPERTIES += dalvik.vm.dexopt-flags=m=y endif # Install an apns-conf.xml file if one's not already being installed. @@ -256,7 +275,7 @@ endif # If we're on an eng or tests build, but not on the sdk, and we have # a better one, use that instead. ifneq ($(filter eng tests,$(TARGET_BUILD_VARIANT)),) - ifdef is_sdk_build + ifndef is_sdk_build apns_to_use := $(wildcard vendor/google/etc/apns-conf.xml) ifneq ($(strip $(apns_to_use)),) PRODUCT_COPY_FILES := \ @@ -267,6 +286,7 @@ ifneq ($(filter eng tests,$(TARGET_BUILD_VARIANT)),) endif ADDITIONAL_BUILD_PROPERTIES += net.bt.name=Android +ADDITIONAL_BUILD_PROPERTIES += ro.config.sync=yes # enable vm tracing in files for now to help track # the cause of ANRs in the content process @@ -317,8 +337,6 @@ endif # Bring in all modules that need to be built. ifneq ($(dont_bother),true) -subdir_makefiles := - ifeq ($(HOST_OS),windows) SDK_ONLY := true endif @@ -338,6 +356,7 @@ subdirs := \ dalvik/tools/hprof-conv \ development/emulator/mksdcard \ development/tools/line_endings \ + development/tools/sdklauncher \ development/host \ external/expat \ external/libpng \ @@ -371,6 +390,7 @@ subdirs += \ development/tools/sdkstats \ development/tools/sdkmanager \ development/tools/mkstubs \ + development/tools/layoutopt \ frameworks/base \ frameworks/base/tools/layoutlib \ external/googleclient \ @@ -390,8 +410,6 @@ ifeq ($(BUILD_TINY_ANDROID), true) # TINY_ANDROID is a super-minimal build configuration, handy for board # bringup and very low level debugging -INTERNAL_DEFAULT_DOCS_TARGETS := - subdirs := \ bionic \ system/core \ @@ -410,7 +428,6 @@ else # !BUILD_TINY_ANDROID # # Typical build; include any Android.mk files we can find. # -INTERNAL_DEFAULT_DOCS_TARGETS := offline-sdk-docs subdirs := $(TOP) FULL_BUILD := true @@ -419,41 +436,6 @@ endif # !BUILD_TINY_ANDROID endif # !SDK_ONLY -# Can't use first-makefiles-under here because -# --mindepth=2 makes the prunes not work. -subdir_makefiles += \ - $(shell build/tools/findleaves.sh --prune="*\.git*" --prune="*\.repo*" --prune="./out" $(subdirs) Android.mk) - -# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE) -# or under vendor/*/$(TARGET_DEVICE). Search in both places, but -# make sure only one exists. -# Real boards should always be associated with an OEM vendor. -board_config_mk := \ - $(strip $(wildcard \ - $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \ - vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \ - )) -ifeq ($(board_config_mk),) - $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE)) -endif -ifneq ($(words $(board_config_mk)),1) - $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk)) -endif -include $(board_config_mk) -TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk))) -board_config_mk := - -# Clean up/verify variables defined by the board config file. -TARGET_BOOTLOADER_BOARD_NAME := $(strip $(TARGET_BOOTLOADER_BOARD_NAME)) -TARGET_CPU_ABI := $(strip $(TARGET_CPU_ABI)) -ifeq ($(TARGET_CPU_ABI),) - $(error No TARGET_CPU_ABI defined by board config: $(board_config_mk)) -endif - -# -# Include all of the makefiles in the system -# - ifneq ($(ONE_SHOT_MAKEFILE),) # We've probably been invoked by the "mm" shell function # with a subdirectory's makefile. @@ -465,14 +447,25 @@ include $(ONE_SHOT_MAKEFILE) # would have been with a normal make. CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS),)) FULL_BUILD := -INTERNAL_DEFAULT_DOCS_TARGETS := # Stub out the notice targets, which probably aren't defined # when using ONE_SHOT_MAKEFILE. NOTICE-HOST-%: ; NOTICE-TARGET-%: ; -else + +else # ONE_SHOT_MAKEFILE + +# +# Include all of the makefiles in the system +# + +# Can't use first-makefiles-under here because +# --mindepth=2 makes the prunes not work. +subdir_makefiles := \ + $(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git $(subdirs) Android.mk) + include $(subdir_makefiles) -endif +endif # ONE_SHOT_MAKEFILE + # ------------------------------------------------------------------- # All module makefiles have been included at this point. # ------------------------------------------------------------------- @@ -673,7 +666,6 @@ droidcore: files \ $(INSTALLED_BOOTIMAGE_TARGET) \ $(INSTALLED_RECOVERYIMAGE_TARGET) \ $(INSTALLED_USERDATAIMAGE_TARGET) \ - $(INTERNAL_DEFAULT_DOCS_TARGETS) \ $(INSTALLED_FILES_FILE) # The actual files built by the droidcore target changes depending diff --git a/core/notice_files.mk b/core/notice_files.mk index 24295c7..fd76d51 100644 --- a/core/notice_files.mk +++ b/core/notice_files.mk @@ -20,12 +20,19 @@ else # We can't use xxx_OUT_STATIC_LIBRARIES because it points into # device-obj or host-obj. module_installed_filename := \ - $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_SHARED_LIBRARIES))/$(notdir $(LOCAL_BUILT_MODULE)) + $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_SHARED_LIBRARIES))/$(notdir $(LOCAL_BUILT_MODULE)) else ifeq ($(LOCAL_MODULE_CLASS),JAVA_LIBRARIES) # Stick the static java libraries with the regular java libraries. + module_leaf := $(notdir $(LOCAL_BUILT_MODULE)) + # javalib.jar is the default name for the build module (and isn't meaningful) + # If that's what we have, substitute the module name instead. These files + # aren't included on the device, so this name is synthetic anyway. + ifeq ($(module_leaf),javalib.jar) + module_leaf := $(LOCAL_MODULE).jar + endif module_installed_filename := \ - $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_JAVA_LIBRARIES))/$(notdir $(LOCAL_BUILT_MODULE)) + $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_JAVA_LIBRARIES))/$(module_leaf) else $(error Cannot determine where to install NOTICE file for $(LOCAL_MODULE)) endif # JAVA_LIBRARIES diff --git a/core/package.mk b/core/package.mk index 6b09bda..828f4c7 100644 --- a/core/package.mk +++ b/core/package.mk @@ -111,6 +111,11 @@ endif LOCAL_BUILT_MODULE_STEM := package.apk +proguard_options_file := $(package_expected_intermediates_COMMON)/proguard_options +ifneq ($(strip $(LOCAL_PROGUARD_ENABLED)),custom) + LOCAL_PROGUARD_FLAGS := -include $(proguard_options_file) $(LOCAL_PROGUARD_FLAGS) +endif + # The dex files go in the package, so we don't # want to install them separately for this module. old_DONT_INSTALL_DEX_FILES := $(DONT_INSTALL_DEX_FILES) @@ -143,6 +148,7 @@ endif $(R_file_stamp): PRIVATE_RESOURCE_PUBLICS_OUTPUT := \ $(intermediates.COMMON)/public_resources.xml +$(R_file_stamp): PRIVATE_PROGUARD_OPTIONS_FILE := $(proguard_options_file) $(R_file_stamp): $(all_res_assets) $(full_android_manifest) $(AAPT) | $(ACP) @echo "target R.java/Manifest.java: $(PRIVATE_MODULE) ($@)" @rm -f $@ @@ -164,6 +170,8 @@ $(R_file_stamp): $(all_res_assets) $(full_android_manifest) $(AAPT) | $(ACP) $(ACP) -fpt $$GENERATED_R_FILE $@ || exit 32; \ done; \ +$(proguard_options_file): $(R_file_stamp) + ifdef LOCAL_EXPORT_PACKAGE_RESOURCES # Put this module's resources into a PRODUCT-agnositc package that # other packages can use to build their own PRODUCT-agnostic R.java (etc.) diff --git a/core/pathmap.mk b/core/pathmap.mk index e281b9d..1ae663d 100644 --- a/core/pathmap.mk +++ b/core/pathmap.mk @@ -28,8 +28,8 @@ # pathmap_INCL := \ bluedroid:system/bluetooth/bluedroid/include \ - bluez-libs:external/bluez/libs/include \ - bluez-utils:external/bluez/utils \ + bluez:external/bluetooth/bluez \ + glib:external/bluetooth/glib \ bootloader:bootable/bootloader/legacy/include \ corecg:external/skia/include/core \ dbus:external/dbus \ @@ -76,7 +76,6 @@ FRAMEWORKS_BASE_SUBDIRS := \ $(addsuffix /java, \ core \ graphics \ - im \ location \ media \ opengl \ diff --git a/core/prebuilt.mk b/core/prebuilt.mk index 4cbbb99..b2bb07c 100644 --- a/core/prebuilt.mk +++ b/core/prebuilt.mk @@ -26,6 +26,8 @@ else prebuilt_module_is_a_library := endif +PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES)) + # Ensure that prebuilt .apks have been aligned. ifneq ($(filter APPS,$(LOCAL_MODULE_CLASS)),) $(LOCAL_BUILT_MODULE) : $(LOCAL_PATH)/$(LOCAL_SRC_FILES) | $(ZIPALIGN) diff --git a/core/prelink-linux-arm.map b/core/prelink-linux-arm.map index eb42ad3..64149bc 100644 --- a/core/prelink-linux-arm.map +++ b/core/prelink-linux-arm.map @@ -27,7 +27,7 @@ libsysutils.so 0xAF400000 liba2dp.so 0xAEE00000 audio.so 0xAED00000 input.so 0xAEC00000 -libhcid.so 0xAEB00000 +libbluetoothd.so 0xAEB00000 libbluedroid.so 0xAEA00000 libbluetooth.so 0xAE900000 libdbus.so 0xAE800000 @@ -50,8 +50,10 @@ libdvm.so 0xAD000000 # graphics libpixelflinger.so 0xACF00000 +# libcorecg is for backward-compatibility with donut libcorecg.so 0xACE00000 libsurfaceflinger.so 0xACD00000 +libGLES_android.so 0xACC80000 libagl.so 0xACC00000 libGLESv1_CM.so 0xACB00000 @@ -64,14 +66,21 @@ libacc.so 0xAC600000 libexif.so 0xAC500000 libui.so 0xAC400000 -libsgl.so 0xAC000000 +# libsgl is for backward-compatibility with donut +libsgl.so 0xAC200000 +libskia.so 0xAC000000 +librs_jni.so 0xABF00000 +libRS.so 0xAB900000 + # audio -libFLAC.so 0xAB900000 -libspeech.so 0xAB800000 +libFLAC.so 0xAB8A0000 +libaudiopolicy.so 0xAB880000 +libaudiopolicygeneric.so 0xAB800000 +libsoundpool.so 0xAB780000 libaudio.so 0xAB700000 -libsonivox.so 0xAB600000 -libsoundpool.so 0xAB500000 +libspeech.so 0xAB600000 +libsonivox.so 0xAB500000 libvorbisidec.so 0xAB400000 libmedia_jni.so 0xAB300000 libmediaplayerservice.so 0xAB280000 @@ -83,6 +92,7 @@ libaudioflinger.so 0xAB000000 libsqlite.so 0xAAC00000 libexpat.so 0xAAB00000 libwebcore.so 0xAA000000 +libbinder.so 0xA9D80000 libutils.so 0xA9D00000 libcameraservice.so 0xA9C80000 libhardware.so 0xA9C70000 @@ -92,13 +102,29 @@ libsystem_server.so 0xA9A00000 libime.so 0xA9800000 libgps.so 0xA9700000 libcamera.so 0xA9680000 -libqcamera.so 0xA9400000 +liboemcamera.so 0xA9400000 -# pv opencore libraries -libopencore_author.so 0xA7B00000 -libopencore_player.so 0xA7800000 -libopencore_common.so 0xA7300000 -libopencore_2way.so 0xA7000000 +# pv libraries +libpvasf.so 0xA7C26000 +libpvasfreg.so 0xA7C00000 +libomx_sharedlibrary.so 0xA7BA0000 +libopencore_download.so 0xA7B40000 +libopencore_downloadreg.so 0xA7B00000 +libopencore_net_support.so 0xA7A00000 +libopencore_rtsp.so 0xA7900000 +libopencore_rtspreg.so 0xA7890000 +libopencore_author.so 0xA7800000 +libomx_aacdec_sharedlibrary.so 0xA7700000 +libomx_amrdec_sharedlibrary.so 0xA76A0000 +libomx_amrenc_sharedlibrary.so 0xA7680000 +libomx_avcdec_sharedlibrary.so 0xA7660000 +libomx_avcenc_sharedlibrary.so 0xA7610000 +libomx_m4vdec_sharedlibrary.so 0xA75C0000 +libomx_m4venc_sharedlibrary.so 0xA7590000 +libomx_mp3dec_sharedlibrary.so 0xA7450000 +libopencore_mp4local.so 0xA7400000 +libopencore_mp4localreg.so 0xA7300000 +libopencore_player.so 0xA7000000 # opencore hardware support libmm-adspsvc.so 0xA6FFD000 @@ -137,3 +163,4 @@ librpc.so 0x9A400000 libtrace_test.so 0x9A300000 libsrec_jni.so 0x9A200000 libcerttool_jni.so 0x9A100000 + diff --git a/core/product.mk b/core/product.mk index adc81c3..a9a24d2 100644 --- a/core/product.mk +++ b/core/product.mk @@ -68,7 +68,8 @@ _product_var_list := \ PRODUCT_SDK_ADDON_NAME \ PRODUCT_SDK_ADDON_COPY_FILES \ PRODUCT_SDK_ADDON_COPY_MODULES \ - PRODUCT_SDK_ADDON_DOC_MODULE + PRODUCT_SDK_ADDON_DOC_MODULE \ + PRODUCT_DEFAULT_WIFI_CHANNELS define dump-product $(info ==== $(1) ====)\ diff --git a/core/product_config.mk b/core/product_config.mk index 7cfa5f4..0a693f4 100644 --- a/core/product_config.mk +++ b/core/product_config.mk @@ -193,6 +193,12 @@ ifneq (,$(extra_locales)) extra_locales := endif +# Default to medium-density assets. +# (Can be overridden in the device config, e.g.: PRODUCT_LOCALES += hdpi) +PRODUCT_LOCALES := $(strip \ + $(PRODUCT_LOCALES) \ + $(if $(filter %dpi,$(PRODUCT_LOCALES)),,mdpi)) + # Assemble the list of options. PRODUCT_AAPT_CONFIG := $(PRODUCT_LOCALES) @@ -214,6 +220,9 @@ ifndef PRODUCT_MANUFACTURER PRODUCT_MANUFACTURER := unknown endif +PRODUCT_DEFAULT_WIFI_CHANNELS := \ + $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEFAULT_WIFI_CHANNELS)) + # Which policy should this product use PRODUCT_POLICY := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_POLICY)) diff --git a/core/proguard.flags b/core/proguard.flags new file mode 100644 index 0000000..afd1548 --- /dev/null +++ b/core/proguard.flags @@ -0,0 +1,7 @@ +# see http://sourceforge.net/tracker/?func=detail&aid=2787465&group_id=54750&atid=474707 +-optimizations !code/simplification/arithmetic +-allowaccessmodification + +# Some classes in the libraries extend package private classes to chare common functionality +# that isn't explicitly part of the API +-dontskipnonpubliclibraryclasses diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk index 96ce0c1..478b572 100644 --- a/core/tasks/cts.mk +++ b/core/tasks/cts.mk @@ -68,7 +68,9 @@ CTS_CASE_LIST := \ CtsAppTestCases \ CtsContentTestCases \ CtsDatabaseTestCases \ - CtsGestureTestCases \ + CtsDpiTestCases \ + CtsDpiTestCases2 \ + CtsGestureTestCases \ CtsGraphicsTestCases \ CtsHardwareTestCases \ CtsLocationTestCases \ @@ -77,6 +79,7 @@ CTS_CASE_LIST := \ CtsPermissionTestCases \ CtsPermission2TestCases \ CtsProviderTestCases \ + CtsSpeechTestCases \ CtsTelephonyTestCases \ CtsTextTestCases \ CtsUtilTestCases \ diff --git a/core/tasks/product-graph.mk b/core/tasks/product-graph.mk index ead1406..6442252 100644 --- a/core/tasks/product-graph.mk +++ b/core/tasks/product-graph.mk @@ -15,21 +15,34 @@ # products_pdf := $(OUT_DIR)/products.pdf +products_graph := $(products_pdf:%.pdf=%.dot) -# This rule doens't include any nodes that don't inherit from -# anything or don't have anything inherit from them, to make the -# graph more readable. To add that, add this line to the rule -# below: -# $(foreach p,$(ALL_PRODUCTS), echo \"$(p)\";) \ - -$(products_pdf): +$(products_graph): + @echo Product graph DOT: $@ $(hide) ( \ echo 'digraph {'; \ + echo 'graph [ ratio=.5 ];'; \ $(foreach p,$(ALL_PRODUCTS), \ $(foreach d,$(PRODUCTS.$(strip $(p)).INHERITS_FROM), \ echo \"$(d)\" -\> \"$(p)\";)) \ + $(foreach prod, \ + $(sort $(foreach p,$(ALL_PRODUCTS), \ + $(foreach d,$(PRODUCTS.$(strip $(p)).INHERITS_FROM), \ + $(d))) \ + $(foreach p,$(ALL_PRODUCTS),$(p))), \ + echo \"$(prod)\" [ label=\"$(dir $(prod))\\n$(notdir $(prod))\"];) \ echo '}' \ - ) | dot -Tpdf -Nshape=box -o $@ + ) > $@ + +# This rule doesn't include any nodes that don't inherit from +# anything or don't have anything inherit from them, to make the +# graph more readable. To add that, add this line to the rule +# below: +# $(foreach p,$(ALL_PRODUCTS), echo \"$(p)\";) \ + +$(products_pdf): $(products_graph) + @echo Product graph PDF: $@ + dot -Tpdf -Nshape=box -o $@ $< product-graph: $(products_pdf) diff --git a/core/version_defaults.mk b/core/version_defaults.mk index 0e6cf5c..f10b331 100644 --- a/core/version_defaults.mk +++ b/core/version_defaults.mk @@ -41,7 +41,7 @@ ifeq "" "$(PLATFORM_VERSION)" # which is the version that we reveal to the end user. # Update this value when the platform version changes (rather # than overriding it somewhere else). Can be an arbitrary string. - PLATFORM_VERSION := 1.6 + PLATFORM_VERSION := 2.1 endif ifeq "" "$(PLATFORM_SDK_VERSION)" @@ -53,12 +53,12 @@ ifeq "" "$(PLATFORM_SDK_VERSION)" # intermediate builds). During development, this number remains at the # SDK version the branch is based on and PLATFORM_VERSION_CODENAME holds # the code-name of the new development work. - PLATFORM_SDK_VERSION := 4 + PLATFORM_SDK_VERSION := 6 endif ifeq "" "$(PLATFORM_VERSION_CODENAME)" - # If the build is not a final release build, then this is the current - # development code-name. If this is a final release build, it is simply "REL". + # This is the current development code-name, if the build is not a final + # release build. If this is a final release build, it is simply "REL". PLATFORM_VERSION_CODENAME := REL endif diff --git a/envsetup.sh b/envsetup.sh index 6325c3d..a32d0b2 100644 --- a/envsetup.sh +++ b/envsetup.sh @@ -102,7 +102,7 @@ function setpaths() # and in with the new CODE_REVIEWS= prebuiltdir=$(getprebuilt) - export ANDROID_EABI_TOOLCHAIN=$prebuiltdir/toolchain/arm-eabi-4.2.1/bin + export ANDROID_EABI_TOOLCHAIN=$prebuiltdir/toolchain/arm-eabi-4.4.0/bin export ANDROID_TOOLCHAIN=$ANDROID_EABI_TOOLCHAIN export ANDROID_QTOOLS=$T/development/emulator/qtools export ANDROID_BUILD_PATHS=:$(get_build_var ANDROID_BUILD_PATHS):$ANDROID_QTOOLS:$ANDROID_TOOLCHAIN:$ANDROID_EABI_TOOLCHAIN$CODE_REVIEWS @@ -596,6 +596,8 @@ function mm() # Find the closest Android.mk file. T=$(gettop) local M=$(findmakefile) + # Remove the path to top as the makefilepath needs to be relative + local M=`echo $M|sed 's:'$T'/::'` if [ ! "$T" ]; then echo "Couldn't locate the top of the tree. Try setting TOP." elif [ ! "$M" ]; then @@ -634,6 +636,7 @@ function mmm() ARGS="$ARGS showcommands" else echo "No Android.mk in $DIR." + return 1 fi fi done @@ -653,6 +656,26 @@ function croot() fi } +function cproj() +{ + TOPFILE=build/core/envsetup.mk + # We redirect cd to /dev/null in case it's aliased to + # a command that prints something as a side-effect + # (like pushd) + local HERE=$PWD + T= + while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do + T=$PWD + if [ -f "$T/Android.mk" ]; then + cd $T + return + fi + cd .. > /dev/null + done + cd $HERE > /dev/null + echo "can't find Android.mk" +} + function pid() { local EXE="$1" @@ -944,14 +967,7 @@ function runtest() echo "Couldn't locate the top of the tree. Try setting TOP." >&2 return fi - (cd "$T" && development/testrunner/runtest.py $@) -} - -# TODO: Remove this some time after 1 June 2009 -function runtest_py() -{ - echo "runtest_py is obsolete; use runtest instead" >&2 - return 1 + ("$T"/development/testrunner/runtest.py $@) } function godir () { diff --git a/target/board/Android.mk b/target/board/Android.mk index 64e3a74..fc2f651 100644 --- a/target/board/Android.mk +++ b/target/board/Android.mk @@ -20,11 +20,8 @@ else INSTALLED_KERNEL_TARGET := endif -ifneq ($(strip $(TARGET_NO_RADIOIMAGE)),true) - INSTALLED_RADIOIMAGE_TARGET := $(PRODUCT_OUT)/radio.img -else - INSTALLED_RADIOIMAGE_TARGET := -endif +# Use the add-radio-file function to add values to this variable. +INSTALLED_RADIOIMAGE_TARGET := ifeq (,$(wildcard $(TARGET_DEVICE_DIR)/AndroidBoard.mk)) ifeq (,$(wildcard $(TARGET_DEVICE_DIR)/Android.mk)) diff --git a/target/board/generic/BoardConfig.mk b/target/board/generic/BoardConfig.mk index 6ec2de3..2b72d01 100644 --- a/target/board/generic/BoardConfig.mk +++ b/target/board/generic/BoardConfig.mk @@ -1,12 +1,11 @@ # config.mk -# +# # Product-specific compile-time definitions. # # The generic product target doesn't have any hardware-specific pieces. TARGET_NO_BOOTLOADER := true TARGET_NO_KERNEL := true -TARGET_NO_RADIOIMAGE := true TARGET_CPU_ABI := armeabi HAVE_HTC_AUDIO_DRIVER := true BOARD_USES_GENERIC_AUDIO := true diff --git a/target/product/core.mk b/target/product/core.mk index 347b127..a93ba7c 100644 --- a/target/product/core.mk +++ b/target/product/core.mk @@ -3,7 +3,8 @@ PRODUCT_NAME := PRODUCT_DEVICE := PRODUCT_POLICY := android.policy_phone PRODUCT_PROPERTY_OVERRIDES := \ - ro.config.notification_sound=F1_New_SMS.ogg + ro.config.notification_sound=OnTheHunt.ogg \ + ro.config.alarm_alert=Alarm_Classic.ogg PRODUCT_PACKAGES := \ framework-res \ @@ -25,4 +26,3 @@ PRODUCT_PACKAGES := \ UserDictionaryProvider \ PackageInstaller \ Bugreport - diff --git a/target/product/generic.mk b/target/product/generic.mk index b9bc070..b4b1b09 100644 --- a/target/product/generic.mk +++ b/target/product/generic.mk @@ -3,11 +3,17 @@ # you should derive from generic_with_google.mk PRODUCT_PACKAGES := \ + AccountAndSyncSettings \ AlarmClock \ AlarmProvider \ + Bluetooth \ + Calculator \ Calendar \ Camera \ + CertInstaller \ DrmProvider \ + Email \ + Gallery \ LatinIME \ Mms \ Music \ @@ -15,7 +21,6 @@ PRODUCT_PACKAGES := \ Sync \ Updater \ CalendarProvider \ - SubscribedFeedsProvider \ SyncProvider $(call inherit-product, $(SRC_TARGET_DIR)/product/core.mk) diff --git a/target/product/generic_with_google.mk b/target/product/generic_with_google.mk index dddbbb7..ba30f15 100644 --- a/target/product/generic_with_google.mk +++ b/target/product/generic_with_google.mk @@ -4,13 +4,16 @@ # from generic.mk PRODUCT_PACKAGES := \ - GoogleContactsProvider \ + ContactsProvider \ + GoogleContactsSyncAdapter \ GoogleSubscribedFeedsProvider \ com.google.android.gtalkservice \ + com.google.android.datamessaging \ com.google.android.maps PRODUCT_COPY_FILES := \ vendor/google/frameworks/maps/com.google.android.maps.xml:system/etc/permissions/com.google.android.maps.xml \ + vendor/google/frameworks/datamessaging/com.google.android.datamessaging.xml:system/etc/permissions/com.google.android.datamessaging.xml \ vendor/google/apps/GTalkService/com.google.android.gtalkservice.xml:system/etc/permissions/com.google.android.gtalkservice.xml diff --git a/target/product/min_dev.mk b/target/product/min_dev.mk index 7d0fbe6..92ba973 100644 --- a/target/product/min_dev.mk +++ b/target/product/min_dev.mk @@ -1,7 +1,8 @@ PRODUCT_POLICY := android.policy_phone PRODUCT_PROPERTY_OVERRIDES := \ - ro.config.notification_sound=F1_New_SMS.ogg + ro.config.notification_sound=OnTheHunt.ogg \ + ro.config.alarm_alert=Alarm_Classic.ogg PRODUCT_BRAND := generic PRODUCT_NAME := min_dev PRODUCT_DEVICE := generic diff --git a/target/product/sdk.mk b/target/product/sdk.mk index d847377..f35de85 100644 --- a/target/product/sdk.mk +++ b/target/product/sdk.mk @@ -1,6 +1,7 @@ PRODUCT_PROPERTY_OVERRIDES := PRODUCT_PACKAGES := \ + AccountAndSyncSettings \ AlarmClock \ Camera \ Calculator \ @@ -28,7 +29,8 @@ PRODUCT_PACKAGES := \ SoftKeyboard PRODUCT_COPY_FILES := \ - development/data/etc/vold.conf:system/etc/vold.conf + development/data/etc/vold.conf:system/etc/vold.conf \ + frameworks/base/data/etc/android.hardware.camera.autofocus.xml:system/etc/permissions/android.hardware.camera.autofocus.xml $(call inherit-product, $(SRC_TARGET_DIR)/product/core.mk) @@ -37,6 +39,9 @@ PRODUCT_BRAND := generic PRODUCT_NAME := sdk PRODUCT_DEVICE := generic PRODUCT_LOCALES := \ + ldpi \ + hdpi \ + mdpi \ en_US \ en_GB \ en_CA \ @@ -64,3 +69,10 @@ PRODUCT_LOCALES := \ ru_RU \ ko_KR +# include available languages for TTS in the system image +include external/svox/pico/lang/PicoLangDeDeInSystem.mk +include external/svox/pico/lang/PicoLangEnGBInSystem.mk +include external/svox/pico/lang/PicoLangEnUsInSystem.mk +include external/svox/pico/lang/PicoLangEsEsInSystem.mk +include external/svox/pico/lang/PicoLangFrFrInSystem.mk +include external/svox/pico/lang/PicoLangItItInSystem.mk diff --git a/tools/adbs b/tools/adbs new file mode 100755 index 0000000..8b1fac6 --- /dev/null +++ b/tools/adbs @@ -0,0 +1,222 @@ +#!/usr/bin/env python + +# Copyright (C) 2009 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. + +import os +import re +import string +import sys + +############################################################################### +# match "#00 pc 0003f52e /system/lib/libdvm.so" for example +############################################################################### +trace_line = re.compile("(.*)(\#[0-9]+) (..) ([0-9a-f]{8}) ([^\r\n \t]*)") + +# returns a list containing the function name and the file/lineno +def CallAddr2Line(lib, addr): + global symbols_dir + global addr2line_cmd + global cppfilt_cmd + + if lib != "": + cmd = addr2line_cmd + \ + " -f -e " + symbols_dir + lib + " 0x" + addr + stream = os.popen(cmd) + lines = stream.readlines() + list = map(string.strip, lines) + else: + list = [] + if list != []: + # Name like "move_forward_type<JavaVMOption>" causes troubles + mangled_name = re.sub('<', '\<', list[0]); + mangled_name = re.sub('>', '\>', mangled_name); + cmd = cppfilt_cmd + " " + mangled_name + stream = os.popen(cmd) + list[0] = stream.readline() + stream.close() + list = map(string.strip, list) + else: + list = [ "(unknown)", "(unknown)" ] + return list + + +############################################################################### +# similar to CallAddr2Line, but using objdump to find out the name of the +# containing function of the specified address +############################################################################### +def CallObjdump(lib, addr): + global objdump_cmd + global symbols_dir + + unknown = "(unknown)" + uname = os.uname()[0] + if uname == "Darwin": + proc = os.uname()[-1] + if proc == "i386": + uname = "darwin-x86" + else: + uname = "darwin-ppc" + elif uname == "Linux": + uname = "linux-x86" + if lib != "": + next_addr = string.atoi(addr, 16) + 1 + cmd = objdump_cmd \ + + " -C -d --start-address=0x" + addr + " --stop-address=" \ + + str(next_addr) \ + + " " + symbols_dir + lib + stream = os.popen(cmd) + lines = stream.readlines() + map(string.strip, lines) + stream.close() + else: + return unknown + + # output looks like + # + # file format elf32-littlearm + # + # Disassembly of section .text: + # + # 0000833c <func+0x4>: + # 833c: 701a strb r2, [r3, #0] + # + # we want to extract the "func" part + num_lines = len(lines) + if num_lines < 2: + return unknown + func_name = lines[num_lines-2] + func_regexp = re.compile("(^.*\<)(.*)(\+.*\>:$)") + components = func_regexp.match(func_name) + if components is None: + return unknown + return components.group(2) + +############################################################################### +# determine the symbols directory in the local build +############################################################################### +def FindSymbolsDir(): + global symbols_dir + + try: + path = os.environ['ANDROID_PRODUCT_OUT'] + "/symbols" + except: + cmd = "CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core " \ + + "SRC_TARGET_DIR=build/target make -f build/core/envsetup.mk " \ + + "dumpvar-abs-TARGET_OUT_UNSTRIPPED" + stream = os.popen(cmd) + str = stream.read() + stream.close() + path = str.strip() + + if (not os.path.exists(path)): + print path + " not found!" + sys.exit(1) + + symbols_dir = path + +############################################################################### +# determine the path of binutils +############################################################################### +def SetupToolsPath(): + global addr2line_cmd + global objdump_cmd + global cppfilt_cmd + global symbols_dir + + uname = os.uname()[0] + if uname == "Darwin": + proc = os.uname()[-1] + if proc == "i386": + uname = "darwin-x86" + else: + uname = "darwin-ppc" + elif uname == "Linux": + uname = "linux-x86" + prefix = "./prebuilt/" + uname + "/toolchain/arm-eabi-4.4.0/bin/" + addr2line_cmd = prefix + "arm-eabi-addr2line" + + if (not os.path.exists(addr2line_cmd)): + try: + prefix = os.environ['ANDROID_BUILD_TOP'] + "/prebuilt/" + uname + \ + "/toolchain/arm-eabi-4.4.0/bin/" + except: + prefix = ""; + + addr2line_cmd = prefix + "arm-eabi-addr2line" + if (not os.path.exists(addr2line_cmd)): + print addr2line_cmd + " not found!" + sys.exit(1) + + objdump_cmd = prefix + "arm-eabi-objdump" + cppfilt_cmd = prefix + "arm-eabi-c++filt" + +############################################################################### +# look up the function and file/line number for a raw stack trace line +# groups[0]: log tag +# groups[1]: stack level +# groups[2]: "pc" +# groups[3]: code address +# groups[4]: library name +############################################################################### +def SymbolTranslation(groups): + lib_name = groups[4] + code_addr = groups[3] + caller = CallObjdump(lib_name, code_addr) + func_line_pair = CallAddr2Line(lib_name, code_addr) + + # If a callee is inlined to the caller, objdump will see the caller's + # address but addr2line will report the callee's address. So the printed + # format is desgined to be "caller<-callee file:line" + if (func_line_pair[0] != caller): + print groups[0] + groups[1] + " " + caller + "<-" + \ + ' '.join(func_line_pair[:]) + " " + else: + print groups[0] + groups[1] + " " + ' '.join(func_line_pair[:]) + " " + +############################################################################### + +if __name__ == '__main__': + # pass the options to adb + adb_cmd = "adb " + ' '.join(sys.argv[1:]) + + # setup addr2line_cmd and objdump_cmd + SetupToolsPath() + + # setup the symbols directory + FindSymbolsDir() + + # invoke the adb command and filter its output + stream = os.popen(adb_cmd) + while (True): + line = stream.readline() + + # EOF reached + if (line == ''): + break + + # remove the trailing \n + line = line.strip() + + # see if this is a stack trace line + match = trace_line.match(line) + if (match): + groups = match.groups() + # translate raw address into symbols + SymbolTranslation(groups) + else: + print line + + # adb itself aborts + stream.close() diff --git a/tools/apicheck/src/com/android/apicheck/ApiCheck.java b/tools/apicheck/src/com/android/apicheck/ApiCheck.java index 20a98ce..c8272dd 100644 --- a/tools/apicheck/src/com/android/apicheck/ApiCheck.java +++ b/tools/apicheck/src/com/android/apicheck/ApiCheck.java @@ -127,7 +127,7 @@ public class ApiCheck { } private static class MakeHandler extends DefaultHandler { - + private ApiInfo mApi; private PackageInfo mCurrentPackage; private ClassInfo mCurrentClass; @@ -139,8 +139,9 @@ public class ApiCheck { super(); mApi = new ApiInfo(); } - - public void startElement(String uri, String localName, String qName, + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) { if (qName.equals("package")) { mCurrentPackage = new PackageInfo(attributes.getValue("name"), @@ -150,25 +151,25 @@ public class ApiCheck { // push the old outer scope for later recovery, then set // up the new current class object mClassScope.push(mCurrentClass); - mCurrentClass = new ClassInfo(attributes.getValue("name"), + mCurrentClass = new ClassInfo(attributes.getValue("name"), mCurrentPackage, attributes.getValue("extends") , - qName.equals("interface"), + qName.equals("interface"), Boolean.valueOf( attributes.getValue("abstract")), Boolean.valueOf( attributes.getValue("static")), Boolean.valueOf( attributes.getValue("final")), - attributes.getValue("deprecated"), + attributes.getValue("deprecated"), attributes.getValue("visibility"), SourcePositionInfo.fromXml(attributes.getValue("source")), mCurrentClass); } else if (qName.equals("method")) { - mCurrentMethod = new MethodInfo(attributes.getValue("name"), + mCurrentMethod = new MethodInfo(attributes.getValue("name"), attributes.getValue("return") , Boolean.valueOf( - attributes.getValue("abstract")), + attributes.getValue("abstract")), Boolean.valueOf( attributes.getValue("native")), Boolean.valueOf( @@ -178,11 +179,11 @@ public class ApiCheck { Boolean.valueOf( attributes.getValue("final")), attributes.getValue("deprecated"), - attributes.getValue("visibility"), + attributes.getValue("visibility"), SourcePositionInfo.fromXml(attributes.getValue("source")), mCurrentClass); } else if (qName.equals("constructor")) { - mCurrentMethod = new ConstructorInfo(attributes.getValue("name"), + mCurrentMethod = new ConstructorInfo(attributes.getValue("name"), attributes.getValue("type") , Boolean.valueOf( attributes.getValue("static")), @@ -193,7 +194,7 @@ public class ApiCheck { SourcePositionInfo.fromXml(attributes.getValue("source")), mCurrentClass); } else if (qName.equals("field")) { - FieldInfo fInfo = new FieldInfo(attributes.getValue("name"), + FieldInfo fInfo = new FieldInfo(attributes.getValue("name"), attributes.getValue("type") , Boolean.valueOf( attributes.getValue("transient")), @@ -218,6 +219,8 @@ public class ApiCheck { mCurrentClass.addInterface(attributes.getValue("name")); } } + + @Override public void endElement(String uri, String localName, String qName) { if (qName.equals("method")) { mCurrentClass.addMethod((MethodInfo) mCurrentMethod); diff --git a/tools/apicheck/src/com/android/apicheck/Errors.java b/tools/apicheck/src/com/android/apicheck/Errors.java index d7013e3..b0b620e 100644 --- a/tools/apicheck/src/com/android/apicheck/Errors.java +++ b/tools/apicheck/src/com/android/apicheck/Errors.java @@ -41,6 +41,7 @@ public class Errors return this.msg.compareTo(that.msg); } + @Override public String toString() { return this.pos.toString() + this.msg; } @@ -115,7 +116,7 @@ public class Errors public static Error CHANGED_CLASS = new Error(23, WARNING); public static Error CHANGED_DEPRECATED = new Error(24, WARNING); public static Error CHANGED_SYNCHRONIZED = new Error(25, ERROR); - + public static Error[] ERRORS = { PARSE_ERROR, ADDED_PACKAGE, diff --git a/tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java b/tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java index 477c1d3..276771b 100644 --- a/tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java +++ b/tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java @@ -80,6 +80,7 @@ public class SourcePositionInfo implements Comparable return new SourcePositionInfo(that.file, line, 0); } + @Override public String toString() { if (this.file == null) { diff --git a/tools/applypatch/Android.mk b/tools/applypatch/Android.mk index 5796cef..d20d6c8 100644 --- a/tools/applypatch/Android.mk +++ b/tools/applypatch/Android.mk @@ -17,7 +17,7 @@ ifneq ($(TARGET_SIMULATOR),true) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := applypatch.c bsdiff.c freecache.c imgpatch.c utils.c +LOCAL_SRC_FILES := applypatch.c bspatch.c freecache.c imgpatch.c utils.c LOCAL_MODULE := libapplypatch LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES += external/bzip2 external/zlib bootable/recovery @@ -47,12 +47,12 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) -LOCAL_SRC_FILES := imgdiff.c utils.c +LOCAL_SRC_FILES := imgdiff.c utils.c bsdiff.c LOCAL_MODULE := imgdiff LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_TAGS := eng -LOCAL_C_INCLUDES += external/zlib -LOCAL_STATIC_LIBRARIES += libz +LOCAL_C_INCLUDES += external/zlib external/bzip2 +LOCAL_STATIC_LIBRARIES += libz libbz include $(BUILD_HOST_EXECUTABLE) diff --git a/tools/applypatch/bsdiff.c b/tools/applypatch/bsdiff.c index d5cd617..b6d342b 100644 --- a/tools/applypatch/bsdiff.c +++ b/tools/applypatch/bsdiff.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2009 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. @@ -14,239 +14,397 @@ * limitations under the License. */ -// This file is a nearly line-for-line copy of bspatch.c from the -// bsdiff-4.3 distribution; the primary differences being how the -// input and output data are read and the error handling. Running -// applypatch with the -l option will display the bsdiff license -// notice. +/* + * Most of this code comes from bsdiff.c from the bsdiff-4.3 + * distribution, which is: + */ +/*- + * Copyright 2003-2005 Colin Percival + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> + +#include <bzlib.h> +#include <err.h> +#include <fcntl.h> #include <stdio.h> -#include <sys/stat.h> -#include <errno.h> -#include <unistd.h> +#include <stdlib.h> #include <string.h> +#include <unistd.h> -#include <bzlib.h> +#define MIN(x,y) (((x)<(y)) ? (x) : (y)) -#include "mincrypt/sha.h" -#include "applypatch.h" - -void ShowBSDiffLicense() { - puts("The bsdiff library used herein is:\n" - "\n" - "Copyright 2003-2005 Colin Percival\n" - "All rights reserved\n" - "\n" - "Redistribution and use in source and binary forms, with or without\n" - "modification, are permitted providing that the following conditions\n" - "are met:\n" - "1. Redistributions of source code must retain the above copyright\n" - " notice, this list of conditions and the following disclaimer.\n" - "2. Redistributions in binary form must reproduce the above copyright\n" - " notice, this list of conditions and the following disclaimer in the\n" - " documentation and/or other materials provided with the distribution.\n" - "\n" - "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" - "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n" - "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n" - "ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n" - "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n" - "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n" - "OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n" - "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n" - "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n" - "IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n" - "POSSIBILITY OF SUCH DAMAGE.\n" - "\n------------------\n\n" - "This program uses Julian R Seward's \"libbzip2\" library, available\n" - "from http://www.bzip.org/.\n" - ); +static void split(off_t *I,off_t *V,off_t start,off_t len,off_t h) +{ + off_t i,j,k,x,tmp,jj,kk; + + if(len<16) { + for(k=start;k<start+len;k+=j) { + j=1;x=V[I[k]+h]; + for(i=1;k+i<start+len;i++) { + if(V[I[k+i]+h]<x) { + x=V[I[k+i]+h]; + j=0; + }; + if(V[I[k+i]+h]==x) { + tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp; + j++; + }; + }; + for(i=0;i<j;i++) V[I[k+i]]=k+j-1; + if(j==1) I[k]=-1; + }; + return; + }; + + x=V[I[start+len/2]+h]; + jj=0;kk=0; + for(i=start;i<start+len;i++) { + if(V[I[i]+h]<x) jj++; + if(V[I[i]+h]==x) kk++; + }; + jj+=start;kk+=jj; + + i=start;j=0;k=0; + while(i<jj) { + if(V[I[i]+h]<x) { + i++; + } else if(V[I[i]+h]==x) { + tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp; + j++; + } else { + tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp; + k++; + }; + }; + + while(jj+j<kk) { + if(V[I[jj+j]+h]==x) { + j++; + } else { + tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp; + k++; + }; + }; + + if(jj>start) split(I,V,start,jj-start,h); + + for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1; + if(jj==kk-1) I[jj]=-1; + + if(start+len>kk) split(I,V,kk,start+len-kk,h); } -static off_t offtin(u_char *buf) +static void qsufsort(off_t *I,off_t *V,u_char *old,off_t oldsize) { - off_t y; + off_t buckets[256]; + off_t i,h,len; + + for(i=0;i<256;i++) buckets[i]=0; + for(i=0;i<oldsize;i++) buckets[old[i]]++; + for(i=1;i<256;i++) buckets[i]+=buckets[i-1]; + for(i=255;i>0;i--) buckets[i]=buckets[i-1]; + buckets[0]=0; + + for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i; + I[0]=oldsize; + for(i=0;i<oldsize;i++) V[i]=buckets[old[i]]; + V[oldsize]=0; + for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1; + I[0]=-1; + + for(h=1;I[0]!=-(oldsize+1);h+=h) { + len=0; + for(i=0;i<oldsize+1;) { + if(I[i]<0) { + len-=I[i]; + i-=I[i]; + } else { + if(len) I[i-len]=-len; + len=V[I[i]]+1-i; + split(I,V,i,len,h); + i+=len; + len=0; + }; + }; + if(len) I[i-len]=-len; + }; + + for(i=0;i<oldsize+1;i++) I[V[i]]=i; +} - y=buf[7]&0x7F; - y=y*256;y+=buf[6]; - y=y*256;y+=buf[5]; - y=y*256;y+=buf[4]; - y=y*256;y+=buf[3]; - y=y*256;y+=buf[2]; - y=y*256;y+=buf[1]; - y=y*256;y+=buf[0]; +static off_t matchlen(u_char *old,off_t oldsize,u_char *new,off_t newsize) +{ + off_t i; - if(buf[7]&0x80) y=-y; + for(i=0;(i<oldsize)&&(i<newsize);i++) + if(old[i]!=new[i]) break; - return y; + return i; } +static off_t search(off_t *I,u_char *old,off_t oldsize, + u_char *new,off_t newsize,off_t st,off_t en,off_t *pos) +{ + off_t x,y; + + if(en-st<2) { + x=matchlen(old+I[st],oldsize-I[st],new,newsize); + y=matchlen(old+I[en],oldsize-I[en],new,newsize); + + if(x>y) { + *pos=I[st]; + return x; + } else { + *pos=I[en]; + return y; + } + }; + + x=st+(en-st)/2; + if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) { + return search(I,old,oldsize,new,newsize,x,en,pos); + } else { + return search(I,old,oldsize,new,newsize,st,x,pos); + }; +} -int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, - const char* patch_filename, ssize_t patch_offset, - SinkFn sink, void* token, SHA_CTX* ctx) { +static void offtout(off_t x,u_char *buf) +{ + off_t y; - unsigned char* new_data; - ssize_t new_size; - if (ApplyBSDiffPatchMem(old_data, old_size, patch_filename, patch_offset, - &new_data, &new_size) != 0) { - return -1; - } + if(x<0) y=-x; else y=x; - if (sink(new_data, new_size, token) < new_size) { - fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno)); - return 1; - } - if (ctx) { - SHA_update(ctx, new_data, new_size); - } - free(new_data); + buf[0]=y%256;y-=buf[0]; + y=y/256;buf[1]=y%256;y-=buf[1]; + y=y/256;buf[2]=y%256;y-=buf[2]; + y=y/256;buf[3]=y%256;y-=buf[3]; + y=y/256;buf[4]=y%256;y-=buf[4]; + y=y/256;buf[5]=y%256;y-=buf[5]; + y=y/256;buf[6]=y%256;y-=buf[6]; + y=y/256;buf[7]=y%256; - return 0; + if(x<0) buf[7]|=0x80; } -int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, - const char* patch_filename, ssize_t patch_offset, - unsigned char** new_data, ssize_t* new_size) { - - FILE* f; - if ((f = fopen(patch_filename, "rb")) == NULL) { - fprintf(stderr, "failed to open patch file\n"); - return 1; - } - - // File format: - // 0 8 "BSDIFF40" - // 8 8 X - // 16 8 Y - // 24 8 sizeof(newfile) - // 32 X bzip2(control block) - // 32+X Y bzip2(diff block) - // 32+X+Y ??? bzip2(extra block) - // with control block a set of triples (x,y,z) meaning "add x bytes - // from oldfile to x bytes from the diff block; copy y bytes from the - // extra block; seek forwards in oldfile by z bytes". - - fseek(f, patch_offset, SEEK_SET); - - unsigned char header[32]; - if (fread(header, 1, 32, f) < 32) { - fprintf(stderr, "failed to read patch file header\n"); - return 1; - } - - if (memcmp(header, "BSDIFF40", 8) != 0) { - fprintf(stderr, "corrupt bsdiff patch file header (magic number)\n"); - return 1; - } - - ssize_t ctrl_len, data_len; - ctrl_len = offtin(header+8); - data_len = offtin(header+16); - *new_size = offtin(header+24); - - if (ctrl_len < 0 || data_len < 0 || *new_size < 0) { - fprintf(stderr, "corrupt patch file header (data lengths)\n"); - return 1; - } - - fclose(f); - - int bzerr; - -#define OPEN_AT(f, bzf, offset) \ - FILE* f; \ - BZFILE* bzf; \ - if ((f = fopen(patch_filename, "rb")) == NULL) { \ - fprintf(stderr, "failed to open patch file\n"); \ - return 1; \ - } \ - if (fseeko(f, offset+patch_offset, SEEK_SET)) { \ - fprintf(stderr, "failed to seek in patch file\n"); \ - return 1; \ - } \ - if ((bzf = BZ2_bzReadOpen(&bzerr, f, 0, 0, NULL, 0)) == NULL) { \ - fprintf(stderr, "failed to bzReadOpen in patch file (%d)\n", bzerr); \ - return 1; \ - } - - OPEN_AT(cpf, cpfbz2, 32); - OPEN_AT(dpf, dpfbz2, 32+ctrl_len); - OPEN_AT(epf, epfbz2, 32+ctrl_len+data_len); - -#undef OPEN_AT - - *new_data = malloc(*new_size); - if (*new_data == NULL) { - fprintf(stderr, "failed to allocate %d bytes of memory for output file\n", - (int)*new_size); - return 1; - } - - off_t oldpos = 0, newpos = 0; - off_t ctrl[3]; - off_t len_read; - int i; - unsigned char buf[8]; - while (newpos < *new_size) { - // Read control data - for (i = 0; i < 3; ++i) { - len_read = BZ2_bzRead(&bzerr, cpfbz2, buf, 8); - if (len_read < 8 || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) { - fprintf(stderr, "corrupt patch (read control)\n"); - return 1; - } - ctrl[i] = offtin(buf); - } - - // Sanity check - if (newpos + ctrl[0] > *new_size) { - fprintf(stderr, "corrupt patch (new file overrun)\n"); - return 1; - } - - // Read diff string - len_read = BZ2_bzRead(&bzerr, dpfbz2, *new_data + newpos, ctrl[0]); - if (len_read < ctrl[0] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) { - fprintf(stderr, "corrupt patch (read diff)\n"); - return 1; - } - - // Add old data to diff string - for (i = 0; i < ctrl[0]; ++i) { - if ((oldpos+i >= 0) && (oldpos+i < old_size)) { - (*new_data)[newpos+i] += old_data[oldpos+i]; - } - } - - // Adjust pointers - newpos += ctrl[0]; - oldpos += ctrl[0]; - - // Sanity check - if (newpos + ctrl[1] > *new_size) { - fprintf(stderr, "corrupt patch (new file overrun)\n"); - return 1; - } - - // Read extra string - len_read = BZ2_bzRead(&bzerr, epfbz2, *new_data + newpos, ctrl[1]); - if (len_read < ctrl[1] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) { - fprintf(stderr, "corrupt patch (read extra)\n"); - return 1; - } - - // Adjust pointers - newpos += ctrl[1]; - oldpos += ctrl[2]; - } - - BZ2_bzReadClose(&bzerr, cpfbz2); - BZ2_bzReadClose(&bzerr, dpfbz2); - BZ2_bzReadClose(&bzerr, epfbz2); - fclose(cpf); - fclose(dpf); - fclose(epf); - - return 0; +// This is main() from bsdiff.c, with the following changes: +// +// - old, oldsize, new, newsize are arguments; we don't load this +// data from files. old and new are owned by the caller; we +// don't free them at the end. +// +// - the "I" block of memory is owned by the caller, who passes a +// pointer to *I, which can be NULL. This way if we call +// bsdiff() multiple times with the same 'old' data, we only do +// the qsufsort() step the first time. +// +int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize, + const char* patch_filename) +{ + int fd; + off_t *I; + off_t scan,pos,len; + off_t lastscan,lastpos,lastoffset; + off_t oldscore,scsc; + off_t s,Sf,lenf,Sb,lenb; + off_t overlap,Ss,lens; + off_t i; + off_t dblen,eblen; + u_char *db,*eb; + u_char buf[8]; + u_char header[32]; + FILE * pf; + BZFILE * pfbz2; + int bz2err; + + if (*IP == NULL) { + off_t* V; + *IP = malloc((oldsize+1) * sizeof(off_t)); + V = malloc((oldsize+1) * sizeof(off_t)); + qsufsort(*IP, V, old, oldsize); + free(V); + } + I = *IP; + + if(((db=malloc(newsize+1))==NULL) || + ((eb=malloc(newsize+1))==NULL)) err(1,NULL); + dblen=0; + eblen=0; + + /* Create the patch file */ + if ((pf = fopen(patch_filename, "w")) == NULL) + err(1, "%s", patch_filename); + + /* Header is + 0 8 "BSDIFF40" + 8 8 length of bzip2ed ctrl block + 16 8 length of bzip2ed diff block + 24 8 length of new file */ + /* File is + 0 32 Header + 32 ?? Bzip2ed ctrl block + ?? ?? Bzip2ed diff block + ?? ?? Bzip2ed extra block */ + memcpy(header,"BSDIFF40",8); + offtout(0, header + 8); + offtout(0, header + 16); + offtout(newsize, header + 24); + if (fwrite(header, 32, 1, pf) != 1) + err(1, "fwrite(%s)", patch_filename); + + /* Compute the differences, writing ctrl as we go */ + if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL) + errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err); + scan=0;len=0; + lastscan=0;lastpos=0;lastoffset=0; + while(scan<newsize) { + oldscore=0; + + for(scsc=scan+=len;scan<newsize;scan++) { + len=search(I,old,oldsize,new+scan,newsize-scan, + 0,oldsize,&pos); + + for(;scsc<scan+len;scsc++) + if((scsc+lastoffset<oldsize) && + (old[scsc+lastoffset] == new[scsc])) + oldscore++; + + if(((len==oldscore) && (len!=0)) || + (len>oldscore+8)) break; + + if((scan+lastoffset<oldsize) && + (old[scan+lastoffset] == new[scan])) + oldscore--; + }; + + if((len!=oldscore) || (scan==newsize)) { + s=0;Sf=0;lenf=0; + for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) { + if(old[lastpos+i]==new[lastscan+i]) s++; + i++; + if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; }; + }; + + lenb=0; + if(scan<newsize) { + s=0;Sb=0; + for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) { + if(old[pos-i]==new[scan-i]) s++; + if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; }; + }; + }; + + if(lastscan+lenf>scan-lenb) { + overlap=(lastscan+lenf)-(scan-lenb); + s=0;Ss=0;lens=0; + for(i=0;i<overlap;i++) { + if(new[lastscan+lenf-overlap+i]== + old[lastpos+lenf-overlap+i]) s++; + if(new[scan-lenb+i]== + old[pos-lenb+i]) s--; + if(s>Ss) { Ss=s; lens=i+1; }; + }; + + lenf+=lens-overlap; + lenb-=lens; + }; + + for(i=0;i<lenf;i++) + db[dblen+i]=new[lastscan+i]-old[lastpos+i]; + for(i=0;i<(scan-lenb)-(lastscan+lenf);i++) + eb[eblen+i]=new[lastscan+lenf+i]; + + dblen+=lenf; + eblen+=(scan-lenb)-(lastscan+lenf); + + offtout(lenf,buf); + BZ2_bzWrite(&bz2err, pfbz2, buf, 8); + if (bz2err != BZ_OK) + errx(1, "BZ2_bzWrite, bz2err = %d", bz2err); + + offtout((scan-lenb)-(lastscan+lenf),buf); + BZ2_bzWrite(&bz2err, pfbz2, buf, 8); + if (bz2err != BZ_OK) + errx(1, "BZ2_bzWrite, bz2err = %d", bz2err); + + offtout((pos-lenb)-(lastpos+lenf),buf); + BZ2_bzWrite(&bz2err, pfbz2, buf, 8); + if (bz2err != BZ_OK) + errx(1, "BZ2_bzWrite, bz2err = %d", bz2err); + + lastscan=scan-lenb; + lastpos=pos-lenb; + lastoffset=pos-scan; + }; + }; + BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL); + if (bz2err != BZ_OK) + errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err); + + /* Compute size of compressed ctrl data */ + if ((len = ftello(pf)) == -1) + err(1, "ftello"); + offtout(len-32, header + 8); + + /* Write compressed diff data */ + if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL) + errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err); + BZ2_bzWrite(&bz2err, pfbz2, db, dblen); + if (bz2err != BZ_OK) + errx(1, "BZ2_bzWrite, bz2err = %d", bz2err); + BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL); + if (bz2err != BZ_OK) + errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err); + + /* Compute size of compressed diff data */ + if ((newsize = ftello(pf)) == -1) + err(1, "ftello"); + offtout(newsize - len, header + 16); + + /* Write compressed extra data */ + if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL) + errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err); + BZ2_bzWrite(&bz2err, pfbz2, eb, eblen); + if (bz2err != BZ_OK) + errx(1, "BZ2_bzWrite, bz2err = %d", bz2err); + BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL); + if (bz2err != BZ_OK) + errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err); + + /* Seek to the beginning, write the header, and close the file */ + if (fseeko(pf, 0, SEEK_SET)) + err(1, "fseeko"); + if (fwrite(header, 32, 1, pf) != 1) + err(1, "fwrite(%s)", patch_filename); + if (fclose(pf)) + err(1, "fclose"); + + /* Free the memory we used */ + free(db); + free(eb); + + return 0; } diff --git a/tools/applypatch/bspatch.c b/tools/applypatch/bspatch.c new file mode 100644 index 0000000..d5cd617 --- /dev/null +++ b/tools/applypatch/bspatch.c @@ -0,0 +1,252 @@ +/* + * 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. + */ + +// This file is a nearly line-for-line copy of bspatch.c from the +// bsdiff-4.3 distribution; the primary differences being how the +// input and output data are read and the error handling. Running +// applypatch with the -l option will display the bsdiff license +// notice. + +#include <stdio.h> +#include <sys/stat.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> + +#include <bzlib.h> + +#include "mincrypt/sha.h" +#include "applypatch.h" + +void ShowBSDiffLicense() { + puts("The bsdiff library used herein is:\n" + "\n" + "Copyright 2003-2005 Colin Percival\n" + "All rights reserved\n" + "\n" + "Redistribution and use in source and binary forms, with or without\n" + "modification, are permitted providing that the following conditions\n" + "are met:\n" + "1. Redistributions of source code must retain the above copyright\n" + " notice, this list of conditions and the following disclaimer.\n" + "2. Redistributions in binary form must reproduce the above copyright\n" + " notice, this list of conditions and the following disclaimer in the\n" + " documentation and/or other materials provided with the distribution.\n" + "\n" + "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" + "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n" + "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n" + "ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n" + "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n" + "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n" + "OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n" + "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n" + "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n" + "IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n" + "POSSIBILITY OF SUCH DAMAGE.\n" + "\n------------------\n\n" + "This program uses Julian R Seward's \"libbzip2\" library, available\n" + "from http://www.bzip.org/.\n" + ); +} + +static off_t offtin(u_char *buf) +{ + off_t y; + + y=buf[7]&0x7F; + y=y*256;y+=buf[6]; + y=y*256;y+=buf[5]; + y=y*256;y+=buf[4]; + y=y*256;y+=buf[3]; + y=y*256;y+=buf[2]; + y=y*256;y+=buf[1]; + y=y*256;y+=buf[0]; + + if(buf[7]&0x80) y=-y; + + return y; +} + + +int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, + const char* patch_filename, ssize_t patch_offset, + SinkFn sink, void* token, SHA_CTX* ctx) { + + unsigned char* new_data; + ssize_t new_size; + if (ApplyBSDiffPatchMem(old_data, old_size, patch_filename, patch_offset, + &new_data, &new_size) != 0) { + return -1; + } + + if (sink(new_data, new_size, token) < new_size) { + fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno)); + return 1; + } + if (ctx) { + SHA_update(ctx, new_data, new_size); + } + free(new_data); + + return 0; +} + +int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, + const char* patch_filename, ssize_t patch_offset, + unsigned char** new_data, ssize_t* new_size) { + + FILE* f; + if ((f = fopen(patch_filename, "rb")) == NULL) { + fprintf(stderr, "failed to open patch file\n"); + return 1; + } + + // File format: + // 0 8 "BSDIFF40" + // 8 8 X + // 16 8 Y + // 24 8 sizeof(newfile) + // 32 X bzip2(control block) + // 32+X Y bzip2(diff block) + // 32+X+Y ??? bzip2(extra block) + // with control block a set of triples (x,y,z) meaning "add x bytes + // from oldfile to x bytes from the diff block; copy y bytes from the + // extra block; seek forwards in oldfile by z bytes". + + fseek(f, patch_offset, SEEK_SET); + + unsigned char header[32]; + if (fread(header, 1, 32, f) < 32) { + fprintf(stderr, "failed to read patch file header\n"); + return 1; + } + + if (memcmp(header, "BSDIFF40", 8) != 0) { + fprintf(stderr, "corrupt bsdiff patch file header (magic number)\n"); + return 1; + } + + ssize_t ctrl_len, data_len; + ctrl_len = offtin(header+8); + data_len = offtin(header+16); + *new_size = offtin(header+24); + + if (ctrl_len < 0 || data_len < 0 || *new_size < 0) { + fprintf(stderr, "corrupt patch file header (data lengths)\n"); + return 1; + } + + fclose(f); + + int bzerr; + +#define OPEN_AT(f, bzf, offset) \ + FILE* f; \ + BZFILE* bzf; \ + if ((f = fopen(patch_filename, "rb")) == NULL) { \ + fprintf(stderr, "failed to open patch file\n"); \ + return 1; \ + } \ + if (fseeko(f, offset+patch_offset, SEEK_SET)) { \ + fprintf(stderr, "failed to seek in patch file\n"); \ + return 1; \ + } \ + if ((bzf = BZ2_bzReadOpen(&bzerr, f, 0, 0, NULL, 0)) == NULL) { \ + fprintf(stderr, "failed to bzReadOpen in patch file (%d)\n", bzerr); \ + return 1; \ + } + + OPEN_AT(cpf, cpfbz2, 32); + OPEN_AT(dpf, dpfbz2, 32+ctrl_len); + OPEN_AT(epf, epfbz2, 32+ctrl_len+data_len); + +#undef OPEN_AT + + *new_data = malloc(*new_size); + if (*new_data == NULL) { + fprintf(stderr, "failed to allocate %d bytes of memory for output file\n", + (int)*new_size); + return 1; + } + + off_t oldpos = 0, newpos = 0; + off_t ctrl[3]; + off_t len_read; + int i; + unsigned char buf[8]; + while (newpos < *new_size) { + // Read control data + for (i = 0; i < 3; ++i) { + len_read = BZ2_bzRead(&bzerr, cpfbz2, buf, 8); + if (len_read < 8 || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) { + fprintf(stderr, "corrupt patch (read control)\n"); + return 1; + } + ctrl[i] = offtin(buf); + } + + // Sanity check + if (newpos + ctrl[0] > *new_size) { + fprintf(stderr, "corrupt patch (new file overrun)\n"); + return 1; + } + + // Read diff string + len_read = BZ2_bzRead(&bzerr, dpfbz2, *new_data + newpos, ctrl[0]); + if (len_read < ctrl[0] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) { + fprintf(stderr, "corrupt patch (read diff)\n"); + return 1; + } + + // Add old data to diff string + for (i = 0; i < ctrl[0]; ++i) { + if ((oldpos+i >= 0) && (oldpos+i < old_size)) { + (*new_data)[newpos+i] += old_data[oldpos+i]; + } + } + + // Adjust pointers + newpos += ctrl[0]; + oldpos += ctrl[0]; + + // Sanity check + if (newpos + ctrl[1] > *new_size) { + fprintf(stderr, "corrupt patch (new file overrun)\n"); + return 1; + } + + // Read extra string + len_read = BZ2_bzRead(&bzerr, epfbz2, *new_data + newpos, ctrl[1]); + if (len_read < ctrl[1] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) { + fprintf(stderr, "corrupt patch (read extra)\n"); + return 1; + } + + // Adjust pointers + newpos += ctrl[1]; + oldpos += ctrl[2]; + } + + BZ2_bzReadClose(&bzerr, cpfbz2); + BZ2_bzReadClose(&bzerr, dpfbz2); + BZ2_bzReadClose(&bzerr, epfbz2); + fclose(cpf); + fclose(dpf); + fclose(epf); + + return 0; +} diff --git a/tools/applypatch/imgdiff.c b/tools/applypatch/imgdiff.c index 51835b4..6ff9b6f 100644 --- a/tools/applypatch/imgdiff.c +++ b/tools/applypatch/imgdiff.c @@ -119,6 +119,7 @@ #include <string.h> #include <sys/stat.h> #include <unistd.h> +#include <sys/types.h> #include "zlib.h" #include "imgdiff.h" @@ -134,6 +135,8 @@ typedef struct { size_t source_start; size_t source_len; + off_t* I; // used by bsdiff + // --- for CHUNK_DEFLATE chunks only: --- // original (compressed) deflate data @@ -167,6 +170,10 @@ static int fileentry_compare(const void* a, const void* b) { } } +// from bsdiff.c +int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize, + const char* patch_filename); + unsigned char* ReadZip(const char* filename, int* num_chunks, ImageChunk** chunks, int include_pseudo_chunk) { @@ -278,6 +285,7 @@ unsigned char* ReadZip(const char* filename, curr->len = st.st_size; curr->data = img; curr->filename = NULL; + curr->I = NULL; ++curr; ++*num_chunks; } @@ -292,6 +300,7 @@ unsigned char* ReadZip(const char* filename, curr->deflate_len = temp_entries[nextentry].deflate_len; curr->deflate_data = img + pos; curr->filename = temp_entries[nextentry].filename; + curr->I = NULL; curr->len = temp_entries[nextentry].uncomp_len; curr->data = malloc(curr->len); @@ -336,6 +345,7 @@ unsigned char* ReadZip(const char* filename, } curr->data = img + pos; curr->filename = NULL; + curr->I = NULL; pos += curr->len; ++*num_chunks; @@ -400,6 +410,7 @@ unsigned char* ReadImage(const char* filename, curr->type = CHUNK_NORMAL; curr->len = GZIP_HEADER_LEN; curr->data = p; + curr->I = NULL; pos += curr->len; p += curr->len; @@ -407,6 +418,7 @@ unsigned char* ReadImage(const char* filename, curr->type = CHUNK_DEFLATE; curr->filename = NULL; + curr->I = NULL; // We must decompress this chunk in order to discover where it // ends, and so we can put the uncompressed data and its length @@ -452,6 +464,7 @@ unsigned char* ReadImage(const char* filename, curr->start = pos; curr->len = GZIP_FOOTER_LEN; curr->data = img+pos; + curr->I = NULL; pos += curr->len; p += curr->len; @@ -475,6 +488,7 @@ unsigned char* ReadImage(const char* filename, *chunks = realloc(*chunks, *num_chunks * sizeof(ImageChunk)); ImageChunk* curr = *chunks + (*num_chunks-1); curr->start = pos; + curr->I = NULL; // 'pos' is not the offset of the start of a gzip chunk, so scan // forward until we find a gzip header. @@ -591,43 +605,12 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) { } } - char stemp[] = "/tmp/imgdiff-src-XXXXXX"; - char ttemp[] = "/tmp/imgdiff-tgt-XXXXXX"; char ptemp[] = "/tmp/imgdiff-patch-XXXXXX"; - mkstemp(stemp); - mkstemp(ttemp); mkstemp(ptemp); - FILE* f = fopen(stemp, "wb"); - if (f == NULL) { - fprintf(stderr, "failed to open src chunk %s: %s\n", - stemp, strerror(errno)); - return NULL; - } - if (fwrite(src->data, 1, src->len, f) != src->len) { - fprintf(stderr, "failed to write src chunk to %s: %s\n", - stemp, strerror(errno)); - return NULL; - } - fclose(f); - - f = fopen(ttemp, "wb"); - if (f == NULL) { - fprintf(stderr, "failed to open tgt chunk %s: %s\n", - ttemp, strerror(errno)); - return NULL; - } - if (fwrite(tgt->data, 1, tgt->len, f) != tgt->len) { - fprintf(stderr, "failed to write tgt chunk to %s: %s\n", - ttemp, strerror(errno)); - return NULL; - } - fclose(f); - - char cmd[200]; - sprintf(cmd, "bsdiff %s %s %s", stemp, ttemp, ptemp); - if (system(cmd) != 0) { - fprintf(stderr, "failed to run bsdiff: %s\n", strerror(errno)); + int r = bsdiff(src->data, src->len, &(src->I), tgt->data, tgt->len, ptemp); + if (r != 0) { + fprintf(stderr, "bsdiff() failed: %d\n", r); return NULL; } @@ -641,8 +624,6 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) { unsigned char* data = malloc(st.st_size); if (tgt->type == CHUNK_NORMAL && tgt->len <= st.st_size) { - unlink(stemp); - unlink(ttemp); unlink(ptemp); tgt->type = CHUNK_RAW; @@ -652,7 +633,7 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) { *size = st.st_size; - f = fopen(ptemp, "rb"); + FILE* f = fopen(ptemp, "rb"); if (f == NULL) { fprintf(stderr, "failed to open patch %s: %s\n", ptemp, strerror(errno)); return NULL; @@ -663,8 +644,6 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) { } fclose(f); - unlink(stemp); - unlink(ttemp); unlink(ptemp); tgt->source_start = src->start; @@ -784,6 +763,14 @@ ImageChunk* FindChunkByName(const char* name, return NULL; } +void DumpChunks(ImageChunk* chunks, int num_chunks) { + int i; + for (i = 0; i < num_chunks; ++i) { + printf("chunk %d: type %d start %d len %d\n", + i, chunks[i].type, chunks[i].start, chunks[i].len); + } +} + int main(int argc, char** argv) { if (argc != 4 && argc != 5) { usage: @@ -829,14 +816,29 @@ int main(int argc, char** argv) { // Verify that the source and target images have the same chunk // structure (ie, the same sequence of deflate and normal chunks). + if (!zip_mode) { + // Merge the gzip header and footer in with any adjacent + // normal chunks. + MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks); + MergeAdjacentNormalChunks(src_chunks, &num_src_chunks); + } + if (num_src_chunks != num_tgt_chunks) { fprintf(stderr, "source and target don't have same number of chunks!\n"); + printf("source chunks:\n"); + DumpChunks(src_chunks, num_src_chunks); + printf("target chunks:\n"); + DumpChunks(tgt_chunks, num_tgt_chunks); return 1; } for (i = 0; i < num_src_chunks; ++i) { if (src_chunks[i].type != tgt_chunks[i].type) { fprintf(stderr, "source and target don't have same chunk " "structure! (chunk %d)\n", i); + printf("source chunks:\n"); + DumpChunks(src_chunks, num_src_chunks); + printf("target chunks:\n"); + DumpChunks(tgt_chunks, num_tgt_chunks); return 1; } } diff --git a/tools/apriori/prelinkmap.c b/tools/apriori/prelinkmap.c index 739c181..9fb00e4 100644 --- a/tools/apriori/prelinkmap.c +++ b/tools/apriori/prelinkmap.c @@ -7,11 +7,14 @@ typedef struct mapentry mapentry; +#define MAX_ALIASES 10 + struct mapentry { mapentry *next; unsigned base; - char name[0]; + char *names[MAX_ALIASES]; + int num_names; }; static mapentry *maplist = 0; @@ -22,14 +25,13 @@ static mapentry *maplist = 0; */ #define PRELINK_MIN 0x90000000 -#define PRELINK_MAX 0xB0000000 +#define PRELINK_MAX 0xBFFFFFFF void pm_init(const char *file) { unsigned line = 0; char buf[256]; char *x; - unsigned n; FILE *fp; mapentry *me; unsigned last = -1UL; @@ -65,26 +67,52 @@ void pm_init(const char *file) continue; } - n = strtoul(x, 0, 16); - /* Note that this is not the only bounds check. If a library's size - exceeds its slot as defined in the prelink map, the prelinker will - exit with an error. See pm_report_library_size_in_memory(). - */ - FAILIF((n < PRELINK_MIN) || (n > PRELINK_MAX), - "%s:%d base 0x%08x out of range.\n", - file, line, n); - - me = malloc(sizeof(mapentry) + strlen(buf) + 1); - FAILIF(me == NULL, "Out of memory parsing %s\n", file); + if (isalpha(*x)) { + /* Assume that this is an alias, and look through the list of + already-installed libraries. + */ + me = maplist; + while(me) { + /* The strlen() call ignores the newline at the end of x */ + if (!strncmp(me->names[0], x, strlen(me->names[0]))) { + PRINT("Aliasing library %s to %s at %08x\n", + buf, x, me->base); + break; + } + me = me->next; + } + FAILIF(!me, "Nonexistent alias %s -> %s\n", buf, x); + } + else { + unsigned n = strtoul(x, 0, 16); + /* Note that this is not the only bounds check. If a library's + size exceeds its slot as defined in the prelink map, the + prelinker will exit with an error. See + pm_report_library_size_in_memory(). + */ + FAILIF((n < PRELINK_MIN) || (n > PRELINK_MAX), + "%s:%d base 0x%08x out of range.\n", + file, line, n); + + me = malloc(sizeof(mapentry)); + FAILIF(me == NULL, "Out of memory parsing %s\n", file); + + FAILIF(last <= n, "The prelink map is not in descending order " + "at entry %s (%08x)!\n", buf, n); + last = n; + + me->base = n; + me->next = maplist; + me->num_names = 0; + maplist = me; + } - FAILIF(last <= n, "The prelink map is not in descending order " - "at entry %s (%08x)!\n", buf, n); - last = n; - - me->base = n; - strcpy(me->name, buf); - me->next = maplist; - maplist = me; + FAILIF(me->num_names >= MAX_ALIASES, + "Too many aliases for library %s, maximum is %d.\n", + me->names[0], + MAX_ALIASES); + me->names[me->num_names] = strdup(buf); + me->num_names++; } fclose(fp); @@ -99,41 +127,44 @@ void pm_report_library_size_in_memory(const char *name, { char *x; mapentry *me; + int n; x = strrchr(name,'/'); if(x) name = x+1; for(me = maplist; me; me = me->next){ - if(!strcmp(name, me->name)) { - off_t slot = me->next ? me->next->base : PRELINK_MAX; - slot -= me->base; - FAILIF(fsize > slot, - "prelink map error: library %s@0x%08x is too big " - "at %lld bytes, it runs %lld bytes into " - "library %s@0x%08x!\n", - me->name, me->base, fsize, fsize - slot, - me->next->name, me->next->base); - break; + for (n = 0; n < me->num_names; n++) { + if(!strcmp(name, me->names[n])) { + off_t slot = me->next ? me->next->base : PRELINK_MAX; + slot -= me->base; + FAILIF(fsize > slot, + "prelink map error: library %s@0x%08x is too big " + "at %lld bytes, it runs %lld bytes into " + "library %s@0x%08x!\n", + me->names[0], me->base, fsize, fsize - slot, + me->next->names[0], me->next->base); + return; + } } } - FAILIF(!me,"library '%s' not in prelink map\n", name); + FAILIF(1, "library '%s' not in prelink map\n", name); } unsigned pm_get_next_link_address(const char *lookup_name) { char *x; mapentry *me; + int n; x = strrchr(lookup_name,'/'); if(x) lookup_name = x+1; - for(me = maplist; me; me = me->next){ - if(!strcmp(lookup_name, me->name)) { - return me->base; - } - } - - FAILIF(1==1,"library '%s' not in prelink map\n", lookup_name); + for(me = maplist; me; me = me->next) + for (n = 0; n < me->num_names; n++) + if(!strcmp(lookup_name, me->names[n])) + return me->base; + + FAILIF(1, "library '%s' not in prelink map\n", lookup_name); return 0; } diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh index 5c738a2..af5aa47 100755 --- a/tools/buildinfo.sh +++ b/tools/buildinfo.sh @@ -24,6 +24,7 @@ echo "ro.product.cpu.abi=$TARGET_CPU_ABI" echo "ro.product.manufacturer=$PRODUCT_MANUFACTURER" echo "ro.product.locale.language=$PRODUCT_DEFAULT_LANGUAGE" echo "ro.product.locale.region=$PRODUCT_DEFAULT_REGION" +echo "ro.wifi.channels=$PRODUCT_DEFAULT_WIFI_CHANNELS" echo "ro.board.platform=$TARGET_BOARD_PLATFORM" echo "# ro.build.product is obsolete; use ro.product.device" diff --git a/tools/dexpreopt/Config.mk b/tools/dexpreopt/Config.mk index c6639b2..443b8c9 100644 --- a/tools/dexpreopt/Config.mk +++ b/tools/dexpreopt/Config.mk @@ -77,6 +77,13 @@ ifdef sign_dexpreopt $(shell echo "$(p) $(PACKAGES.$(p).CERTIFICATE) $(PACKAGES.$(p).PRIVATE_KEY)" >> $(dexpreopt_package_certs_file))) endif +# The kernel used for ARMv7 system images is different +ifeq ($(TARGET_ARCH_VARIANT),armv7-a) +BUILD_DEXPREOPT_KERNEL := prebuilt/android-arm/kernel/kernel-qemu-armv7 +else +BUILD_DEXPREOPT_KERNEL := prebuilt/android-arm/kernel/kernel-qemu +endif + # Build an optimized image from the unoptimized image BUILT_DEXPREOPT_SYSTEMIMAGE := $(intermediates)/system.img $(BUILT_DEXPREOPT_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE_UNOPT) @@ -99,7 +106,7 @@ $(BUILT_DEXPREOPT_SYSTEMIMAGE): $(hide) \ PATH=$(HOST_OUT_EXECUTABLES):$$PATH \ $(DEXPREOPT) \ - --kernel prebuilt/android-arm/kernel/kernel-qemu \ + --kernel $(BUILD_DEXPREOPT_KERNEL) \ --ramdisk $(BUILT_DEXPREOPT_RAMDISK) \ --image $(BUILT_SYSTEMIMAGE_UNOPT) \ --system $(PRODUCT_OUT) \ diff --git a/tools/droiddoc/src/AnnotationInstanceInfo.java b/tools/droiddoc/src/AnnotationInstanceInfo.java index 07d4aa3..c4abc7e 100644 --- a/tools/droiddoc/src/AnnotationInstanceInfo.java +++ b/tools/droiddoc/src/AnnotationInstanceInfo.java @@ -35,6 +35,7 @@ class AnnotationInstanceInfo return mElementValues; } + @Override public String toString() { StringBuilder str = new StringBuilder(); diff --git a/tools/droiddoc/src/AttrTagInfo.java b/tools/droiddoc/src/AttrTagInfo.java index abc5452..7f1b4d9 100644 --- a/tools/droiddoc/src/AttrTagInfo.java +++ b/tools/droiddoc/src/AttrTagInfo.java @@ -98,7 +98,8 @@ public class AttrTagInfo extends TagInfo public FieldInfo reference() { return REF_COMMAND.equals(mCommand) ? mRefField : null; } - + + @Override public String name() { return NAME_COMMAND.equals(mCommand) ? mAttrName : null; } @@ -107,6 +108,7 @@ public class AttrTagInfo extends TagInfo return DESCRIPTION_COMMAND.equals(mCommand) ? mDescrComment : null; } + @Override public void makeHDF(HDF data, String base) { super.makeHDF(data, base); diff --git a/tools/droiddoc/src/ClassInfo.java b/tools/droiddoc/src/ClassInfo.java index 5c61941..f3f11de 100644 --- a/tools/droiddoc/src/ClassInfo.java +++ b/tools/droiddoc/src/ClassInfo.java @@ -101,7 +101,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco mSelfFields = null; mSelfAttributes = null; mDeprecatedKnown = false; - + Arrays.sort(mEnumConstants, FieldInfo.comparator); Arrays.sort(mInnerClasses, ClassInfo.comparator); } @@ -111,16 +111,16 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco // objects selfAttributes(); } - + public void init3(TypeInfo[] types, ClassInfo[] realInnerClasses){ mTypeParameters = types; mRealInnerClasses = realInnerClasses; } - + public ClassInfo[] getRealInnerClasses(){ return mRealInnerClasses; } - + public TypeInfo[] getTypeParameters(){ return mTypeParameters; } @@ -146,6 +146,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco } } + @Override public ContainerInfo parent() { return this; @@ -351,7 +352,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco { return comment().briefTags(); } - + public boolean isDeprecated() { boolean deprecated = false; if (!mDeprecatedKnown) { @@ -551,7 +552,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco public MethodInfo[] allSelfMethods() { return mAllSelfMethods; } - + public void addMethod(MethodInfo method) { MethodInfo[] methods = new MethodInfo[mAllSelfMethods.length + 1]; int i = 0; @@ -596,7 +597,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco } } } - + //constructors too for (MethodInfo m: constructors()) { for (AttrTagInfo tag: m.comment().attrTags()) { @@ -1136,7 +1137,11 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco if (kind != null) { data.setValue(base + ".kind", kind); } - + + if (cl.mIsIncluded) { + data.setValue(base + ".included", "true"); + } + // xml attributes i=0; for (AttributeInfo attr: cl.selfAttributes()) { @@ -1170,6 +1175,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco } } + @Override public boolean isHidden() { int val = mHidden; @@ -1301,7 +1307,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco return f; } } - + // then look at our enum constants (these are really fields, maybe // they should be mixed into fields(). not sure) for (FieldInfo f: enumConstants()) { @@ -1346,11 +1352,11 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco return false; } } - + public void setNonWrittenConstructors(MethodInfo[] nonWritten) { mNonWrittenConstructors = nonWritten; } - + public MethodInfo[] getNonWrittenConstructors() { return mNonWrittenConstructors; } @@ -1377,23 +1383,24 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco } return null; } - + public void setHiddenMethods(MethodInfo[] mInfo){ mHiddenMethods = mInfo; } public MethodInfo[] getHiddenMethods(){ return mHiddenMethods; } + @Override public String toString(){ return this.qualifiedName(); } - + public void setReasonIncluded(String reason) { mReasonIncluded = reason; } - + public String getReasonIncluded() { - return mReasonIncluded; + return mReasonIncluded; } private ClassDoc mClass; diff --git a/tools/droiddoc/src/Comment.java b/tools/droiddoc/src/Comment.java index 3f1bf6c..553cdf2 100644 --- a/tools/droiddoc/src/Comment.java +++ b/tools/droiddoc/src/Comment.java @@ -157,7 +157,7 @@ public class Comment else if (name.equals("@literal")) { mInlineTagsList.add(new LiteralTagInfo(name, name, text, pos)); } - else if (name.equals("@hide") || name.equals("@doconly")) { + else if (name.equals("@hide") || name.equals("@pending") || name.equals("@doconly")) { // nothing } else if (name.equals("@attr")) { @@ -206,7 +206,7 @@ public class Comment for (int i=0; i<N; i++) { if (mInlineTagsList.get(i).name().equals("@more")) { more = i; - } + } } if (more >= 0) { for (int i=0; i<more; i++) { @@ -225,7 +225,7 @@ public class Comment } } mBriefTagsList.add(t); - + } } } @@ -307,12 +307,12 @@ public class Comment mHidden = 0; return false; } - boolean b = mText.indexOf("@hide") >= 0; + boolean b = mText.indexOf("@hide") >= 0 || mText.indexOf("@pending") >= 0; mHidden = b ? 1 : 0; return b; } } - + public boolean isDocOnly() { if (mDocOnly >= 0) { return mDocOnly != 0; @@ -391,5 +391,5 @@ public class Comment ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>(); ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>(); - + } diff --git a/tools/droiddoc/src/Converter.java b/tools/droiddoc/src/Converter.java index 4014f7f..ee911f4 100644 --- a/tools/droiddoc/src/Converter.java +++ b/tools/droiddoc/src/Converter.java @@ -238,6 +238,7 @@ public class Converter } private static Cache mClasses = new Cache() { + @Override protected Object make(Object o) { ClassDoc c = (ClassDoc)o; @@ -268,19 +269,21 @@ public class Converter } return cl; } + @Override protected void made(Object o, Object r) { if (mClassesNeedingInit == null) { initClass((ClassDoc)o, (ClassInfo)r); ((ClassInfo)r).init2(); } - } + } + @Override ClassInfo[] all() { return (ClassInfo[])mCache.values().toArray(new ClassInfo[mCache.size()]); } }; - + private static MethodInfo[] getHiddenMethods(MethodDoc[] methods){ if (methods == null) return null; ArrayList<MethodInfo> out = new ArrayList<MethodInfo>(); @@ -342,7 +345,7 @@ public class Converter } return out.toArray(new MethodInfo[out.size()]); } - + private static MethodInfo[] convertNonWrittenConstructors(ConstructorDoc[] methods) { if (methods == null) return null; @@ -367,6 +370,7 @@ public class Converter } private static Cache mMethods = new Cache() { + @Override protected Object make(Object o) { if (o instanceof AnnotationTypeElementDoc) { @@ -374,7 +378,7 @@ public class Converter MethodInfo result = new MethodInfo( m.getRawCommentText(), Converter.convertTypes(m.typeParameters()), - m.name(), m.signature(), + m.name(), m.signature(), Converter.obtainClass(m.containingClass()), Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), @@ -399,7 +403,7 @@ public class Converter MethodInfo result = new MethodInfo( m.getRawCommentText(), Converter.convertTypes(m.typeParameters()), - m.name(), m.signature(), + m.name(), m.signature(), Converter.obtainClass(m.containingClass()), Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), @@ -424,7 +428,7 @@ public class Converter MethodInfo result = new MethodInfo( m.getRawCommentText(), Converter.convertTypes(m.typeParameters()), - m.name(), m.signature(), + m.name(), m.signature(), Converter.obtainClass(m.containingClass()), Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), @@ -472,6 +476,7 @@ public class Converter } private static Cache mFields = new Cache() { + @Override protected Object make(Object o) { FieldDoc f = (FieldDoc)o; @@ -496,6 +501,7 @@ public class Converter } private static Cache mPackagees = new Cache() { + @Override protected Object make(Object o) { PackageDoc p = (PackageDoc)o; @@ -510,7 +516,8 @@ public class Converter } private static Cache mTypes = new Cache() { - protected Object make(Object o) + @Override + protected Object make(Object o) { Type t = (Type)o; String simpleTypeName; @@ -524,6 +531,7 @@ public class Converter Converter.obtainClass(t.asClassDoc())); return ti; } + @Override protected void made(Object o, Object r) { Type t = (Type)o; @@ -545,8 +553,9 @@ public class Converter Converter.convertTypes(t.asWildcardType().extendsBounds())); } } + @Override protected Object keyFor(Object o) - { + { Type t = (Type)o; String keyString = o.getClass().getName() + "/" + o.toString() + "/"; if (t.asParameterizedType() != null){ @@ -584,13 +593,13 @@ public class Converter }else{ keyString += "NoWildCardType//"; } - - - + + + return keyString; } }; - + private static MemberInfo obtainMember(MemberDoc o) @@ -599,6 +608,7 @@ public class Converter } private static Cache mMembers = new Cache() { + @Override protected Object make(Object o) { if (o instanceof MethodDoc) { @@ -633,6 +643,7 @@ public class Converter } private static Cache mAnnotationInstances = new Cache() { + @Override protected Object make(Object o) { AnnotationDesc a = (AnnotationDesc)o; diff --git a/tools/droiddoc/src/DroidDoc.java b/tools/droiddoc/src/DroidDoc.java index 4ff26bc..f48b56c 100644 --- a/tools/droiddoc/src/DroidDoc.java +++ b/tools/droiddoc/src/DroidDoc.java @@ -40,7 +40,7 @@ public class DroidDoc private static final int TYPE_WIDGET = 1; private static final int TYPE_LAYOUT = 2; private static final int TYPE_LAYOUT_PARAM = 3; - + public static final int SHOW_PUBLIC = 0x00000001; public static final int SHOW_PROTECTED = 0x00000003; public static final int SHOW_PACKAGE = 0x00000007; @@ -84,7 +84,7 @@ public class DroidDoc } return false; } - + public static boolean start(RootDoc r) { String keepListFile = null; @@ -95,6 +95,7 @@ public class DroidDoc String stubsDir = null; //Create the dependency graph for the stubs directory boolean apiXML = false; + boolean noDocs = false; String apiFile = null; String debugStubsFile = ""; HashSet<String> stubPackages = null; @@ -187,6 +188,9 @@ public class DroidDoc apiXML = true; apiFile = a[1]; } + else if (a[0].equals("-nodocs")) { + noDocs = true; + } else if (a[0].equals("-since")) { sinceTagger.addVersion(a[1], a[2]); } @@ -200,62 +204,70 @@ public class DroidDoc // Set up the data structures Converter.makeInfo(r); - // Files for proofreading - if (proofreadFile != null) { - Proofread.initProofread(proofreadFile); - } - if (todoFile != null) { - TodoFile.writeTodoFile(todoFile); - } + if (!noDocs) { + long startTime = System.nanoTime(); - // Apply @since tags from the XML file - sinceTagger.tagAll(Converter.rootClasses()); + // Apply @since tags from the XML file + sinceTagger.tagAll(Converter.rootClasses()); - // HTML Pages - if (ClearPage.htmlDir != null) { - writeHTMLPages(); - } + // Files for proofreading + if (proofreadFile != null) { + Proofread.initProofread(proofreadFile); + } + if (todoFile != null) { + TodoFile.writeTodoFile(todoFile); + } - // Navigation tree - NavTree.writeNavTree(javadocDir); + // HTML Pages + if (ClearPage.htmlDir != null) { + writeHTMLPages(); + } - // Packages Pages - writePackages(javadocDir - + (ClearPage.htmlDir!=null - ? "packages" + htmlExtension - : "index" + htmlExtension)); + // Navigation tree + NavTree.writeNavTree(javadocDir); - // Classes - writeClassLists(); - writeClasses(); - writeHierarchy(); - // writeKeywords(); + // Packages Pages + writePackages(javadocDir + + (ClearPage.htmlDir!=null + ? "packages" + htmlExtension + : "index" + htmlExtension)); - // Lists for JavaScript - writeLists(); - if (keepListFile != null) { - writeKeepList(keepListFile); - } + // Classes + writeClassLists(); + writeClasses(); + writeHierarchy(); + // writeKeywords(); - // Sample Code - for (SampleCode sc: sampleCodes) { - sc.write(); - } + // Lists for JavaScript + writeLists(); + if (keepListFile != null) { + writeKeepList(keepListFile); + } - // Index page - writeIndex(); + // Sample Code + for (SampleCode sc: sampleCodes) { + sc.write(); + } - Proofread.finishProofread(proofreadFile); + // Index page + writeIndex(); + + Proofread.finishProofread(proofreadFile); + + if (sdkValuePath != null) { + writeSdkValues(sdkValuePath); + } + + long time = System.nanoTime() - startTime; + System.out.println("DroidDoc took " + (time / 1000000000) + " sec. to write docs to " + + ClearPage.outputDir); + } // Stubs if (stubsDir != null) { Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages); } - if (sdkValuePath != null) { - writeSdkValues(sdkValuePath); - } - Errors.printErrors(); return !Errors.hadError; } @@ -401,6 +413,9 @@ public class DroidDoc if (option.equals("-apixml")) { return 2; } + if (option.equals("-nodocs")) { + return 1; + } if (option.equals("-since")) { return 3; } @@ -777,7 +792,7 @@ public class DroidDoc data.setValue("package.since", pkg.getSince()); data.setValue("package.descr", "...description..."); - makeClassListHDF(data, "package.interfaces", + makeClassListHDF(data, "package.interfaces", ClassInfo.sortByName(pkg.interfaces())); makeClassListHDF(data, "package.classes", ClassInfo.sortByName(pkg.ordinaryClasses())); @@ -871,7 +886,7 @@ public class DroidDoc HDF data = makeHDF(); Collections.sort(keywords); - + int i=0; for (KeywordEntry entry: keywords) { String base = "keywords." + entry.firstChar() + "." + i; @@ -964,10 +979,11 @@ public class DroidDoc } /** - * Returns true if the given element has an @hide annotation. + * Returns true if the given element has an @hide or @pending annotation. */ private static boolean hasHideAnnotation(Doc doc) { - return doc.getRawCommentText().indexOf("@hide") != -1; + String comment = doc.getRawCommentText(); + return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -1; } /** @@ -1059,7 +1075,7 @@ public class DroidDoc if (methodName.equals("getRawCommentText")) { return filterComment((String) method.invoke(target, args)); } - + // escape "&" in disjunctive types. if (proxy instanceof Type && methodName.equals("toString")) { return ((String) method.invoke(target, args)) @@ -1114,7 +1130,7 @@ public class DroidDoc throw new RuntimeException("invalid scope for object " + scoped); } } - + /** * Collect the values used by the Dev tools and write them in files packaged with the SDK * @param output the ouput directory for the files. @@ -1124,16 +1140,16 @@ public class DroidDoc ArrayList<String> broadcastActions = new ArrayList<String>(); ArrayList<String> serviceActions = new ArrayList<String>(); ArrayList<String> categories = new ArrayList<String>(); - + ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>(); ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>(); ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>(); - + ClassInfo[] classes = Converter.allClasses(); // Go through all the fields of all the classes, looking SDK stuff. for (ClassInfo clazz : classes) { - + // first check constant fields for the SdkConstant annotation. FieldInfo[] fields = clazz.allSelfFields(); for (FieldInfo field : fields) { @@ -1162,7 +1178,7 @@ public class DroidDoc } } } - + // Now check the class for @Widget or if its in the android.widget package // (unless the class is hidden or abstract, or non public) if (clazz.isHidden() == false && clazz.isPublic() && clazz.isAbstract() == false) { @@ -1181,7 +1197,7 @@ public class DroidDoc } } } - + if (annotated == false) { // lets check if this is inside android.widget PackageInfo pckg = clazz.containingPackage(); @@ -1221,7 +1237,7 @@ public class DroidDoc Collections.sort(categories); writeValues(output + "/categories.txt", categories); - + // before writing the list of classes, we do some checks, to make sure the layout params // are enclosed by a layout class (and not one that has been declared as a widget) for (int i = 0 ; i < layoutParams.size();) { @@ -1233,10 +1249,10 @@ public class DroidDoc i++; } } - + writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams); } - + /** * Writes a list of values into a text files. * @param pathname the absolute os path of the output file. @@ -1248,7 +1264,7 @@ public class DroidDoc try { fw = new FileWriter(pathname, false); bw = new BufferedWriter(fw); - + for (String value : values) { bw.append(value).append('\n'); } @@ -1282,7 +1298,7 @@ public class DroidDoc try { fw = new FileWriter(pathname, false); bw = new BufferedWriter(fw); - + // write the 3 types of classes. for (ClassInfo clazz : widgets) { writeClass(bw, clazz, 'W'); @@ -1325,7 +1341,7 @@ public class DroidDoc } writer.append('\n'); } - + /** * Checks the inheritance of {@link ClassInfo} objects. This method return * <ul> @@ -1333,7 +1349,7 @@ public class DroidDoc * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li> * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends <code>android.view.ViewGroup$LayoutParams</code></li> * <li>{@link #TYPE_NONE}: in all other cases</li> - * </ul> + * </ul> * @param clazz the {@link ClassInfo} to check. */ private static int checkInheritance(ClassInfo clazz) { @@ -1344,12 +1360,12 @@ public class DroidDoc } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) { return TYPE_LAYOUT_PARAM; } - + ClassInfo parent = clazz.superclass(); if (parent != null) { return checkInheritance(parent); } - + return TYPE_NONE; } } diff --git a/tools/droiddoc/src/Errors.java b/tools/droiddoc/src/Errors.java index 95439f1..77852f8 100644 --- a/tools/droiddoc/src/Errors.java +++ b/tools/droiddoc/src/Errors.java @@ -41,6 +41,7 @@ public class Errors return this.msg.compareTo(that.msg); } + @Override public String toString() { String whereText = this.pos == null ? "unknown: " : this.pos.toString() + ':'; return whereText + this.msg; diff --git a/tools/droiddoc/src/FieldInfo.java b/tools/droiddoc/src/FieldInfo.java index 1c975e4..d9371e8 100644 --- a/tools/droiddoc/src/FieldInfo.java +++ b/tools/droiddoc/src/FieldInfo.java @@ -26,7 +26,7 @@ public class FieldInfo extends MemberInfo return a.name().compareTo(b.name()); } }; - + public FieldInfo(String name, ClassInfo containingClass, ClassInfo realContainingClass, boolean isPublic, boolean isProtected, boolean isPackagePrivate, boolean isPrivate, @@ -92,7 +92,7 @@ public class FieldInfo extends MemberInfo { return constantLiteralValue(mConstantValue); } - + public boolean isDeprecated() { boolean deprecated = false; if (!mDeprecatedKnown) { @@ -124,7 +124,7 @@ public class FieldInfo extends MemberInfo if (val instanceof Boolean || val instanceof Byte || val instanceof Short - || val instanceof Integer) + || val instanceof Integer) { str = val.toString(); } @@ -291,6 +291,7 @@ public class FieldInfo extends MemberInfo } } + @Override public boolean isExecutable() { return false; diff --git a/tools/droiddoc/src/MemberInfo.java b/tools/droiddoc/src/MemberInfo.java index 2a2572a..05da583 100644 --- a/tools/droiddoc/src/MemberInfo.java +++ b/tools/droiddoc/src/MemberInfo.java @@ -115,6 +115,7 @@ public abstract class MemberInfo extends DocInfo implements Comparable, Scoped return mIsSynthetic; } + @Override public ContainerInfo parent() { return mContainingClass; @@ -130,7 +131,7 @@ public abstract class MemberInfo extends DocInfo implements Comparable, Scoped { return mKind; } - + public AnnotationInstanceInfo[] annotations() { return mAnnotations; diff --git a/tools/droiddoc/src/MethodInfo.java b/tools/droiddoc/src/MethodInfo.java index bded88b..3211038 100644 --- a/tools/droiddoc/src/MethodInfo.java +++ b/tools/droiddoc/src/MethodInfo.java @@ -25,9 +25,9 @@ public class MethodInfo extends MemberInfo return a.name().compareTo(b.name()); } }; - + private class InlineTags implements InheritedTags - { + { public TagInfo[] tags() { return comment().tags(); @@ -42,7 +42,7 @@ public class MethodInfo extends MemberInfo } } } - + private static void addInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue) { for (ClassInfo i: ifaces) { @@ -79,7 +79,7 @@ public class MethodInfo extends MemberInfo } return null; } - + private static void addRealInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue) { for (ClassInfo i: ifaces) { @@ -92,7 +92,7 @@ public class MethodInfo extends MemberInfo addInterfaces(i.realInterfaces(), queue); } } - + public MethodInfo findRealOverriddenMethod(String name, String signature, HashSet notStrippable) { if (mReturnType == null) { // ctor @@ -103,7 +103,7 @@ public class MethodInfo extends MemberInfo } ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>(); - if (containingClass().realSuperclass() != null && + if (containingClass().realSuperclass() != null && containingClass().realSuperclass().isAbstract()) { queue.add(containingClass()); } @@ -121,7 +121,7 @@ public class MethodInfo extends MemberInfo } return null; } - + public MethodInfo findSuperclassImplementation(HashSet notStrippable) { if (mReturnType == null) { // ctor @@ -138,7 +138,7 @@ public class MethodInfo extends MemberInfo } ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>(); - if (containingClass().realSuperclass() != null && + if (containingClass().realSuperclass() != null && containingClass().realSuperclass().isAbstract()) { queue.add(containingClass()); } @@ -154,7 +154,7 @@ public class MethodInfo extends MemberInfo } return null; } - + public ClassInfo findRealOverriddenClass(String name, String signature) { if (mReturnType == null) { // ctor @@ -165,7 +165,7 @@ public class MethodInfo extends MemberInfo } ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>(); - if (containingClass().realSuperclass() != null && + if (containingClass().realSuperclass() != null && containingClass().realSuperclass().isAbstract()) { queue.add(containingClass()); } @@ -199,7 +199,7 @@ public class MethodInfo extends MemberInfo } } } - + private class ReturnTags implements InheritedTags { public TagInfo[] tags() { return comment().returnTags(); @@ -213,7 +213,7 @@ public class MethodInfo extends MemberInfo } } } - + public boolean isDeprecated() { boolean deprecated = false; if (!mDeprecatedKnown) { @@ -237,7 +237,7 @@ public class MethodInfo extends MemberInfo } return mIsDeprecated; } - + public TypeInfo[] getTypeParameters(){ return mTypeParameters; } @@ -274,7 +274,7 @@ public class MethodInfo extends MemberInfo // The underlying MethodDoc for an interface's declared methods winds up being marked // non-abstract. Correct that here by looking at the immediate-parent class, and marking - // this method abstract if it is an unimplemented interface method. + // this method abstract if it is an unimplemented interface method. if (containingClass.isInterface()) { isAbstract = true; } @@ -448,7 +448,7 @@ public class MethodInfo extends MemberInfo + tag.parameterName() + "'"); } } - + // get our parent's tags to fill in the blanks MethodInfo overridden = this.findOverriddenMethod(name(), signature()); if (overridden != null) { @@ -508,7 +508,7 @@ public class MethodInfo extends MemberInfo { return mParameters; } - + public boolean matchesParams(String[] params, String[] dimensions) { @@ -589,6 +589,7 @@ public class MethodInfo extends MemberInfo return result; } + @Override public boolean isExecutable() { return true; @@ -617,21 +618,23 @@ public class MethodInfo extends MemberInfo { return mDefaultAnnotationElementValue; } - + public void setVarargs(boolean set){ mIsVarargs = set; } public boolean isVarArgs(){ return mIsVarargs; } + + @Override public String toString(){ return this.name(); } - + public void setReason(String reason) { mReasonOpened = reason; } - + public String getReason() { return mReasonOpened; } diff --git a/tools/droiddoc/src/PackageInfo.java b/tools/droiddoc/src/PackageInfo.java index 18c636e..17ad1b7 100644 --- a/tools/droiddoc/src/PackageInfo.java +++ b/tools/droiddoc/src/PackageInfo.java @@ -57,11 +57,13 @@ public class PackageInfo extends DocInfo implements ContainerInfo return s; } + @Override public ContainerInfo parent() { return null; } + @Override public boolean isHidden() { return comment().isHidden(); diff --git a/tools/droiddoc/src/ParamTagInfo.java b/tools/droiddoc/src/ParamTagInfo.java index c21ecd5..d6f2b6b 100644 --- a/tools/droiddoc/src/ParamTagInfo.java +++ b/tools/droiddoc/src/ParamTagInfo.java @@ -76,6 +76,7 @@ public class ParamTagInfo extends ParsedTagInfo return mParameterName; } + @Override public void makeHDF(HDF data, String base) { data.setValue(base + ".name", parameterName()); diff --git a/tools/droiddoc/src/SampleTagInfo.java b/tools/droiddoc/src/SampleTagInfo.java index c80083b..c7ad1cc 100644 --- a/tools/droiddoc/src/SampleTagInfo.java +++ b/tools/droiddoc/src/SampleTagInfo.java @@ -36,7 +36,7 @@ import java.util.regex.Matcher; * Both tags accept either a filename and an id or just a filename. If no id * is provided, the entire file is copied. If an id is provided, the lines * in the given file between the first two lines containing BEGIN_INCLUDE(id) - * and END_INCLUDE(id), for the given id, are copied. The id may be only + * and END_INCLUDE(id), for the given id, are copied. The id may be only * letters, numbers and underscore (_). * * Four examples: @@ -274,6 +274,7 @@ public class SampleTagInfo extends TagInfo return result.substring(0); } + @Override public void makeHDF(HDF data, String base) { data.setValue(base + ".name", name()); diff --git a/tools/droiddoc/src/SeeTagInfo.java b/tools/droiddoc/src/SeeTagInfo.java index 94863b5..8420ed3 100644 --- a/tools/droiddoc/src/SeeTagInfo.java +++ b/tools/droiddoc/src/SeeTagInfo.java @@ -45,6 +45,7 @@ public class SeeTagInfo extends TagInfo return linkReference().label; } + @Override public void makeHDF(HDF data, String base) { LinkReference linkRef = linkReference(); diff --git a/tools/droiddoc/src/SourcePositionInfo.java b/tools/droiddoc/src/SourcePositionInfo.java index 6244803..ac605ec 100644 --- a/tools/droiddoc/src/SourcePositionInfo.java +++ b/tools/droiddoc/src/SourcePositionInfo.java @@ -76,6 +76,7 @@ public class SourcePositionInfo implements Comparable return new SourcePositionInfo(that.file, line, 0); } + @Override public String toString() { return file + ':' + line; diff --git a/tools/droiddoc/src/TypeInfo.java b/tools/droiddoc/src/TypeInfo.java index 5196c13..45e9db9 100644 --- a/tools/droiddoc/src/TypeInfo.java +++ b/tools/droiddoc/src/TypeInfo.java @@ -249,6 +249,7 @@ public class TypeInfo } } + @Override public String toString(){ String returnString = ""; returnString += "Primitive?: " + mIsPrimitive + " TypeVariable?: " + diff --git a/tools/droiddoc/templates-sdk/customization.cs b/tools/droiddoc/templates-sdk/customization.cs index 6bdb992..6ae8446 100644 --- a/tools/droiddoc/templates-sdk/customization.cs +++ b/tools/droiddoc/templates-sdk/customization.cs @@ -73,9 +73,10 @@ def:custom_masthead() ?> call:default_search_box() ?><?cs if:reference ?> <div id="api-level-toggle"> - <label for="apiLevelControl"><a href="<?cs var:toroot ?>guide/appendix/api-levels.html">Filter by API Level</a>: </label> - <select id="apiLevelControl"> - <!-- option elements added by buildApiLevelToggle() --> + <input type="checkbox" id="apiLevelCheckbox" onclick="toggleApiLevelSelector(this)" /> + <label for="apiLevelCheckbox" class="disabled">Filter by API Level: </label> + <select id="apiLevelSelector"> + <!-- option elements added by buildApiLevelSelector() --> </select> </div> <script> @@ -85,7 +86,7 @@ def:custom_masthead() ?> if:!last(since) ?>, <?cs /if ?><?cs /each ?> ]; - buildApiLevelToggle(); + buildApiLevelSelector(); </script><?cs /if ?> </div><!-- headerRight --> diff --git a/tools/droiddoc/templates-sdk/devdoc-nav.cs b/tools/droiddoc/templates-sdk/devdoc-nav.cs deleted file mode 100644 index a69c175..0000000 --- a/tools/droiddoc/templates-sdk/devdoc-nav.cs +++ /dev/null @@ -1,66 +0,0 @@ -<ul> - <li><div><a href="<?cs var:toroot ?>index.html">Home</a></div></li> - <li><div><a href="<?cs var:toroot ?>what-is-android.html">What is Android?</a></div></li> - <li><div><a href="<?cs var:toroot ?>intro/index.html">Getting Started</a></div> - <ul> - <li><div><a href="<?cs var:toroot ?>intro/installing.html">Installing the SDK</a></div></li> - <li><div><a href="<?cs var:toroot ?>intro/upgrading.html">Upgrading the SDK</a></div></li> - <li><div><a href="<?cs var:toroot ?>intro/develop-and-debug.html">Developing/Debugging</a></div></li> - <li><div><a href="<?cs var:toroot ?>intro/hello-android.html">Hello Android</a></div></li> - <li><div><a href="<?cs var:toroot ?>intro/anatomy.html">Anatomy of an App</a></div></li> - <li><div><a href="<?cs var:toroot ?>intro/tutorial.html">Notepad Tutorial</a></div></li> - <li><div><a href="<?cs var:toroot ?>intro/tools.html">Development Tools</a></div></li> - <li><div><a href="<?cs var:toroot ?>intro/appmodel.html">Application Model</a></div></li> - <li><div><a href="<?cs var:toroot ?>intro/lifecycle.html">Application Life Cycle</a></div></li> - </ul> - </li> - <li><div><div><a href="<?cs var:toroot ?>devel/index.html">Developing Applications</a></div> - <ul> - <li><div><a href="<?cs var:toroot ?>devel/implementing-ui.html">Implementing a UI</a></div></li> - <li><div><a href="<?cs var:toroot ?>devel/building-blocks.html">Building Blocks</a></div></li> - <li><div><a href="<?cs var:toroot ?>devel/data.html">Data Storage and Retrieval</a></div></li> - <li><div><a href="<?cs var:toroot ?>devel/security.html">Security Model</a></div></li> - <li><div><a href="<?cs var:toroot ?>devel/resources-i18n.html">Resources and i18n</a></div></li> - </ul> - </li> - <li><div><a href="<?cs var:toroot ?>toolbox/index.html">Developer Toolbox</a></div> - <ul> - <li><div><a href="<?cs var:toroot ?>toolbox/philosophy.html">Design Philosophy</a></div></li> - <li><div><a href="<?cs var:toroot ?>toolbox/custom-components.html">Building Custom Components</a></div></li> - <li><div><a href="<?cs var:toroot ?>toolbox/optional-apis.html">Optional APIs</a></div></li> - </ul> - </li> - <li><div><a href="<?cs var:toroot ?>samples/index.html">Sample Code</a></div> - <ul> - <li><div><a href="<?cs var:toroot ?>samples/ApiDemos/index.html">API Demos</a></div></li> - <li><div><a href="<?cs var:toroot ?>samples/LunarLander/index.html">Lunar Lander</a></div></li> - <li><div><a href="<?cs var:toroot ?>samples/NotePad/index.html">Note Pad</a></div></li> - </ul> - </li> - <li> <a href="<?cs var:toroot ?>reference/index.html"><strong>Reference Information</strong></a> - <ul> - <li><a href="<?cs var:toroot ?>reference/packages.html">Package Index</a></li> - <li><a href="<?cs var:toroot ?>reference/classes.html">Class Index</a></li> - <li><a href="<?cs var:toroot ?>reference/hierarchy.html">Class Hierarchy</a></li> - <li><a href="<?cs var:toroot ?>reference/view-gallery.html">List of Views</a></li> - <li><a href="<?cs var:toroot ?>reference/available-intents.html">List of Intents</a></li> - <li><a href="<?cs var:toroot ?>reference/android/Manifest.permission.html">List of Permissions</a></li> - <li><a href="<?cs var:toroot ?>reference/available-resources.html">List of Resource Types</a></li> - <li><a href="<?cs var:toroot ?>reference/aidl.html">Android IDL</a></li> - <li><a href="<?cs var:toroot ?>reference/glossary.html">Glossary</a></li> - <li><a href="<?cs var:toroot ?>reference/keywords.html">Index</a></li> - </ul> - </li> - <li><div><a href="<?cs var:toroot ?>kb/index.html">FAQs</a></div> - <ul> - <li><div><a href="<?cs var:toroot ?>kb/general.html">General</a></div></li> - <li><div><a href="<?cs var:toroot ?>kb/commontasks.html">Common Tasks</a></div></li> - <li><div><a href="<?cs var:toroot ?>kb/troubleshooting.html">Troubleshooting</a></div></li> - <li><div><a href="<?cs var:toroot ?>kb/licensingandoss.html">Open Source Licensing</a></div></li> - <li><div><a href="<?cs var:toroot ?>kb/framework.html">Application Framework</a></div></li> - <li><div><a href="<?cs var:toroot ?>kb/security.html">Security</a></div></li> - </ul> - </li> - <li><div><a href="<?cs var:toroot ?>roadmap.html">Roadmap</a></div></li> - <li><div><a href="<?cs var:toroot ?>goodies/index.html">Goodies</a></div></li> -</ul>
\ No newline at end of file diff --git a/tools/droiddoc/templates-sdk/header_tabs.cs b/tools/droiddoc/templates-sdk/header_tabs.cs index 2a897cb..97d9048 100644 --- a/tools/droiddoc/templates-sdk/header_tabs.cs +++ b/tools/droiddoc/templates-sdk/header_tabs.cs @@ -5,80 +5,82 @@ elif:home ?>home<?cs elif:community ?>community<?cs elif:videos ?>videos<?cs /if ?>"> - - <li id="home-link"><a href="<?cs var:toroot ?><?cs if:android.whichdoc != "online" ?>offline.html<?cs else ?>index.html<?cs /if ?>"> + + <li id="home-link"><a href="<?cs var:toroot ?><?cs + if:android.whichdoc != "online" ?>offline.html<?cs + else ?>index.html<?cs /if ?>"> <?cs if:!sdk.redirect ?> <span class="en">Home</span> - <span class="de">Startseite</span> - <span class="es"></span> - <span class="fr"></span> - <span class="it"></span> - <span class="ja">ホーム</span> - <span class="zh-CN">主页</span> - <span class="zh-TW">首頁</span> + <span style="display:none" class="de">Startseite</span> + <span style="display:none" class="es"></span> + <span style="display:none" class="fr"></span> + <span style="display:none" class="it"></span> + <span style="display:none" class="ja">ホーム</span> + <span style="display:none" class="zh-CN">主页</span> + <span style="display:none" class="zh-TW">首頁</span> <?cs /if ?> </a></li> - <li id="sdk-link"><a href="<?cs var:toroot ?>sdk/<?cs var:sdk.current ?>/index.html"> + <li id="sdk-link"><a href="<?cs var:toroot ?>sdk/index.html"> <span class="en">SDK</span> </a></li> <li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html" onClick="return loadLast('guide')"> <?cs if:!sdk.redirect ?> <span class="en">Dev Guide</span> - <span class="de">Handbuch</span> - <span class="es">Guía</span> - <span class="fr">Guide</span> - <span class="it">Guida</span> - <span class="ja">開発ガイド</span> - <span class="zh-CN">开发人员指南</span> - <span class="zh-TW">開發指南</span> + <span style="display:none" class="de">Handbuch</span> + <span style="display:none" class="es">Guía</span> + <span style="display:none" class="fr">Guide</span> + <span style="display:none" class="it">Guida</span> + <span style="display:none" class="ja">開発ガイド</span> + <span style="display:none" class="zh-CN">开发人员指南</span> + <span style="display:none" class="zh-TW">開發指南</span> <?cs /if ?> </a></li> <li id="reference-link"><a href="<?cs var:toroot ?>reference/packages.html" onClick="return loadLast('reference')"> <?cs if:!sdk.redirect ?> <span class="en">Reference</span> - <span class="de">Referenz</span> - <span class="es">Referencia</span> - <span class="fr">Référence</span> - <span class="it">Riferimento</span> - <span class="ja">リファレンス</span> - <span class="zh-CN">参考</span> - <span class="zh-TW">參考資料</span> + <span style="display:none" class="de">Referenz</span> + <span style="display:none" class="es">Referencia</span> + <span style="display:none" class="fr">Référence</span> + <span style="display:none" class="it">Riferimento</span> + <span style="display:none" class="ja">リファレンス</span> + <span style="display:none" class="zh-CN">参考</span> + <span style="display:none" class="zh-TW">參考資料</span> <?cs /if ?> </a></li> <li><a href="http://android-developers.blogspot.com" onClick="return requestAppendHL(this.href)"> <?cs if:!sdk.redirect ?> <span class="en">Blog</span> - <span class="de"></span> - <span class="es"></span> - <span class="fr"></span> - <span class="it"></span> - <span class="ja">ブログ</span> - <span class="zh-CN">博客</span> - <span class="zh-TW">網誌</span> + <span style="display:none" class="de"></span> + <span style="display:none" class="es"></span> + <span style="display:none" class="fr"></span> + <span style="display:none" class="it"></span> + <span style="display:none" class="ja">ブログ</span> + <span style="display:none" class="zh-CN">博客</span> + <span style="display:none" class="zh-TW">網誌</span> <?cs /if ?> </a></li> <li id="videos-link"><a href="<?cs var:toroot ?>videos/index.html" onClick="return loadLast('videos')"> <?cs if:!sdk.redirect ?> <span class="en">Videos</span> - <span class="de"></span> - <span class="es"></span> - <span class="fr"></span> - <span class="it"></span> - <span class="ja">ビデオ</span> - <span class="zh-CN"></span> - <span class="zh-TW"></span> + <span style="display:none" class="de"></span> + <span style="display:none" class="es"></span> + <span style="display:none" class="fr"></span> + <span style="display:none" class="it"></span> + <span style="display:none" class="ja">ビデオ</span> + <span style="display:none" class="zh-CN"></span> + <span style="display:none" class="zh-TW"></span> <?cs /if ?> </a></li> <li id="community-link"><a href="<?cs var:toroot ?>community/index.html"> <?cs if:!sdk.redirect ?> <span class="en">Community</span> - <span class="de"></span> - <span class="es">Comunidad</span> - <span class="fr">Communauté</span> - <span class="it"></span> - <span class="ja">コミュニティ</span> - <span class="zh-CN">社区</span> - <span class="zh-TW">社群</span> + <span style="display:none" class="de"></span> + <span style="display:none" class="es">Comunidad</span> + <span style="display:none" class="fr">Communauté</span> + <span style="display:none" class="it"></span> + <span style="display:none" class="ja">コミュニティ</span> + <span style="display:none" class="zh-CN">社区</span> + <span style="display:none" class="zh-TW">社群</span> <?cs /if ?> </a></li> diff --git a/tools/droiddoc/templates-sdk/sdkpage.cs b/tools/droiddoc/templates-sdk/sdkpage.cs index fdc1df5..ebaee07 100644 --- a/tools/droiddoc/templates-sdk/sdkpage.cs +++ b/tools/droiddoc/templates-sdk/sdkpage.cs @@ -33,28 +33,29 @@ <div class="g-unit" id="doc-content" > <div id="jd-header" class="guide-header" > <span class="crumb"> </span> - <h1><?cs if:android.whichdoc == "online" ?>Download <?cs /if ?><?cs var:page.title ?></h1> + <h1><?cs if:android.whichdoc == "online" ?>Download the <?cs /if ?><?cs var:page.title ?></h1> </div> <div id="jd-content"> - <p><em><?cs - if:ndk ?><?cs - var:ndk.date ?><?cs + <?cs + if:ndk ?><p><em><?cs + var:ndk.date ?></em></p><?cs else ?><?cs - var:sdk.date ?><?cs - /if ?></em> - </p> + if:android.whichdoc == "online" ?><p><em><?cs + var:sdk.date ?></em></p><?cs + /if ?><?cs + /if ?> <?cs if:sdk.not_latest_version ?> <div class="special"> <p><strong>This is NOT the current Android SDK release.</strong></p> - <p><a href="/sdk/<?cs var:sdk.current ?>/index.html">Download the current Android SDK</a></p> + <p><a href="/sdk/index.html">Download the current Android SDK</a></p> </div> <?cs /if ?> <?cs if:android.whichdoc != "online" && !android.preview ?> -<p>The sections below provide an overview of the SDK package. </p> +<!-- <p>The sections below provide an overview of how to install the SDK package. </p> --> <?cs else ?> <?cs if:ndk ?> @@ -62,10 +63,10 @@ <p>The Android NDK is a companion tool to the Android SDK that lets Android application developers build performance-critical portions of their apps in native code. It is designed for use <em>only</em> in conjunction with the -Android SDK, so if you have not already installed the Android 1.5 SDK, please do -so before downloading the NDK. Also, please read <a href="#overview">What is the -Android NDK?</a> to get an understanding of what the NDK offers and whether it -will be useful to you.</p> +Android SDK, so if you have not already installed the latest Android SDK, please +do so before downloading the NDK. Also, please read <a href="#overview">What is +the Android NDK?</a> to get an understanding of what the NDK offers and whether +it will be useful to you.</p> <p>Select the download package that is appropriate for your development computer. </p> @@ -110,16 +111,49 @@ computer. </p> Android 1.6 and we are pleased to announce the availability of an early look SDK to give you a head-start on developing applications for it. </p> - <p>The Android 1.6 platform includes a variety of improvements and new - features for users and developers. Additionally, the SDK itself introduces - several new capabilities that enable you to develop applications more - efficiently. See the <a href="features.html">Android 1.6 Highlights</a> - document for a list of highlights.</p> - <?cs /if ?> + <p>The Android <?cs var:sdk.preview.version ?> platform includes a variety of + improvements and new features for users and developers. Additionally, the SDK + itself introduces several new capabilities that enable you to develop + applications more efficiently. See the <a href="features.html">Android <?cs + var:sdk.preview.version ?> Platform Highlights</a> document for a list of + highlights.</p> +<?cs /if ?> +<?cs # end if NDK ... the following is for the SDK ?> + + <div class="toggle-content special"> + <p>The Android SDK has changed! If you've worked with the Android SDK before, + you will notice several important differences:</p> + + <div class="toggle-content-toggleme" style="display:none"> + <ul style="padding-bottom:.0;"> + <li style="margin-top:.5em">The SDK downloadable package includes <em>only</em> + the latest version of the Android SDK Tools.</li> + <li>Once you've installed the SDK, you now use the Android SDK and AVD Manager + to download all of the SDK components that you need, such as Android platforms, + SDK add-ons, tools, and documentation. </li> + <li>The new approach is modular — you can install only the components you + need and update any or all components without affecting other parts of your + development environment.</li> + <li>In short, once you've installed the new SDK, you will not need to download + an SDK package again. Instead, you will use the Android SDK and AVD Manager to + keep your development environment up-to-date. </li> + </ul> + <p style="margin-top:0">If you are currently using the Android 1.6 SDK, you + do not need to install the new SDK, because your existing SDK already + includes the Android SDK and AVD Manager tool. To develop against Android + 2.0, for example, you can just download the Android 2.0 platform (and + updated SDK Tools) into your existing SDK. Refer to <a + href="adding-components.html">Adding SDK Components</a>.</p> + </div> + + <a href='#' class='toggle-content-button show' onclick="toggleContent(this);return false;"> + <span>show more</span><span style='display:none'>show less</span> + </a> + </div> -<p>Before downloading, please read the <a href="requirements.html"> -System Requirements</a> document. As you start the download, you will also need to review and agree to -the Terms and Conditions that govern the use of the Android SDK. </p> + <p>If you are new to the Android SDK, please read the <a href="#quickstart">Quick Start</a>, + below, for an overview of how to install and set up the SDK.</p> + <table class="download"> <tr> @@ -156,7 +190,7 @@ the Terms and Conditions that govern the use of the Android SDK. </p> <tr class="alt-color"> <td>ADT Plugin for Eclipse <?cs var:adt.zip_version ?></td> <td> - <a href="<?cs var:toroot ?>sdk/download.html?v=<?cs var:adt.zip_download ?>"><?cs var:adt.zip_download ?></a> + <a href="http://dl.google.com/android/<?cs var:adt.zip_download ?>"><?cs var:adt.zip_download ?></a> </td> <td><?cs var:adt.zip_bytes ?> bytes</td> <td><?cs var:adt.zip_checksum ?></td> @@ -170,14 +204,17 @@ the Terms and Conditions that govern the use of the Android SDK. </p> <?cs if:android.whichdoc != "online" && sdk.preview ?> <p>Welcome developers! The next release of the Android platform will be - Android 1.6 and we are pleased to announce the availability of an early look SDK - to give you a head-start on developing applications for it. </p> - - <p>The Android 1.6 platform includes a variety of improvements and new features - for users and developers. Additionally, the SDK itself introduces several new - capabilities that enable you to develop applications more efficiently. - See the <a href="http://developer.android.com/sdk/preview/features.html"> - Android 1.6 Highlights</a> document for a list of highlights.</p> +Android <?cs var:sdk.preview.version ?> and we are pleased to announce the +availability of an early look SDK to give you a head-start on developing +applications for it. </p> + + <p>The Android <?cs var:sdk.preview.version ?> platform includes a variety of +improvements and new features for users and developers. Additionally, the SDK +itself introduces several new capabilities that enable you to develop +applications more efficiently. See the <a +href="http://developer.android.com/sdk/preview/features.html">Android +<?cs var:sdk.preview.version ?> Highlights</a> document for a list of +highlights.</p> <?cs /if ?> <?cs call:tag_list(root.descr) ?> diff --git a/tools/droiddoc/templates/assets/android-developer-core.css b/tools/droiddoc/templates/assets/android-developer-core.css index acb873d..2b30c05 100644 --- a/tools/droiddoc/templates/assets/android-developer-core.css +++ b/tools/droiddoc/templates/assets/android-developer-core.css @@ -48,11 +48,12 @@ a:visited code { } input, select, -textarea, option { +textarea, option, label { font-family:inherit; font-size:inherit; padding:0; margin:0; + vertical-align:middle; } option { @@ -81,6 +82,7 @@ pre { padding:10px; margin:0 0 1em 1em; overflow:auto; + line-height:inherit; /* fixes vertical scrolling in webkit */ } h1,h2,h3,h4,h5 { @@ -194,7 +196,7 @@ hr.blue { height: 114px; position:relative; z-index:100; - min-width:576px; + min-width:675px; /* min width for the tabs, before they wrap */ padding:0 10px; border-bottom:3px solid #94b922; } diff --git a/tools/droiddoc/templates/assets/android-developer-docs.css b/tools/droiddoc/templates/assets/android-developer-docs.css index d72098f..c52222c 100644 --- a/tools/droiddoc/templates/assets/android-developer-docs.css +++ b/tools/droiddoc/templates/assets/android-developer-docs.css @@ -339,9 +339,12 @@ links to summary tables) */ } #api-level-toggle { - float:right; padding:0 10px; font-size:11px; + float:right; +} + +#api-level-toggle label.disabled { color:#999; } @@ -660,7 +663,27 @@ h5.jd-tagtitle { div.special { padding: .5em 1em 1em 1em; margin: 0 0 1em; - background-color: #ddf0f2; + background-color: #DAF3FC; + border:1px solid #d3ecf5; + border-radius:5px; + -moz-border-radius:5px; + -webkit-border-radius:5px; +} + +.toggle-content-toggleme { + display:none; +} + +.toggle-content-button { + font-size:.9em; + line-height:.9em; + text-decoration:none; + position:relative; + top:5px; +} + +.toggle-content-button:hover { + text-decoration:underline; } div.special p { @@ -751,6 +774,8 @@ ul.no-style { font-weight: bold; color: red; text-decoration: none; + vertical-align:top; + line-height:.9em; } pre.classic { diff --git a/tools/droiddoc/templates/assets/android-developer-docs.js b/tools/droiddoc/templates/assets/android-developer-docs.js index b16ed0d..6431163 100644 --- a/tools/droiddoc/templates/assets/android-developer-docs.js +++ b/tools/droiddoc/templates/assets/android-developer-docs.js @@ -10,6 +10,7 @@ var NAV_PREF_PANELS = "panels"; var nav_pref; var toRoot; var isMobile = false; // true if mobile, so we can adjust some layout +var isIE6 = false; // true if IE6 function addLoadEvent(newfun) { var current = window.onload; @@ -24,17 +25,24 @@ function addLoadEvent(newfun) { } var agent = navigator['userAgent']; +// If a mobile phone, set flag and do mobile setup if ((agent.indexOf("Mobile") != -1) || (agent.indexOf("BlackBerry") != -1) || (agent.indexOf("Mini") != -1)) { isMobile = true; addLoadEvent(mobileSetup); +// If not a mobile browser, set the onresize event for IE6, and others +} else if (agent.indexOf("MSIE 6.0") != -1) { + isIE6 = true; + addLoadEvent(function() { + window.onresize = resizeAll; + }); +} else { + addLoadEvent(function() { + window.onresize = resizeHeight; + }); } -addLoadEvent(function() { -window.onresize = resizeAll; -}); - function mobileSetup() { $("body").css({'overflow':'auto'}); $("html").css({'overflow':'auto'}); @@ -50,7 +58,7 @@ addLoadEvent( function() { var lists = document.createElement("script"); lists.setAttribute("type","text/javascript"); lists.setAttribute("src", toRoot+"reference/lists.js"); - $("head").append($(lists)); + document.getElementsByTagName("head")[0].appendChild(lists); } ); function setToRoot(root) { @@ -60,8 +68,12 @@ function setToRoot(root) { function restoreWidth(navWidth) { var windowWidth = $(window).width() + "px"; - content.css({marginLeft:parseInt(navWidth) + 6 + "px", //account for 6px-wide handle-bar - width:parseInt(windowWidth) - parseInt(navWidth) - 6 + "px"}); + content.css({marginLeft:parseInt(navWidth) + 6 + "px"}); //account for 6px-wide handle-bar + + if (isIE6) { + content.css({width:parseInt(windowWidth) - parseInt(navWidth) - 6 + "px"}); // necessary in order for scrollbars to be visible + } + sidenav.css({width:navWidth}); resizePackagesNav.css({width:navWidth}); classesNav.css({width:navWidth}); @@ -99,14 +111,14 @@ function readCookie(cookie) { } function writeCookie(cookie, val, section, expiration) { - if (!val) return; + if (val==undefined) return; section = section == null ? "_" : "_"+section+"_"; if (expiration == null) { var date = new Date(); date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week expiration = date.toGMTString(); } - document.cookie = cookie_namespace+section+cookie+"="+val+"; expires="+expiration+"; path=/"; + document.cookie = cookie_namespace + section + cookie + "=" + val + "; expires=" + expiration+"; path=/"; } function init() { @@ -124,7 +136,7 @@ function init() { } if (!isMobile) { - $("#resize-packages-nav").resizable({handles: "s", resize: function(e, ui) { resizeHeight(); } }); + $("#resize-packages-nav").resizable({handles: "s", resize: function(e, ui) { resizePackagesHeight(); } }); $(".side-nav-resizable").resizable({handles: "e", resize: function(e, ui) { resizeWidth(); } }); var cookieWidth = readCookie(cookiePath+'width'); var cookieHeight = readCookie(cookiePath+'height'); @@ -156,8 +168,8 @@ function highlightNav(fullPageName) { var htmlPos = fullPageName.lastIndexOf(".html", fullPageName.length); var pathPageName = fullPageName.slice(firstSlashPos, htmlPos + 5); var link = $("#devdoc-nav a[href$='"+ pathPageName+"']"); - if ((link.length == 0) && ((fullPageName.indexOf("/guide/") != -1) || (fullPageName.indexOf("/sdk/") != -1))) { -// if there's no match, then let's backstep through the directory until we find an index.html page that matches our ancestor directories (only for dev guide and sdk) + if ((link.length == 0) && (fullPageName.indexOf("/guide/") != -1)) { +// if there's no match, then let's backstep through the directory until we find an index.html page that matches our ancestor directories (only for dev guide) lastBackstep = pathPageName.lastIndexOf("/"); while (link.length == 0) { backstepDirectory = pathPageName.lastIndexOf("/", lastBackstep); @@ -174,23 +186,46 @@ function highlightNav(fullPageName) { } } -function resizeHeight() { +/* Resize the height of the nav panels in the reference, + * and save the new size to a cookie */ +function resizePackagesHeight() { var windowHeight = ($(window).height() - HEADER_HEIGHT); - var swapperHeight = windowHeight - 13; - $("#swapper").css({height:swapperHeight + "px"}); - sidenav.css({height:windowHeight + "px"}); - content.css({height:windowHeight + "px"}); + var swapperHeight = windowHeight - 13; // move 13px for swapper link at the bottom resizePackagesNav.css({maxHeight:swapperHeight + "px"}); classesNav.css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"}); + + $("#swapper").css({height:swapperHeight + "px"}); $("#packages-nav").css({height:parseInt(resizePackagesNav.css("height")) - 6 + "px"}); //move 6px for handle - devdocNav.css({height:sidenav.css("height")}); - $("#nav-tree").css({height:swapperHeight + "px"}); - + var basePath = getBaseUri(location.pathname); var section = basePath.substring(1,basePath.indexOf("/",1)); writeCookie("height", resizePackagesNav.css("height"), section, null); } +/* Resize the height of the side-nav and doc-content divs, + * which creates the frame effect */ +function resizeHeight() { + // Get the window height and always resize the doc-content and side-nav divs + var windowHeight = ($(window).height() - HEADER_HEIGHT); + content.css({height:windowHeight + "px"}); + sidenav.css({height:windowHeight + "px"}); + + var href = location.href; + // If in the reference docs, also resize the "swapper", "classes-nav", and "nav-tree" divs + if (href.indexOf("/reference/") != -1) { + var swapperHeight = windowHeight - 13; + $("#swapper").css({height:swapperHeight + "px"}); + $("#classes-nav").css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"}); + $("#nav-tree").css({height:swapperHeight + "px"}); + + // If in the dev guide docs, also resize the "devdoc-nav" div + } else if (href.indexOf("/guide/") != -1) { + $("#devdoc-nav").css({height:sidenav.css("height")}); + } +} + +/* Resize the width of the "side-nav" and the left margin of the "doc-content" div, + * which creates the resizable side bar */ function resizeWidth() { var windowWidth = $(window).width() + "px"; if (sidenav.length) { @@ -198,24 +233,27 @@ function resizeWidth() { } else { var sidenavWidth = 0; } - content.css({marginLeft:parseInt(sidenavWidth) + 6 + "px", //account for 6px-wide handle-bar - width:parseInt(windowWidth) - parseInt(sidenavWidth) - 6 + "px"}); + content.css({marginLeft:parseInt(sidenavWidth) + 6 + "px"}); //account for 6px-wide handle-bar + + if (isIE6) { + content.css({width:parseInt(windowWidth) - parseInt(sidenavWidth) - 6 + "px"}); // necessary in order to for scrollbars to be visible + } + resizePackagesNav.css({width:sidenavWidth}); classesNav.css({width:sidenavWidth}); $("#packages-nav").css({width:sidenavWidth}); - + var basePath = getBaseUri(location.pathname); var section = basePath.substring(1,basePath.indexOf("/",1)); writeCookie("width", sidenavWidth, section, null); } +/* For IE6 only, + * because it can't properly perform auto width for "doc-content" div, + * avoiding this for all browsers provides better performance */ function resizeAll() { - if (!isMobile) { - resizeHeight(); - if ($(".side-nav-resizable").length) { - resizeWidth(); - } - } + resizeHeight(); + resizeWidth(); } function getBaseUri(uri) { @@ -448,3 +486,18 @@ function getLangPref() { } return (lang != 0) ? lang : 'en'; } + + +function toggleContent(obj) { + var button = $(obj); + var div = $(obj.parentNode); + var toggleMe = $(".toggle-content-toggleme",div); + if (button.hasClass("show")) { + toggleMe.slideDown(); + button.removeClass("show").addClass("hide"); + } else { + toggleMe.slideUp(); + button.removeClass("hide").addClass("show"); + } + $("span", button).toggle(); +} diff --git a/tools/droiddoc/templates/assets/android-developer-reference.js b/tools/droiddoc/templates/assets/android-developer-reference.js index 3080760..6299596 100644 --- a/tools/droiddoc/templates/assets/android-developer-reference.js +++ b/tools/droiddoc/templates/assets/android-developer-reference.js @@ -1,48 +1,78 @@ /* API LEVEL TOGGLE */ addLoadEvent(changeApiLevel); + +var API_LEVEL_ENABLED_COOKIE = "api_level_enabled"; var API_LEVEL_COOKIE = "api_level"; var minLevel = 1; -function buildApiLevelToggle() { - var maxLevel = SINCE_DATA.length; - var userApiLevel = readCookie(API_LEVEL_COOKIE); - - if (userApiLevel != 0) { - selectedLevel = userApiLevel; - } else { - selectedLevel = maxLevel; - } +function toggleApiLevelSelector(checkbox) { + var date = new Date(); + date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years + var expiration = date.toGMTString(); + if (checkbox.checked) { + $("#apiLevelSelector").removeAttr("disabled"); + $("#api-level-toggle label").removeClass("disabled"); + writeCookie(API_LEVEL_ENABLED_COOKIE, 1, null, expiration); + } else { + $("#apiLevelSelector").attr("disabled","disabled"); + $("#api-level-toggle label").addClass("disabled"); + writeCookie(API_LEVEL_ENABLED_COOKIE, 0, null, expiration); + } + changeApiLevel(); +} + +function buildApiLevelSelector() { + var maxLevel = SINCE_DATA.length; + var userApiLevelEnabled = readCookie(API_LEVEL_ENABLED_COOKIE); + var userApiLevel = readCookie(API_LEVEL_COOKIE); + userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default + + if (userApiLevelEnabled == 0) { + $("#apiLevelSelector").attr("disabled","disabled"); + } else { + $("#apiLevelCheckbox").attr("checked","checked"); + $("#api-level-toggle label").removeClass("disabled"); + } minLevel = $("body").attr("class"); - var select = $("#apiLevelControl").html("").change(changeApiLevel); - for (var i = maxLevel-1; i >= 0; i--) { - var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]); -// if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames) - select.append(option); - } + var select = $("#apiLevelSelector").html("").change(changeApiLevel); + for (var i = maxLevel-1; i >= 0; i--) { + var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]); + // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames) + select.append(option); + } // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true) - var selectedLevelItem = $("#apiLevelControl option[value='"+selectedLevel+"']").get(0); + var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0); selectedLevelItem.setAttribute('selected',true); } function changeApiLevel() { - var selectedLevel = $("#apiLevelControl option:selected").val(); - toggleVisisbleApis(selectedLevel, "body"); - - var date = new Date(); - date.setTime(date.getTime()+(50*365*24*60*60*1000)); // keep this for 50 years - writeCookie(API_LEVEL_COOKIE, selectedLevel, null, date); + var maxLevel = SINCE_DATA.length; + var userApiLevelEnabled = readCookie(API_LEVEL_ENABLED_COOKIE); + var selectedLevel = maxLevel; + + if (userApiLevelEnabled == 0) { + toggleVisisbleApis(selectedLevel, "body"); + } else { + selectedLevel = $("#apiLevelSelector option:selected").val(); + toggleVisisbleApis(selectedLevel, "body"); + + var date = new Date(); + date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years + var expiration = date.toGMTString(); + writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration); + } - if (selectedLevel < minLevel) { - var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class"; - $("#naMessage").show().html("<div><p><strong>This " + thing + " is not available with API Level " + selectedLevel + ".</strong></p>" + if (selectedLevel < minLevel) { + var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class"; + $("#naMessage").show().html("<div><p><strong>This " + thing + " is not available with API Level " + selectedLevel + ".</strong></p>" + "<p>To use this " + thing + ", your application must specify API Level " + minLevel + " or higher in its manifest " + "and be compiled against a version of the Android library that supports an equal or higher API Level. To reveal this " + "document, change the value of the API Level filter above.</p>" + "<p><a href='" +toRoot+ "guide/appendix/api-levels.html'>What is the API Level?</a></p></div>"); - } else { + } else { $("#naMessage").hide(); } } @@ -156,7 +186,7 @@ function expand_node(me, node) node.expanded = true; // perform api level toggling because new nodes are new to the DOM - var selectedLevel = $("#apiLevelControl option:selected").val(); + var selectedLevel = $("#apiLevelSelector option:selected").val(); toggleVisisbleApis(selectedLevel, "#side-nav"); } } @@ -228,7 +258,7 @@ function init_default_navtree(toroot) { init_navtree("nav-tree", toroot, NAVTREE_DATA); // perform api level toggling because because the whole tree is new to the DOM - var selectedLevel = $("#apiLevelControl option:selected").val(); + var selectedLevel = $("#apiLevelSelector option:selected").val(); toggleVisisbleApis(selectedLevel, "#side-nav"); } diff --git a/tools/droiddoc/templates/assets/images/home/donut-android.png b/tools/droiddoc/templates/assets/images/home/donut-android.png Binary files differindex 85bc952..6aba06b 100644..100755 --- a/tools/droiddoc/templates/assets/images/home/donut-android.png +++ b/tools/droiddoc/templates/assets/images/home/donut-android.png diff --git a/tools/droiddoc/templates/assets/images/home/eclair-android.png b/tools/droiddoc/templates/assets/images/home/eclair-android.png Binary files differnew file mode 100644 index 0000000..d476ce9 --- /dev/null +++ b/tools/droiddoc/templates/assets/images/home/eclair-android.png diff --git a/tools/droiddoc/templates/assets/search_autocomplete.js b/tools/droiddoc/templates/assets/search_autocomplete.js index 929751f..086674a 100644 --- a/tools/droiddoc/templates/assets/search_autocomplete.js +++ b/tools/droiddoc/templates/assets/search_autocomplete.js @@ -168,6 +168,6 @@ function search_focus_changed(obj, focused) function submit_search() { var query = document.getElementById('search_autocomplete').value; - document.location = toRoot + 'search.html#q=' + query; // toRoot is initialized in android-developer-docs.js + document.location = toRoot + 'search.html#q=' + query + '&t=0'; return false; } diff --git a/tools/droiddoc/templates/class.cs b/tools/droiddoc/templates/class.cs index 9bc2ba0..89eb927 100644 --- a/tools/droiddoc/templates/class.cs +++ b/tools/droiddoc/templates/class.cs @@ -188,7 +188,7 @@ Summary: <?cs # summary macros ?> -<?cs def:write_method_summary(methods) ?> +<?cs def:write_method_summary(methods, included) ?> <?cs set:count = #1 ?> <?cs each:method = methods ?> <?cs # The apilevel-N class MUST BE LAST in the sequence of class names ?> @@ -202,8 +202,7 @@ Summary: <?cs call:type_link(method.returnType) ?></nobr> </td> <td class="jd-linkcol" width="100%"><nobr> - <span class="sympad"><a href="<?cs var:toroot ?><?cs var:method.href ?>"> - <?cs var:method.name ?></a></span>(<?cs call:parameter_list(method.params) ?>)</nobr> + <span class="sympad"><?cs call:cond_link(method.name, toroot, method.href, included) ?></span>(<?cs call:parameter_list(method.params) ?>)</nobr> <?cs if:subcount(method.shortDescr) || subcount(method.deprecated) ?> <div class="jd-descrdiv"><?cs call:short_descr(method) ?></div> <?cs /if ?> @@ -212,7 +211,7 @@ Summary: <?cs /each ?> <?cs /def ?> -<?cs def:write_field_summary(fields) ?> +<?cs def:write_field_summary(fields, included) ?> <?cs set:count = #1 ?> <?cs each:field=fields ?> <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:field.since ?>" > @@ -221,26 +220,26 @@ Summary: <?cs var:field.static ?> <?cs var:field.final ?> <?cs call:type_link(field.type) ?></nobr></td> - <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a></td> + <td class="jd-linkcol"><?cs call:cond_link(field.name, toroot, field.href, included) ?></td> <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?></td> </tr> <?cs set:count = count + #1 ?> <?cs /each ?> <?cs /def ?> -<?cs def:write_constant_summary(fields) ?> +<?cs def:write_constant_summary(fields, included) ?> <?cs set:count = #1 ?> <?cs each:field=fields ?> <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:field.since ?>" > <td class="jd-typecol"><?cs call:type_link(field.type) ?></td> - <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a></td> + <td class="jd-linkcol"><?cs call:cond_link(field.name, toroot, field.href, included) ?></td> <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?></td> </tr> <?cs set:count = count + #1 ?> <?cs /each ?> <?cs /def ?> -<?cs def:write_attr_summary(attrs) ?> +<?cs def:write_attr_summary(attrs, included) ?> <?cs set:count = #1 ?> <tr> <td><nobr><em>Attribute Name</em></nobr></td> @@ -249,9 +248,9 @@ Summary: </tr> <?cs each:attr=attrs ?> <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:attr.since ?>" > - <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:attr.href ?>"><?cs var:attr.name ?></a></td> + <td class="jd-linkcol"><?cs if:included ?><a href="<?cs var:toroot ?><?cs var:attr.href ?>"><?cs /if ?><?cs var:attr.name ?><?cs if:included ?></a><?cs /if ?></td> <td class="jd-linkcol"><?cs each:m=attr.methods ?> - <a href="<?cs var:toroot ?><?cs var:m.href ?>"><?cs var:m.name ?></a> + <?cs call:cond_link(m.name, toroot, m.href, included) ?> <?cs /each ?> </td> <td class="jd-descrcol" width="100%"><?cs call:short_descr(attr) ?> </td> @@ -293,7 +292,7 @@ Summary: <?cs if:subcount(class.attrs) ?> <!-- =========== FIELD SUMMARY =========== --> <table id="lattrs" class="jd-sumtable"><tr><th colspan="12">XML Attributes</th></tr> -<?cs call:write_attr_summary(class.attrs) ?> +<?cs call:write_attr_summary(class.attrs, 1) ?> <?cs /if ?> <?cs # if there are inherited attrs, write the table ?> @@ -308,14 +307,14 @@ Summary: <tr class="api apilevel-<?cs var:cl.since ?>" > <td colspan="12"> <?cs call:expando_trigger("inherited-attrs-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?> -<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a> +<?cs call:cond_link(cl.qualified, toroot, cl.link, cl.included) ?> <div id="inherited-attrs-<?cs var:cl.qualified ?>"> <div id="inherited-attrs-<?cs var:cl.qualified ?>-list" class="jd-inheritedlinks"> </div> <div id="inherited-attrs-<?cs var:cl.qualified ?>-summary" style="display: none;"> <table class="jd-sumtable-expando"> - <?cs call:write_attr_summary(cl.attrs) ?></table> + <?cs call:write_attr_summary(cl.attrs, cl.included) ?></table> </div> </div> </td></tr> @@ -332,7 +331,7 @@ Summary: <?cs each:field=class.enumConstants ?> <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:field.since ?>" > <td class="jd-descrcol"><?cs call:type_link(field.type) ?> </td> - <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a> </td> + <td class="jd-linkcol"><?cs call:cond_link(field.name, toroot, field.href, cl.included) ?> </td> <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?> </td> </tr> <?cs set:count = count + #1 ?> @@ -343,7 +342,7 @@ Summary: <?cs # this next line must be exactly like this to be parsed by eclipse ?> <!-- =========== ENUM CONSTANT SUMMARY =========== --> <table id="constants" class="jd-sumtable"><tr><th colspan="12">Constants</th></tr> -<?cs call:write_constant_summary(class.constants) ?> +<?cs call:write_constant_summary(class.constants, 1) ?> </table> <?cs /if ?> @@ -359,14 +358,14 @@ Summary: <tr class="api apilevel-<?cs var:cl.since ?>" > <td colspan="12"> <?cs call:expando_trigger("inherited-constants-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?> -<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a> +<?cs call:cond_link(cl.qualified, toroot, cl.link, cl.included) ?> <div id="inherited-constants-<?cs var:cl.qualified ?>"> <div id="inherited-constants-<?cs var:cl.qualified ?>-list" class="jd-inheritedlinks"> </div> <div id="inherited-constants-<?cs var:cl.qualified ?>-summary" style="display: none;"> <table class="jd-sumtable-expando"> - <?cs call:write_constant_summary(cl.constants) ?></table> + <?cs call:write_constant_summary(cl.constants, cl.included) ?></table> </div> </div> </td></tr> @@ -379,7 +378,7 @@ Summary: <?cs # this next line must be exactly like this to be parsed by eclipse ?> <!-- =========== FIELD SUMMARY =========== --> <table id="lfields" class="jd-sumtable"><tr><th colspan="12">Fields</th></tr> -<?cs call:write_field_summary(class.fields) ?> +<?cs call:write_field_summary(class.fields, 1) ?> </table> <?cs /if ?> @@ -395,14 +394,14 @@ Summary: <tr class="api apilevel-<?cs var:cl.since ?>" > <td colspan="12"> <?cs call:expando_trigger("inherited-fields-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?> -<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a> +<?cs call:cond_link(cl.qualified, toroot, cl.link, cl.included) ?> <div id="inherited-fields-<?cs var:cl.qualified ?>"> <div id="inherited-fields-<?cs var:cl.qualified ?>-list" class="jd-inheritedlinks"> </div> <div id="inherited-fields-<?cs var:cl.qualified ?>-summary" style="display: none;"> <table class="jd-sumtable-expando"> - <?cs call:write_field_summary(cl.fields) ?></table> + <?cs call:write_field_summary(cl.fields, cl.included) ?></table> </div> </div> </td></tr> @@ -415,7 +414,7 @@ Summary: <?cs # this next line must be exactly like this to be parsed by eclipse ?> <!-- ======== CONSTRUCTOR SUMMARY ======== --> <table id="pubctors" class="jd-sumtable"><tr><th colspan="12">Public Constructors</th></tr> -<?cs call:write_method_summary(class.ctors.public) ?> +<?cs call:write_method_summary(class.ctors.public, 1) ?> </table> <?cs /if ?> @@ -423,7 +422,7 @@ Summary: <?cs # this next line must be exactly like this to be parsed by eclipse ?> <!-- ======== CONSTRUCTOR SUMMARY ======== --> <table id="proctors" class="jd-sumtable"><tr><th colspan="12">Protected Constructors</th></tr> -<?cs call:write_method_summary(class.ctors.protected) ?> +<?cs call:write_method_summary(class.ctors.protected, 1) ?> </table> <?cs /if ?> @@ -431,7 +430,7 @@ Summary: <?cs # this next line must be exactly like this to be parsed by eclipse ?> <!-- ========== METHOD SUMMARY =========== --> <table id="pubmethods" class="jd-sumtable"><tr><th colspan="12">Public Methods</th></tr> -<?cs call:write_method_summary(class.methods.public) ?> +<?cs call:write_method_summary(class.methods.public, 1) ?> </table> <?cs /if ?> @@ -439,7 +438,7 @@ Summary: <?cs # this next line must be exactly like this to be parsed by eclipse ?> <!-- ========== METHOD SUMMARY =========== --> <table id="promethods" class="jd-sumtable"><tr><th colspan="12">Protected Methods</th></tr> -<?cs call:write_method_summary(class.methods.protected) ?> +<?cs call:write_method_summary(class.methods.protected, 1) ?> </table> <?cs /if ?> @@ -454,14 +453,14 @@ Summary: <?cs if:subcount(cl.methods) ?> <tr class="api apilevel-<?cs var:cl.since ?>" > <td colspan="12"><?cs call:expando_trigger("inherited-methods-"+cl.qualified, "closed") ?> -From <?cs var:cl.kind ?> <a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a> +From <?cs var:cl.kind ?> <?cs call:cond_link(cl.qualified, toroot, cl.link, cl.included) ?> <div id="inherited-methods-<?cs var:cl.qualified ?>"> <div id="inherited-methods-<?cs var:cl.qualified ?>-list" class="jd-inheritedlinks"> </div> <div id="inherited-methods-<?cs var:cl.qualified ?>-summary" style="display: none;"> <table class="jd-sumtable-expando"> - <?cs call:write_method_summary(cl.methods) ?></table> + <?cs call:write_method_summary(cl.methods, cl.included) ?></table> </div> </div> </td></tr> diff --git a/tools/droiddoc/templates/macros.cs b/tools/droiddoc/templates/macros.cs index 14dc90e..0c59f32 100644 --- a/tools/droiddoc/templates/macros.cs +++ b/tools/droiddoc/templates/macros.cs @@ -39,6 +39,15 @@ def:type_link_impl(type, link) ?><?cs <?cs def:class_name(type) ?><?cs call:type_link_impl(type, "false") ?><?cs /def ?> <?cs def:type_link(type) ?><?cs call:type_link_impl(type, "true") ?><?cs /def ?> +<?cs # a conditional link. + if the "condition" parameter evals to true then the link is displayed + otherwise only the text is displayed +?><?cs +def:cond_link(text, root, path, condition) ?><?cs + if:condition ?><a href="<?cs var:root ?><?cs var:path ?>"><?cs /if ?><?cs var:text ?><?cs if:condition ?></a><?cs /if ?><?cs +/def ?> + + <?cs # A comma separated parameter list ?><?cs def:parameter_list(params) ?><?cs each:param = params ?><?cs @@ -64,6 +73,15 @@ def:tag_list(tags) ?><?cs elif:tag.kind == "@sdkCurrent" ?><?cs var:sdk.current ?><?cs elif:tag.kind == "@sdkCurrentVersion" ?><?cs var:sdk.version ?><?cs elif:tag.kind == "@sdkCurrentRelId" ?><?cs var:sdk.rel.id ?><?cs + elif:tag.kind == "@sdkPlatformVersion" ?><?cs var:sdk.platform.version ?><?cs + elif:tag.kind == "@sdkPlatformApiLevel" ?><?cs var:sdk.platform.apiLevel ?><?cs + elif:tag.kind == "@sdkPlatformMajorMinor" ?><?cs var:sdk.platform.majorMinor ?><?cs + elif:tag.kind == "@sdkPlatformReleaseDate" ?><?cs var:sdk.platform.releaseDate ?><?cs + elif:tag.kind == "@sdkPlatformDeployableDate" ?><?cs var:sdk.platform.deployableDate ?><?cs + elif:tag.kind == "@adtZipVersion" ?><?cs var:adt.zip.version ?><?cs + elif:tag.kind == "@adtZipDownload" ?><?cs var:adt.zip.download ?><?cs + elif:tag.kind == "@adtZipBytes" ?><?cs var:adt.zip.bytes ?><?cs + elif:tag.kind == "@adtZipChecksum" ?><?cs var:adt.zip.checksum ?><?cs elif:tag.kind == "@inheritDoc" ?><?cs # This is the case when @inheritDoc is in something that doesn't inherit from anything?><?cs elif:tag.kind == "@attr" ?><?cs @@ -89,8 +107,8 @@ def:short_descr(obj) ?><?cs <?cs # Show the red box with the deprecated warning ?><?cs def:deprecated_warning(obj) ?><?cs if:subcount(obj.deprecated) ?><p> - <p class="warning jd-deprecated-warning"> - <strong><?cs call:deprecated_text(obj.kind) ?></strong><?cs + <p class="caution"> + <strong><?cs call:deprecated_text(obj.kind) ?></strong><br/> <?cs call:tag_list(obj.deprecated) ?> </p><?cs /if ?><?cs diff --git a/tools/findleaves.py b/tools/findleaves.py new file mode 100755 index 0000000..0adf188 --- /dev/null +++ b/tools/findleaves.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# +# Copyright (C) 2009 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. +# + +# +# Finds files with the specified name under a particular directory, stopping +# the search in a given subdirectory when the file is found. +# + +import os +import sys + +def perform_find(mindepth, prune, dirlist, filename): + result = [] + pruneleaves = set(map(lambda x: os.path.split(x)[1], prune)) + for rootdir in dirlist: + rootdepth = rootdir.count("/") + for root, dirs, files in os.walk(rootdir): + # prune + check_prune = False + for d in dirs: + if d in pruneleaves: + check_prune = True + break + if check_prune: + i = 0 + while i < len(dirs): + if dirs[i] in prune: + del dirs[i] + else: + i += 1 + # mindepth + if mindepth > 0: + depth = 1 + root.count("/") - rootdepth + if depth < mindepth: + continue + # match + if filename in files: + result.append(os.path.join(root, filename)) + del dirs[:] + return result + +def usage(): + sys.stderr.write("""Usage: %(progName)s [<options>] <dirlist> <filename> +Options: + --mindepth=<mindepth> + Both behave in the same way as their find(1) equivalents. + --prune=<dirname> + Avoids returning results from inside any directory called <dirname> + (e.g., "*/out/*"). May be used multiple times. +""" % { + "progName": os.path.split(sys.argv[0])[1], + }) + sys.exit(1) + +def main(argv): + mindepth = -1 + prune = [] + i=1 + while i<len(argv) and len(argv[i])>2 and argv[i][0:2] == "--": + arg = argv[i] + if arg.startswith("--mindepth="): + try: + mindepth = int(arg[len("--mindepth="):]) + except ValueError: + usage() + elif arg.startswith("--prune="): + p = arg[len("--prune="):] + if len(p) == 0: + usage() + prune.append(p) + else: + usage() + i += 1 + if len(argv)-i < 2: # need both <dirlist> and <filename> + usage() + dirlist = argv[i:-1] + filename = argv[-1] + results = perform_find(mindepth, prune, dirlist, filename) + results.sort() + for r in set(results): + print r + +if __name__ == "__main__": + main(sys.argv) diff --git a/tools/findleaves.sh b/tools/findleaves.sh deleted file mode 100755 index 74bfd24..0000000 --- a/tools/findleaves.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/bash -# -# 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. -# - -# -# Finds files with the specified name under a particular directory, stopping -# the search in a given subdirectory when the file is found. -# - -set -o nounset # fail when dereferencing unset variables -set -o errexit # fail if any subcommand fails - -progName=`basename $0` - -function warn() { - echo "$progName: $@" >&2 -} - -function trace() { - echo "$progName: $@" -} - -function usage() { - if [[ $# > 0 ]] - then - warn $@ - fi - cat <<-EOF -Usage: $progName [<options>] <dirlist> <filename> -Options: - --mindepth=<mindepth> - --maxdepth=<maxdepth> - Both behave in the same way as their find(1) equivalents. - --prune=<glob> - Avoids returning results from any path matching the given glob-style - pattern (e.g., "*/out/*"). May be used multiple times. -EOF - exit 1 -} - -function fail() { - warn $@ - exit 1 -} - -if [ $# -lt 2 ] -then - usage -fi - -findargs="" -while [[ "${1:0:2}" == "--" ]] -do - arg=${1:2} - name=${arg%%=*} - value=${arg##*=} - if [[ "$name" == "mindepth" || "$name" == "maxdepth" ]] - then - # Add to beginning of findargs; these must come before the expression. - findargs="-$name $value $findargs" - elif [[ "$name" == "prune" ]] - then - # Add to end of findargs; these are part of the expression. - findargs="$findargs -path $value -prune -or" - fi - shift -done - -nargs=$# -# The filename is the last argument -filename="${!nargs}" - -# Print out all files that match, as long as the path isn't explicitly -# pruned. This will print out extraneous results from directories whose -# parents have a match. These are filtered out by the awk script below. -find -L "${@:1:$nargs-1}" $findargs -type f -name "$filename" -print 2>/dev/null | - -# Only pass along the directory of each match. -sed -e 's/\/[^\/]*$/\//' | - -# Sort the output, so directories appear immediately before their contents. -# If there are any duplicates, the awk script will implicitly ignore them. -# The LC_ALL=C forces sort(1) to use bytewise ordering instead of listening -# to the locale, which may do case-insensitive and/or alphanumeric-only -# sorting. -LC_ALL=C sort | - -# Always print the first line, which can't possibly be covered by a -# parent directory match. After that, only print lines where the last -# line printed isn't a prefix. -awk -v "filename=$filename" ' - (NR == 1) || (index($0, last) != 1) { - last = $0; - printf("%s%s\n", $0, filename); - } -' diff --git a/tools/releasetools/amend_generator.py b/tools/releasetools/amend_generator.py index 70e71d4..f8f4344 100644 --- a/tools/releasetools/amend_generator.py +++ b/tools/releasetools/amend_generator.py @@ -87,6 +87,10 @@ class AmendGenerator(object): 'dur' seconds.""" self.script.append("show_progress %f %d" % (frac, int(dur))) + def SetProgress(self, frac): + """Not implemented in amend.""" + pass + def PatchCheck(self, filename, *sha1): """Check that the given file (or MTD reference) has one of the given *sha1 hashes.""" diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py index 2e3138c..27264dd 100644 --- a/tools/releasetools/common.py +++ b/tools/releasetools/common.py @@ -15,6 +15,7 @@ import errno import getopt import getpass +import imp import os import re import shutil @@ -33,7 +34,7 @@ OPTIONS.search_path = "out/host/linux-x86" OPTIONS.max_image_size = {} OPTIONS.verbose = False OPTIONS.tempfiles = [] - +OPTIONS.device_specific = None class ExternalError(RuntimeError): pass @@ -184,14 +185,20 @@ def GetKeyPasswords(keylist): return key_passwords -def SignFile(input_name, output_name, key, password, align=None): +def SignFile(input_name, output_name, key, password, align=None, + whole_file=False): """Sign the input_name zip/jar/apk, producing output_name. Use the given key and password (the latter may be None if the key does not have a password. If align is an integer > 1, zipalign is run to align stored files in the output zip on 'align'-byte boundaries. + + If whole_file is true, use the "-w" option to SignApk to embed a + signature that covers the whole file in the archive comment of the + zip file. """ + if align == 0 or align == 1: align = None @@ -201,13 +208,14 @@ def SignFile(input_name, output_name, key, password, align=None): else: sign_name = output_name - p = Run(["java", "-jar", - os.path.join(OPTIONS.search_path, "framework", "signapk.jar"), - key + ".x509.pem", - key + ".pk8", - input_name, sign_name], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) + cmd = ["java", "-Xmx512m", "-jar", + os.path.join(OPTIONS.search_path, "framework", "signapk.jar")] + if whole_file: + cmd.append("-w") + cmd.extend([key + ".x509.pem", key + ".pk8", + input_name, sign_name]) + + p = Run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) if password is not None: password += "\n" p.communicate(password) @@ -247,6 +255,10 @@ COMMON_DOCSTRING = """ Prepend <dir>/bin to the list of places to search for binaries run by this script, and expect to find jars in <dir>/framework. + -s (--device_specific) <file> + Path to the python module containing device-specific + releasetools code. + -v (--verbose) Show command lines being executed. @@ -271,8 +283,9 @@ def ParseOptions(argv, try: opts, args = getopt.getopt( - argv, "hvp:" + extra_opts, - ["help", "verbose", "path="] + list(extra_long_opts)) + argv, "hvp:s:" + extra_opts, + ["help", "verbose", "path=", "device_specific="] + + list(extra_long_opts)) except getopt.GetoptError, err: Usage(docstring) print "**", str(err), "**" @@ -288,6 +301,8 @@ def ParseOptions(argv, OPTIONS.verbose = True elif o in ("-p", "--path"): OPTIONS.search_path = a + elif o in ("-s", "--device_specific"): + OPTIONS.device_specific = a else: if extra_option_handler is None or not extra_option_handler(o, a): assert False, "unknown option \"%s\"" % (o,) @@ -412,3 +427,68 @@ def ZipWriteStr(zip, filename, data, perms=0644): zinfo.compress_type = zip.compression zinfo.external_attr = perms << 16 zip.writestr(zinfo, data) + + +class DeviceSpecificParams(object): + module = None + def __init__(self, **kwargs): + """Keyword arguments to the constructor become attributes of this + object, which is passed to all functions in the device-specific + module.""" + for k, v in kwargs.iteritems(): + setattr(self, k, v) + + if self.module is None: + path = OPTIONS.device_specific + if not path: return + try: + if os.path.isdir(path): + info = imp.find_module("releasetools", [path]) + else: + d, f = os.path.split(path) + b, x = os.path.splitext(f) + if x == ".py": + f = b + info = imp.find_module(f, [d]) + self.module = imp.load_module("device_specific", *info) + except ImportError: + print "unable to load device-specific module; assuming none" + + def _DoCall(self, function_name, *args, **kwargs): + """Call the named function in the device-specific module, passing + the given args and kwargs. The first argument to the call will be + the DeviceSpecific object itself. If there is no module, or the + module does not define the function, return the value of the + 'default' kwarg (which itself defaults to None).""" + if self.module is None or not hasattr(self.module, function_name): + return kwargs.get("default", None) + return getattr(self.module, function_name)(*((self,) + args), **kwargs) + + def FullOTA_Assertions(self): + """Called after emitting the block of assertions at the top of a + full OTA package. Implementations can add whatever additional + assertions they like.""" + return self._DoCall("FullOTA_Assertions") + + def FullOTA_InstallEnd(self): + """Called at the end of full OTA installation; typically this is + used to install the image for the device's baseband processor.""" + return self._DoCall("FullOTA_InstallEnd") + + def IncrementalOTA_Assertions(self): + """Called after emitting the block of assertions at the top of an + incremental OTA package. Implementations can add whatever + additional assertions they like.""" + return self._DoCall("IncrementalOTA_Assertions") + + def IncrementalOTA_VerifyEnd(self): + """Called at the end of the verification phase of incremental OTA + installation; additional checks can be placed here to abort the + script before any changes are made.""" + return self._DoCall("IncrementalOTA_VerifyEnd") + + def IncrementalOTA_InstallEnd(self): + """Called at the end of incremental OTA installation; typically + this is used to install the image for the device's baseband + processor.""" + return self._DoCall("IncrementalOTA_InstallEnd") diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py index e7a15cd..d1902e1 100644 --- a/tools/releasetools/edify_generator.py +++ b/tools/releasetools/edify_generator.py @@ -100,9 +100,16 @@ class EdifyGenerator(object): def ShowProgress(self, frac, dur): """Update the progress bar, advancing it over 'frac' over the next - 'dur' seconds.""" + 'dur' seconds. 'dur' may be zero to advance it via SetProgress + commands instead of by time.""" self.script.append("show_progress(%f, %d);" % (frac, int(dur))) + def SetProgress(self, frac): + """Set the position of the progress bar within the chunk defined + by the most recent ShowProgress call. 'frac' should be in + [0,1].""" + self.script.append("set_progress(%f);" % (frac,)) + def PatchCheck(self, filename, *sha1): """Check that the given file (or MTD reference) has one of the given *sha1 hashes.""" diff --git a/tools/releasetools/img_from_target_files b/tools/releasetools/img_from_target_files index 00abde4..d157dca 100755 --- a/tools/releasetools/img_from_target_files +++ b/tools/releasetools/img_from_target_files @@ -31,6 +31,7 @@ if sys.hexversion < 0x02040000: print >> sys.stderr, "Python 2.4 or newer is required." sys.exit(1) +import errno import os import re import shutil @@ -82,8 +83,16 @@ def AddSystem(output_zip): # The name of the directory it is making an image out of matters to # mkyaffs2image. It wants "system" but we have a directory named # "SYSTEM", so create a symlink. - os.symlink(os.path.join(OPTIONS.input_tmp, "SYSTEM"), - os.path.join(OPTIONS.input_tmp, "system")) + try: + os.symlink(os.path.join(OPTIONS.input_tmp, "SYSTEM"), + os.path.join(OPTIONS.input_tmp, "system")) + except OSError, e: + # bogus error on my mac version? + # File "./build/tools/releasetools/img_from_target_files", line 86, in AddSystem + # os.path.join(OPTIONS.input_tmp, "system")) + # OSError: [Errno 17] File exists + if (e.errno == errno.EEXIST): + pass p = common.Run(["mkyaffs2image", "-f", os.path.join(OPTIONS.input_tmp, "system"), img.name]) diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files index 4aaad37..f129da1 100755 --- a/tools/releasetools/ota_from_target_files +++ b/tools/releasetools/ota_from_target_files @@ -57,11 +57,13 @@ if sys.hexversion < 0x02040000: sys.exit(1) import copy +import errno import os import re import sha import subprocess import tempfile +import threading import time import zipfile @@ -80,6 +82,7 @@ OPTIONS.wipe_user_data = False OPTIONS.omit_prereq = False OPTIONS.extra_script = None OPTIONS.script_mode = 'auto' +OPTIONS.worker_threads = 3 def MostPopularKey(d, default): """Given a dict, return the key corresponding to the largest @@ -273,19 +276,14 @@ def SignOutput(temp_zip_name, output_zip_name): key_passwords = common.GetKeyPasswords([OPTIONS.package_key]) pw = key_passwords[OPTIONS.package_key] - common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw) + common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw, + whole_file=True) def AppendAssertions(script, input_zip): device = GetBuildProp("ro.product.device", input_zip) script.AssertDevice(device) - info = input_zip.read("OTA/android-info.txt") - m = re.search(r"require\s+version-bootloader\s*=\s*(\S+)", info) - if m: - bootloaders = m.group(1).split("|") - script.AssertSomeBootloader(*bootloaders) - def MakeRecoveryPatch(output_zip, recovery_img, boot_img): """Generate a binary patch that creates the recovery image starting @@ -302,8 +300,9 @@ def MakeRecoveryPatch(output_zip, recovery_img, boot_img): executable. """ - patch = Difference(recovery_img, boot_img, "imgdiff") - common.ZipWriteStr(output_zip, "system/recovery-from-boot.p", patch) + d = Difference(recovery_img, boot_img) + _, _, patch = d.ComputePatch() + common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch) Item.Get("system/recovery-from-boot.p", dir=False) # Images with different content will have a different first page, so @@ -324,7 +323,7 @@ fi 'header_sha1': header_sha1, 'recovery_size': recovery_img.size, 'recovery_sha1': recovery_img.sha1 } - common.ZipWriteStr(output_zip, "system/etc/install-recovery.sh", sh) + common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh) return Item.Get("system/etc/install-recovery.sh", dir=False) @@ -339,19 +338,18 @@ def WriteFullOTAPackage(input_zip, output_zip): # change very often. script = edify_generator.EdifyGenerator(2) + device_specific = common.DeviceSpecificParams( + input_zip=input_zip, + output_zip=output_zip, + script=script, + input_tmp=OPTIONS.input_tmp) + if not OPTIONS.omit_prereq: ts = GetBuildProp("ro.build.date.utc", input_zip) script.AssertOlderBuild(ts) AppendAssertions(script, input_zip) - - script.ShowProgress(0.1, 0) - - try: - common.ZipWriteStr(output_zip, "radio.img", input_zip.read("RADIO/image")) - script.WriteFirmwareImage("radio", "radio.img") - except KeyError: - print "warning: no radio image in input target_files; not flashing radio" + device_specific.FullOTA_Assertions() script.ShowProgress(0.5, 0) @@ -360,6 +358,7 @@ def WriteFullOTAPackage(input_zip, output_zip): script.FormatPartition("system") script.Mount("MTD", "system", "/system") + script.UnpackPackageDir("recovery", "/system") script.UnpackPackageDir("system", "/system") symlinks = CopySystemFiles(input_zip, output_zip) @@ -385,8 +384,11 @@ def WriteFullOTAPackage(input_zip, output_zip): common.ZipWriteStr(output_zip, "boot.img", boot_img.data) script.ShowProgress(0.2, 0) - script.WriteRawImage("boot", "boot.img") script.ShowProgress(0.2, 10) + script.WriteRawImage("boot", "boot.img") + + script.ShowProgress(0.1, 0) + device_specific.FullOTA_InstallEnd() if OPTIONS.extra_script is not None: script.AppendExtra(OPTIONS.extra_script) @@ -423,38 +425,111 @@ def LoadSystemFiles(z): return out -def Difference(tf, sf, diff_program): - """Return the patch (as a string of data) needed to turn sf into tf. - diff_program is the name of an external program (or list, if - additional arguments are desired) to run to generate the diff. - """ +DIFF_PROGRAM_BY_EXT = { + ".gz" : "imgdiff", + ".zip" : ["imgdiff", "-z"], + ".jar" : ["imgdiff", "-z"], + ".apk" : ["imgdiff", "-z"], + ".img" : "imgdiff", + } - ttemp = tf.WriteToTemp() - stemp = sf.WriteToTemp() - ext = os.path.splitext(tf.name)[1] +class Difference(object): + def __init__(self, tf, sf): + self.tf = tf + self.sf = sf + self.patch = None - try: - ptemp = tempfile.NamedTemporaryFile() - if isinstance(diff_program, list): - cmd = copy.copy(diff_program) - else: - cmd = [diff_program] - cmd.append(stemp.name) - cmd.append(ttemp.name) - cmd.append(ptemp.name) - p = common.Run(cmd) - _, err = p.communicate() - if err or p.returncode != 0: - print "WARNING: failure running %s:\n%s\n" % (diff_program, err) - return None - diff = ptemp.read() - finally: - ptemp.close() - stemp.close() - ttemp.close() - - return diff + def ComputePatch(self): + """Compute the patch (as a string of data) needed to turn sf into + tf. Returns the same tuple as GetPatch().""" + + tf = self.tf + sf = self.sf + + ext = os.path.splitext(tf.name)[1] + diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff") + + ttemp = tf.WriteToTemp() + stemp = sf.WriteToTemp() + + ext = os.path.splitext(tf.name)[1] + + try: + ptemp = tempfile.NamedTemporaryFile() + if isinstance(diff_program, list): + cmd = copy.copy(diff_program) + else: + cmd = [diff_program] + cmd.append(stemp.name) + cmd.append(ttemp.name) + cmd.append(ptemp.name) + p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + _, err = p.communicate() + if err or p.returncode != 0: + print "WARNING: failure running %s:\n%s\n" % (diff_program, err) + return None + diff = ptemp.read() + finally: + ptemp.close() + stemp.close() + ttemp.close() + + self.patch = diff + return self.tf, self.sf, self.patch + + + def GetPatch(self): + """Return a tuple (target_file, source_file, patch_data). + patch_data may be None if ComputePatch hasn't been called, or if + computing the patch failed.""" + return self.tf, self.sf, self.patch + + +def ComputeDifferences(diffs): + """Call ComputePatch on all the Difference objects in 'diffs'.""" + print len(diffs), "diffs to compute" + + # Do the largest files first, to try and reduce the long-pole effect. + by_size = [(i.tf.size, i) for i in diffs] + by_size.sort(reverse=True) + by_size = [i[1] for i in by_size] + + lock = threading.Lock() + diff_iter = iter(by_size) # accessed under lock + + def worker(): + try: + lock.acquire() + for d in diff_iter: + lock.release() + start = time.time() + d.ComputePatch() + dur = time.time() - start + lock.acquire() + + tf, sf, patch = d.GetPatch() + if sf.name == tf.name: + name = tf.name + else: + name = "%s (%s)" % (tf.name, sf.name) + if patch is None: + print "patching failed! %s" % (name,) + else: + print "%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % ( + dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name) + lock.release() + except Exception, e: + print e + raise + + # start worker threads; wait for them all to finish. + threads = [threading.Thread(target=worker) + for i in range(OPTIONS.worker_threads)] + for th in threads: + th.start() + while threads: + threads.pop().join() def GetBuildProp(property, z): @@ -484,6 +559,7 @@ def GetRecoveryAPIVersion(zip): except KeyError: return 0 + def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): source_version = GetRecoveryAPIVersion(source_zip) @@ -502,6 +578,12 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): else: raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,)) + device_specific = common.DeviceSpecificParams( + source_zip=source_zip, + target_zip=target_zip, + output_zip=output_zip, + script=script) + print "Loading target..." target_data = LoadSystemFiles(target_zip) print "Loading source..." @@ -509,9 +591,11 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): verbatim_targets = [] patch_list = [] + diffs = [] largest_source_size = 0 for fn in sorted(target_data.keys()): tf = target_data[fn] + assert fn == tf.name sf = source_data.get(fn, None) if sf is None or fn in OPTIONS.require_verbatim: @@ -523,26 +607,23 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): verbatim_targets.append((fn, tf.size)) elif tf.sha1 != sf.sha1: # File is different; consider sending as a patch - diff_method = "bsdiff" - if tf.name.endswith(".gz"): - diff_method = "imgdiff" - d = Difference(tf, sf, diff_method) - if d is not None: - print fn, tf.size, len(d), (float(len(d)) / tf.size) - if d is None or len(d) > tf.size * OPTIONS.patch_threshold: - # patch is almost as big as the file; don't bother patching - tf.AddToZip(output_zip) - verbatim_targets.append((fn, tf.size)) - else: - common.ZipWriteStr(output_zip, "patch/" + fn + ".p", d) - patch_list.append((fn, tf, sf, tf.size)) - largest_source_size = max(largest_source_size, sf.size) + diffs.append(Difference(tf, sf)) else: # Target file identical to source. pass - total_verbatim_size = sum([i[1] for i in verbatim_targets]) - total_patched_size = sum([i[3] for i in patch_list]) + ComputeDifferences(diffs) + + for diff in diffs: + tf, sf, d = diff.GetPatch() + if d is None or len(d) > tf.size * OPTIONS.patch_threshold: + # patch is almost as big as the file; don't bother patching + tf.AddToZip(output_zip) + verbatim_targets.append((tf.name, tf.size)) + else: + common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d) + patch_list.append((tf.name, tf, sf, tf.size)) + largest_source_size = max(largest_source_size, sf.size) source_fp = GetBuildProp("ro.build.fingerprint", source_zip) target_fp = GetBuildProp("ro.build.fingerprint", target_zip) @@ -566,35 +647,31 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): os.path.join(OPTIONS.target_tmp, "RECOVERY"))) updating_recovery = (source_recovery.data != target_recovery.data) - source_radio = source_zip.read("RADIO/image") - target_radio = target_zip.read("RADIO/image") - updating_radio = (source_radio != target_radio) - - # The last 0.1 is reserved for creating symlinks, fixing - # permissions, and writing the boot image (if necessary). - progress_bar_total = 1.0 - if updating_boot: - progress_bar_total -= 0.1 - if updating_radio: - progress_bar_total -= 0.3 + # Here's how we divide up the progress bar: + # 0.1 for verifying the start state (PatchCheck calls) + # 0.8 for applying patches (ApplyPatch calls) + # 0.1 for unpacking verbatim files, symlinking, and doing the + # device-specific commands. AppendAssertions(script, target_zip) + device_specific.IncrementalOTA_Assertions() script.Print("Verifying current system...") - pb_verify = progress_bar_total * 0.3 * \ - (total_patched_size / - float(total_patched_size+total_verbatim_size+1)) - - for i, (fn, tf, sf, size) in enumerate(patch_list): - if i % 5 == 0: - next_sizes = sum([i[3] for i in patch_list[i:i+5]]) - script.ShowProgress(next_sizes * pb_verify / (total_patched_size+1), 1) + script.ShowProgress(0.1, 0) + total_verify_size = float(sum([i[2].size for i in patch_list]) + 1) + if updating_boot: + total_verify_size += source_boot.size + so_far = 0 + for fn, tf, sf, size in patch_list: script.PatchCheck("/"+fn, tf.sha1, sf.sha1) + so_far += sf.size + script.SetProgress(so_far / total_verify_size) if updating_boot: - d = Difference(target_boot, source_boot, "imgdiff") + d = Difference(target_boot, source_boot) + _, _, d = d.ComputePatch() print "boot target: %d source: %d diff: %d" % ( target_boot.size, source_boot.size, len(d)) @@ -603,12 +680,16 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): script.PatchCheck("MTD:boot:%d:%s:%d:%s" % (source_boot.size, source_boot.sha1, target_boot.size, target_boot.sha1)) + so_far += source_boot.size + script.SetProgress(so_far / total_verify_size) if patch_list or updating_recovery or updating_boot: script.CacheFreeSpaceCheck(largest_source_size) script.Print("Unpacking patches...") script.UnpackPackageDir("patch", "/tmp/patchtmp") + device_specific.IncrementalOTA_VerifyEnd() + script.Comment("---- start making changes here ----") if OPTIONS.wipe_user_data: @@ -621,6 +702,19 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): if i not in target_data] + ["/system/recovery.img"]) + script.ShowProgress(0.8, 0) + total_patch_size = float(sum([i[1].size for i in patch_list]) + 1) + if updating_boot: + total_patch_size += target_boot.size + so_far = 0 + + script.Print("Patching system files...") + for fn, tf, sf, size in patch_list: + script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, + sf.sha1, "/tmp/patchtmp/"+fn+".p") + so_far += tf.size + script.SetProgress(so_far / total_patch_size) + if updating_boot: # Produce the boot image by applying a patch to the current # contents of the boot partition, and write it back to the @@ -632,6 +726,8 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): "-", target_boot.size, target_boot.sha1, source_boot.sha1, "/tmp/patchtmp/boot.img.p") + so_far += target_boot.size + script.SetProgress(so_far / total_patch_size) print "boot image changed; including." else: print "boot image unchanged; skipping." @@ -650,29 +746,12 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): # as fodder for constructing the recovery image. recovery_sh_item = MakeRecoveryPatch(output_zip, target_recovery, target_boot) + script.UnpackPackageDir("recovery", "/system") print "recovery image changed; including as patch from boot." else: print "recovery image unchanged; skipping." - if updating_radio: - script.ShowProgress(0.3, 10) - script.Print("Writing radio image...") - script.WriteFirmwareImage("radio", "radio.img") - common.ZipWriteStr(output_zip, "radio.img", target_radio) - print "radio image changed; including." - else: - print "radio image unchanged; skipping." - - script.Print("Patching system files...") - pb_apply = progress_bar_total * 0.7 * \ - (total_patched_size / - float(total_patched_size+total_verbatim_size+1)) - for i, (fn, tf, sf, size) in enumerate(patch_list): - if i % 5 == 0: - next_sizes = sum([i[3] for i in patch_list[i:i+5]]) - script.ShowProgress(next_sizes * pb_apply / (total_patched_size+1), 1) - script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, - sf.sha1, "/tmp/patchtmp/"+fn+".p") + script.ShowProgress(0.1, 10) target_symlinks = CopySystemFiles(target_zip, None) @@ -700,14 +779,10 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): script.DeleteFiles(to_delete) if verbatim_targets: - pb_verbatim = progress_bar_total * \ - (total_verbatim_size / - float(total_patched_size+total_verbatim_size+1)) - script.ShowProgress(pb_verbatim, 5) script.Print("Unpacking new files...") script.UnpackPackageDir("system", "/system") - script.Print("Finishing up...") + script.Print("Symlinks and permissions...") # Create all the symlinks that don't already exist, or point to # somewhere different than what we want. Delete each symlink before @@ -726,6 +801,9 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): # permissions. script.AppendScript(temp_script) + # Do device-specific installation (eg, write radio image). + device_specific.IncrementalOTA_InstallEnd() + if OPTIONS.extra_script is not None: scirpt.AppendExtra(OPTIONS.extra_script) @@ -749,6 +827,8 @@ def main(argv): OPTIONS.extra_script = a elif o in ("-m", "--script_mode"): OPTIONS.script_mode = a + elif o in ("--worker_threads"): + OPTIONS.worker_threads = int(a) else: return False return True @@ -761,7 +841,8 @@ def main(argv): "wipe_user_data", "no_prereq", "extra_script=", - "script_mode="], + "script_mode=", + "worker_threads="], extra_option_handler=option_handler) if len(args) != 2: @@ -777,6 +858,23 @@ def main(argv): print "unzipping target target-files..." OPTIONS.input_tmp = common.UnzipTemp(args[0]) + if OPTIONS.device_specific is None: + # look for the device-specific tools extension location in the input + try: + f = open(os.path.join(OPTIONS.input_tmp, "META", "tool-extensions.txt")) + ds = f.read().strip() + f.close() + if ds: + ds = os.path.normpath(ds) + print "using device-specific extensions in", ds + OPTIONS.device_specific = ds + except IOError, e: + if e.errno == errno.ENOENT: + # nothing specified in the file + pass + else: + raise + common.LoadMaxSizes() if not OPTIONS.max_image_size: print diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks index 158845c..9d296d8 100755 --- a/tools/releasetools/sign_target_files_apks +++ b/tools/releasetools/sign_target_files_apks @@ -20,10 +20,6 @@ target-files zip. Usage: sign_target_files_apks [flags] input_target_files output_target_files - -s (--signapk_jar) <path> - Path of the signapks.jar file used to sign an individual APK - file. - -e (--extra_apks) <name,name,...=key> Add extra APK name/key pairs as though they appeared in apkcerts.txt (so mappings specified by -k and -d are applied). @@ -59,7 +55,7 @@ Usage: sign_target_files_apks [flags] input_target_files output_target_files the last component of the build fingerprint). Prefix each with '+' or '-' to indicate whether that tag should be added or removed. Changes are processed in the order they appear. - Default value is "-test-keys,+ota-rel-keys,+release-keys". + Default value is "-test-keys,+release-keys". """ @@ -84,7 +80,7 @@ OPTIONS = common.OPTIONS OPTIONS.extra_apks = {} OPTIONS.key_map = {} OPTIONS.replace_ota_keys = False -OPTIONS.tag_changes = ("-test-keys", "+ota-rel-keys", "+release-keys") +OPTIONS.tag_changes = ("-test-keys", "+release-keys") def GetApkCerts(tf_zip): certmap = {} @@ -307,9 +303,7 @@ def ReplaceOtaKeys(input_tf_zip, output_tf_zip): def main(argv): def option_handler(o, a): - if o in ("-s", "--signapk_jar"): - OPTIONS.signapk_jar = a - elif o in ("-e", "--extra_apks"): + if o in ("-e", "--extra_apks"): names, key = a.split("=") names = names.split(",") for n in names: @@ -339,9 +333,8 @@ def main(argv): return True args = common.ParseOptions(argv, __doc__, - extra_opts="s:e:d:k:ot:", - extra_long_opts=["signapk_jar=", - "extra_apks=", + extra_opts="e:d:k:ot:", + extra_long_opts=["extra_apks=", "default_key_mappings=", "key_mapping=", "replace_ota_keys", diff --git a/tools/signapk/SignApk.java b/tools/signapk/SignApk.java index caf7935..3244a49 100644 --- a/tools/signapk/SignApk.java +++ b/tools/signapk/SignApk.java @@ -247,7 +247,7 @@ class SignApk { } } - /** Write a .SF file with a digest the specified manifest. */ + /** Write a .SF file with a digest of the specified manifest. */ private static void writeSignatureFile(Manifest manifest, OutputStream out) throws IOException, GeneralSecurityException { Manifest sf = new Manifest(); @@ -304,6 +304,75 @@ class SignApk { pkcs7.encodeSignedData(out); } + private static void signWholeOutputFile(byte[] zipData, + OutputStream outputStream, + X509Certificate publicKey, + PrivateKey privateKey) + throws IOException, GeneralSecurityException { + + // For a zip with no archive comment, the + // end-of-central-directory record will be 22 bytes long, so + // we expect to find the EOCD marker 22 bytes from the end. + if (zipData[zipData.length-22] != 0x50 || + zipData[zipData.length-21] != 0x4b || + zipData[zipData.length-20] != 0x05 || + zipData[zipData.length-19] != 0x06) { + throw new IllegalArgumentException("zip data already has an archive comment"); + } + + Signature signature = Signature.getInstance("SHA1withRSA"); + signature.initSign(privateKey); + signature.update(zipData, 0, zipData.length-2); + + ByteArrayOutputStream temp = new ByteArrayOutputStream(); + + // put a readable message and a null char at the start of the + // archive comment, so that tools that display the comment + // (hopefully) show something sensible. + // TODO: anything more useful we can put in this message? + byte[] message = "signed by SignApk".getBytes("UTF-8"); + temp.write(message); + temp.write(0); + writeSignatureBlock(signature, publicKey, temp); + int total_size = temp.size() + 6; + if (total_size > 0xffff) { + throw new IllegalArgumentException("signature is too big for ZIP file comment"); + } + // signature starts this many bytes from the end of the file + int signature_start = total_size - message.length - 1; + temp.write(signature_start & 0xff); + temp.write((signature_start >> 8) & 0xff); + // Why the 0xff bytes? In a zip file with no archive comment, + // bytes [-6:-2] of the file are the little-endian offset from + // the start of the file to the central directory. So for the + // two high bytes to be 0xff 0xff, the archive would have to + // be nearly 4GB in side. So it's unlikely that a real + // commentless archive would have 0xffs here, and lets us tell + // an old signed archive from a new one. + temp.write(0xff); + temp.write(0xff); + temp.write(total_size & 0xff); + temp.write((total_size >> 8) & 0xff); + temp.flush(); + + // Signature verification checks that the EOCD header is the + // last such sequence in the file (to avoid minzip finding a + // fake EOCD appended after the signature in its scan). The + // odds of producing this sequence by chance are very low, but + // let's catch it here if it does. + byte[] b = temp.toByteArray(); + for (int i = 0; i < b.length-3; ++i) { + if (b[i] == 0x50 && b[i+1] == 0x4b && b[i+2] == 0x05 && b[i+3] == 0x06) { + throw new IllegalArgumentException("found spurious EOCD header at " + i); + } + } + + outputStream.write(zipData, 0, zipData.length-2); + outputStream.write(total_size & 0xff); + outputStream.write((total_size >> 8) & 0xff); + temp.writeTo(outputStream); + } + /** * Copy all the files in a manifest from input to output. We set * the modification times in the output to a fixed time, so as to @@ -340,25 +409,40 @@ class SignApk { } public static void main(String[] args) { - if (args.length != 4) { - System.err.println("Usage: signapk " + + if (args.length != 4 && args.length != 5) { + System.err.println("Usage: signapk [-w] " + "publickey.x509[.pem] privatekey.pk8 " + "input.jar output.jar"); System.exit(2); } + boolean signWholeFile = false; + int argstart = 0; + if (args[0].equals("-w")) { + signWholeFile = true; + argstart = 1; + } + JarFile inputJar = null; JarOutputStream outputJar = null; + FileOutputStream outputFile = null; try { - X509Certificate publicKey = readPublicKey(new File(args[0])); + X509Certificate publicKey = readPublicKey(new File(args[argstart+0])); // Assume the certificate is valid for at least an hour. long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000; - PrivateKey privateKey = readPrivateKey(new File(args[1])); - inputJar = new JarFile(new File(args[2]), false); // Don't verify. - outputJar = new JarOutputStream(new FileOutputStream(args[3])); + PrivateKey privateKey = readPrivateKey(new File(args[argstart+1])); + inputJar = new JarFile(new File(args[argstart+2]), false); // Don't verify. + + OutputStream outputStream = null; + if (signWholeFile) { + outputStream = new ByteArrayOutputStream(); + } else { + outputStream = outputFile = new FileOutputStream(args[argstart+3]); + } + outputJar = new JarOutputStream(outputStream); outputJar.setLevel(9); JarEntry je; @@ -387,13 +471,23 @@ class SignApk { // Everything else copyFiles(manifest, inputJar, outputJar, timestamp); + + outputJar.close(); + outputJar = null; + outputStream.flush(); + + if (signWholeFile) { + outputFile = new FileOutputStream(args[argstart+3]); + signWholeOutputFile(((ByteArrayOutputStream)outputStream).toByteArray(), + outputFile, publicKey, privateKey); + } } catch (Exception e) { e.printStackTrace(); System.exit(1); } finally { try { if (inputJar != null) inputJar.close(); - if (outputJar != null) outputJar.close(); + if (outputFile != null) outputFile.close(); } catch (IOException e) { e.printStackTrace(); System.exit(1); diff --git a/tools/warn.py b/tools/warn.py new file mode 100755 index 0000000..6fea20a --- /dev/null +++ b/tools/warn.py @@ -0,0 +1,523 @@ +#!/usr/bin/env python + +import sys +import re + +if len(sys.argv) == 1: + print 'usage: ' + sys.argv[0] + ' <build.log>' + sys.exit() + +# if you add another level, don't forget to give it a color below +class severity: + UNKNOWN=0 + SKIP=100 + FIXMENOW=1 + HIGH=2 + MEDIUM=3 + LOW=4 + HARMLESS=5 + +def colorforseverity(sev): + if sev == severity.FIXMENOW: + return 'fuchsia' + if sev == severity.HIGH: + return 'red' + if sev == severity.MEDIUM: + return 'orange' + if sev == severity.LOW: + return 'yellow' + if sev == severity.HARMLESS: + return 'limegreen' + if sev == severity.UNKNOWN: + return 'blue' + return 'grey' + +warnpatterns = [ + { 'category':'make', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'make: overriding commands/ignoring old commands', + 'patterns':[r".*: warning: overriding commands for target .+", + r".*: warning: ignoring old commands for target .+"] }, + { 'category':'C/C++', 'severity':severity.HIGH, 'members':[], 'option':'-Wimplicit-function-declaration', + 'description':'Implicit function declaration', + 'patterns':[r".*: warning: implicit declaration of function .+"] }, + { 'category':'C/C++', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: conflicting types for '.+'"] }, + { 'category':'C/C++', 'severity':severity.HIGH, 'members':[], 'option':'-Wtype-limits', + 'description':'Expression always evaluates to true or false', + 'patterns':[r".*: warning: comparison is always false due to limited range of data type", + r".*: warning: comparison of unsigned expression >= 0 is always true", + r".*: warning: comparison of unsigned expression < 0 is always false"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Incompatible pointer types', + 'patterns':[r".*: warning: assignment from incompatible pointer type", + r".*: warning: passing argument [0-9]+ of '.*' from incompatible pointer type", + r".*: warning: initialization from incompatible pointer type"] }, + { 'category':'C/C++', 'severity':severity.HIGH, 'members':[], 'option':'-fno-builtin', + 'description':'Incompatible declaration of built in function', + 'patterns':[r".*: warning: incompatible implicit declaration of built-in function .+"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wunused-parameter', + 'description':'Unused parameter', + 'patterns':[r".*: warning: unused parameter '.*'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wunused', + 'description':'Unused function, variable or label', + 'patterns':[r".*: warning: '.+' defined but not used"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wunused-value', + 'description':'Statement with no effect', + 'patterns':[r".*: warning: statement with no effect"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wmissing-field-initializers', + 'description':'Missing initializer', + 'patterns':[r".*: warning: missing initializer"] }, + { 'category':'cont.', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: \(near initialization for '.+'\)"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wformat', + 'description':'Format string does not match arguments', + 'patterns':[r".*: warning: format '.+' expects type '.+', but argument [0-9]+ has type '.+'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wformat-extra-args', + 'description':'Too many arguments for format string', + 'patterns':[r".*: warning: too many arguments for format"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wsign-compare', + 'description':'Comparison between signed and unsigned', + 'patterns':[r".*: warning: comparison between signed and unsigned", + r".*: warning: comparison of promoted \~unsigned with unsigned", + r".*: warning: signed and unsigned type in conditional expression"] }, + { 'category':'libpng', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'libpng: zero area', + 'patterns':[r".*libpng warning: Ignoring attempt to set cHRM RGB triangle with zero area"] }, + { 'category':'aapt', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'aapt: no comment for public symbol', + 'patterns':[r".*: warning: No comment for public symbol .+"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wmissing-braces', + 'description':'Missing braces around initializer', + 'patterns':[r".*: warning: missing braces around initializer.*"] }, + { 'category':'C/C++', 'severity':severity.HARMLESS, 'members':[], 'option':'', + 'description':'No newline at end of file', + 'patterns':[r".*: warning: no newline at end of file"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wcast-qual', + 'description':'Qualifier discarded', + 'patterns':[r".*: warning: passing argument [0-9]+ of '.+' discards qualifiers from pointer target type", + r".*: warning: assignment discards qualifiers from pointer target type", + r".*: warning: return discards qualifiers from pointer target type"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wattributes', + 'description':'Attribute ignored', + 'patterns':[r".*: warning: '_*packed_*' attribute ignored"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wattributes', + 'description':'Visibility mismatch', + 'patterns':[r".*: warning: '.+' declared with greater visibility than the type of its field '.+'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Shift count greater than width of type', + 'patterns':[r".*: warning: (left|right) shift count >= width of type"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'extern <foo> is initialized', + 'patterns':[r".*: warning: '.+' initialized and declared 'extern'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wold-style-declaration', + 'description':'Old style declaration', + 'patterns':[r".*: warning: 'static' is not at beginning of declaration"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wuninitialized', + 'description':'Variable may be used uninitialized', + 'patterns':[r".*: warning: '.+' may be used uninitialized in this function"] }, + { 'category':'C/C++', 'severity':severity.HIGH, 'members':[], 'option':'-Wuninitialized', + 'description':'Variable is used uninitialized', + 'patterns':[r".*: warning: '.+' is used uninitialized in this function"] }, + { 'category':'ld', 'severity':severity.MEDIUM, 'members':[], 'option':'-fshort-enums', + 'description':'ld: possible enum size mismatch', + 'patterns':[r".*: warning: .* uses variable-size enums yet the output is to use 32-bit enums; use of enum values across objects may fail"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wpointer-sign', + 'description':'Pointer targets differ in signedness', + 'patterns':[r".*: warning: pointer targets in initialization differ in signedness", + r".*: warning: pointer targets in assignment differ in signedness", + r".*: warning: pointer targets in return differ in signedness", + r".*: warning: pointer targets in passing argument [0-9]+ of '.+' differ in signedness"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wstrict-overflow', + 'description':'Assuming overflow does not occur', + 'patterns':[r".*: warning: assuming signed overflow does not occur when assuming that .* is always (true|false)"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wempty-body', + 'description':'Suggest adding braces around empty body', + 'patterns':[r".*: warning: suggest braces around empty body in an 'if' statement", + r".*: warning: empty body in an if-statement", + r".*: warning: suggest braces around empty body in an 'else' statement", + r".*: warning: empty body in an else-statement"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wparentheses', + 'description':'Suggest adding parentheses', + 'patterns':[r".*: warning: suggest explicit braces to avoid ambiguous 'else'", + r".*: warning: suggest parentheses around arithmetic in operand of '.+'", + r".*: warning: suggest parentheses around comparison in operand of '.+'", + r".*: warning: suggest parentheses around '.+?' .+ '.+?'", + r".*: warning: suggest parentheses around assignment used as truth value"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Static variable used in non-static inline function', + 'patterns':[r".*: warning: '.+' is static but used in inline function '.+' which is not static"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wimplicit int', + 'description':'No type or storage class (will default to int)', + 'patterns':[r".*: warning: data definition has no type or storage class"] }, + { 'category':'cont.', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: type defaults to 'int' in declaration of '.+'"] }, + { 'category':'cont.', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: parameter names \(without types\) in function declaration"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wstrict-aliasing', + 'description':'Dereferencing <foo> breaks strict aliasing rules', + 'patterns':[r".*: warning: dereferencing .* break strict-aliasing rules"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wpointer-to-int-cast', + 'description':'Cast from pointer to integer of different size', + 'patterns':[r".*: warning: cast from pointer to integer of different size"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wint-to-pointer-cast', + 'description':'Cast to pointer from integer of different size', + 'patterns':[r".*: warning: cast to pointer from integer of different size"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Symbol redefined', + 'patterns':[r".*: warning: "".+"" redefined"] }, + { 'category':'cont.', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: this is the location of the previous definition"] }, + { 'category':'ld', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'ld: type and size of dynamic symbol are not defined', + 'patterns':[r".*: warning: type and size of dynamic symbol `.+' are not defined"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Pointer from integer without cast', + 'patterns':[r".*: warning: assignment makes pointer from integer without a cast"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Pointer from integer without cast', + 'patterns':[r".*: warning: passing argument [0-9]+ of '.+' makes pointer from integer without a cast"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Integer from pointer without cast', + 'patterns':[r".*: warning: assignment makes integer from pointer without a cast"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Integer from pointer without cast', + 'patterns':[r".*: warning: passing argument [0-9]+ of '.+' makes integer from pointer without a cast"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Integer from pointer without cast', + 'patterns':[r".*: warning: return makes integer from pointer without a cast"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wunknown-pragmas', + 'description':'Ignoring pragma', + 'patterns':[r".*: warning: ignoring #pragma .+"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wclobbered', + 'description':'Variable might be clobbered by longjmp or vfork', + 'patterns':[r".*: warning: variable '.+' might be clobbered by 'longjmp' or 'vfork'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wclobbered', + 'description':'Argument might be clobbered by longjmp or vfork', + 'patterns':[r".*: warning: argument '.+' might be clobbered by 'longjmp' or 'vfork'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wredundant-decls', + 'description':'Redundant declaration', + 'patterns':[r".*: warning: redundant redeclaration of '.+'"] }, + { 'category':'cont.', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: previous declaration of '.+' was here"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wswitch-enum', + 'description':'Enum value not handled in switch', + 'patterns':[r".*: warning: enumeration value '.+' not handled in switch"] }, + { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'-encoding', + 'description':'Java: Non-ascii characters used, but ascii encoding specified', + 'patterns':[r".*: warning: unmappable character for encoding ascii"] }, + { 'category':'java', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Java: Non-varargs call of varargs method with inexact argument type for last parameter', + 'patterns':[r".*: warning: non-varargs call of varargs method with inexact argument type for last parameter"] }, + { 'category':'aapt', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'aapt: String marked untranslatable, but translation exists', + 'patterns':[r".*: warning: string '.+' in .* marked untranslatable but exists in locale '??_??'"] }, + { 'category':'aapt', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'aapt: empty span in string', + 'patterns':[r".*: warning: empty '.+' span found in text '.+"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Taking address of temporary', + 'patterns':[r".*: warning: taking address of temporary"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Possible broken line continuation', + 'patterns':[r".*: warning: backslash and newline separated by space"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Warray-bounds', + 'description':'Array subscript out of bounds', + 'patterns':[r".*: warning: array subscript is above array bounds"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Decimal constant is unsigned only in ISO C90', + 'patterns':[r".*: warning: this decimal constant is unsigned only in ISO C90"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wmain', + 'description':'main is usually a function', + 'patterns':[r".*: warning: 'main' is usually a function"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Typedef ignored', + 'patterns':[r".*: warning: 'typedef' was ignored in this declaration"] }, + { 'category':'C/C++', 'severity':severity.HIGH, 'members':[], 'option':'-Waddress', + 'description':'Address always evaluates to true', + 'patterns':[r".*: warning: the address of '.+' will always evaluate as 'true'"] }, + { 'category':'C/C++', 'severity':severity.FIXMENOW, 'members':[], 'option':'', + 'description':'Freeing a non-heap object', + 'patterns':[r".*: warning: attempt to free a non-heap object '.+'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wchar-subscripts', + 'description':'Array subscript has type char', + 'patterns':[r".*: warning: array subscript has type 'char'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Constant too large for type', + 'patterns':[r".*: warning: integer constant is too large for '.+' type"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Woverflow', + 'description':'Constant too large for type, truncated', + 'patterns':[r".*: warning: large integer implicitly truncated to unsigned type"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Woverflow', + 'description':'Overflow in implicit constant conversion', + 'patterns':[r".*: warning: overflow in implicit constant conversion"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Declaration does not declare anything', + 'patterns':[r".*: warning: declaration 'class .+' does not declare anything"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wreorder', + 'description':'Initialization order will be different', + 'patterns':[r".*: warning: '.+' will be initialized after"] }, + { 'category':'cont.', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: '.+'"] }, + { 'category':'cont.', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: when initialized here"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wmissing-parameter-type', + 'description':'Parameter type not specified', + 'patterns':[r".*: warning: type of '.+' defaults to 'int'"] }, + { 'category':'gcc', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Invalid option for C file', + 'patterns':[r".*: warning: command line option "".+"" is valid for C\+\+\/ObjC\+\+ but not for C"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'User warning', + 'patterns':[r".*: warning: #warning "".+"""] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wextra', + 'description':'Dereferencing void*', + 'patterns':[r".*: warning: dereferencing 'void \*' pointer"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wextra', + 'description':'Comparison of pointer to zero', + 'patterns':[r".*: warning: ordered comparison of pointer with integer zero"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wwrite-strings', + 'description':'Conversion of string constant to non-const char*', + 'patterns':[r".*: warning: deprecated conversion from string constant to '.+'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wstrict-prototypes', + 'description':'Function declaration isn''t a prototype', + 'patterns':[r".*: warning: function declaration isn't a prototype"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wignored-qualifiers', + 'description':'Type qualifiers ignored on function return value', + 'patterns':[r".*: warning: type qualifiers ignored on function return type"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'<foo> declared inside parameter list, scope limited to this definition', + 'patterns':[r".*: warning: '.+' declared inside parameter list"] }, + { 'category':'cont.', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: its scope is only this definition or declaration, which is probably not what you want"] }, + { 'category':'C/C++', 'severity':severity.LOW, 'members':[], 'option':'-Wcomment', + 'description':'Line continuation inside comment', + 'patterns':[r".*: warning: multi-line comment"] }, + { 'category':'C/C++', 'severity':severity.HARMLESS, 'members':[], 'option':'', + 'description':'Extra tokens after #endif', + 'patterns':[r".*: warning: extra tokens at end of #endif directive"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wenum-compare', + 'description':'Comparison between different enums', + 'patterns':[r".*: warning: comparison between 'enum .+' and 'enum .+'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wconversion', + 'description':'Implicit conversion of negative number to unsigned type', + 'patterns':[r".*: warning: converting negative value '.+' to '.+'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Passing NULL as non-pointer argument', + 'patterns':[r".*: warning: passing NULL to non-pointer argument [0-9]+ of '.+'"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wctor-dtor-privacy', + 'description':'Class seems unusable because of private ctor/dtor' , + 'patterns':[r".*: warning: all member functions in class '.+' are private"] }, + # skip this next one, because it only points out some RefBase-based classes where having a private destructor is perfectly fine + { 'category':'C/C++', 'severity':severity.SKIP, 'members':[], 'option':'-Wctor-dtor-privacy', + 'description':'Class seems unusable because of private ctor/dtor' , + 'patterns':[r".*: warning: 'class .+' only defines a private destructor and has no friends"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wctor-dtor-privacy', + 'description':'Class seems unusable because of private ctor/dtor' , + 'patterns':[r".*: warning: 'class .+' only defines private constructors and has no friends"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wpointer-arith', + 'description':'void* used in arithmetic' , + 'patterns':[r".*: warning: pointer of type 'void \*' used in (arithmetic|subtraction)", + r".*: warning: wrong type argument to increment"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'-Wsign-promo', + 'description':'Overload resolution chose to promote from unsigned or enum to signed type' , + 'patterns':[r".*: warning: passing '.+' chooses 'int' over '.* int'"] }, + { 'category':'cont.', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: in call to '.+'"] }, + { 'category':'C/C++', 'severity':severity.HIGH, 'members':[], 'option':'-Wextra', + 'description':'Base should be explicitly initialized in copy constructor', + 'patterns':[r".*: warning: base class '.+' should be explicitly initialized in the copy constructor"] }, + { 'category':'C/C++', 'severity':severity.MEDIUM, 'members':[], 'option':'', + 'description':'Converting from <type> to <other type>', + 'patterns':[r".*: warning: converting to '.+' from '.+'"] }, + + # these next ones are to deal with formatting problems resulting from the log being mixed up by 'make -j' + { 'category':'C/C++', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: ,$"] }, + { 'category':'C/C++', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: $"] }, + { 'category':'C/C++', 'severity':severity.SKIP, 'members':[], 'option':'', + 'description':'', + 'patterns':[r".*: warning: In file included from .+,"] }, + + # catch-all for warnings this script doesn't know about yet + { 'category':'C/C++', 'severity':severity.UNKNOWN, 'members':[], 'option':'', + 'description':'Unclassified/unrecognized warnings', + 'patterns':[r".*: warning: .+"] }, +] + +anchor = 0 +cur_row_color = 0 +row_colors = [ 'e0e0e0', 'd0d0d0' ] + +def output(text): + print text, + +def htmlbig(param): + return '<font size="+2">' + param + '</font>' + +def dumphtmlprologue(title): + output('<html>\n<head>\n<title>' + title + '</title>\n<body>\n') + output(htmlbig(title)) + output('<p>\n') + +def tablerow(text): + global cur_row_color + output('<tr bgcolor="' + row_colors[cur_row_color] + '"><td colspan="2">',) + cur_row_color = 1 - cur_row_color + output(text,) + output('</td></tr>') + +def begintable(text, backgroundcolor): + global anchor + output('<table border="1" rules="cols" frame="box" width="100%" bgcolor="black"><tr bgcolor="' + + backgroundcolor + '"><a name="anchor' + str(anchor) + '"><td>') + output(htmlbig(text[0]) + '<br>') + for i in text[1:]: + output(i + '<br>') + output('</td>') + output('<td width="100" bgcolor="grey"><a align="right" href="#anchor' + str(anchor-1) + + '">previous</a><br><a align="right" href="#anchor' + str(anchor+1) + '">next</a>') + output('</td></a></tr>') + anchor += 1 + +def endtable(): + output('</table><p>') + + +# dump some stats about total number of warnings and such +def dumpstats(): + known = 0 + unknown = 0 + for i in warnpatterns: + if i['severity'] == severity.UNKNOWN: + unknown += len(i['members']) + elif i['severity'] != severity.SKIP: + known += len(i['members']) + output('Number of classified warnings: <b>' + str(known) + '</b><br>' ) + output('Number of unclassified warnings: <b>' + str(unknown) + '</b><br>') + total = unknown + known + output('Total number of warnings: <b>' + str(total) + '</b>') + if total < 1000: + output('(low count may indicate incremental build)') + output('<p>') + +def allpatterns(cat): + pats = '' + for i in cat['patterns']: + pats += i + pats += ' / ' + return pats + +def descriptionfor(cat): + if cat['description'] != '': + return cat['description'] + return allpatterns(cat) + + +# show which warnings no longer occur +def dumpfixed(): + tablestarted = False + for i in warnpatterns: + if len(i['members']) == 0 and i['severity'] != severity.SKIP: + if tablestarted == False: + tablestarted = True + begintable(['Fixed warnings', 'No more occurences. Please consider turning these in to errors if possible, before they are reintroduced in to the build'], 'blue') + tablerow(i['description'] + ' (' + allpatterns(i) + ') ' + i['option']) + if tablestarted: + endtable() + + +# dump a category, provided it is not marked as 'SKIP' and has more than 0 occurrences +def dumpcategory(cat): + if cat['severity'] != severity.SKIP and len(cat['members']) != 0: + header = [descriptionfor(cat),str(len(cat['members'])) + ' occurences:'] + if cat['option'] != '': + header[1:1] = [' (related option: ' + cat['option'] +')'] + begintable(header, colorforseverity(cat['severity'])) + for i in cat['members']: + tablerow(i) + endtable() + + +# dump everything for a given severity +def dumpseverity(sev): + for i in warnpatterns: + if i['severity'] == sev: + dumpcategory(i) + + +def classifywarning(line): + for i in warnpatterns: + for cpat in i['compiledpatterns']: + if cpat.match(line): + i['members'].append(line) + return + else: + # If we end up here, there was a problem parsing the log + # probably caused by 'make -j' mixing the output from + # 2 or more concurrent compiles + pass + +# precompiling every pattern speeds up parsing by about 30x +def compilepatterns(): + for i in warnpatterns: + i['compiledpatterns'] = [] + for pat in i['patterns']: + i['compiledpatterns'].append(re.compile(pat)) + +infile = open(sys.argv[1], 'r') +warnings = [] + +platformversion = 'unknown' +targetproduct = 'unknown' +targetvariant = 'unknown' +linecounter = 0 + +warningpattern = re.compile('.* warning:.*') +compilepatterns() + +# read the log file and classify all the warnings +lastmatchedline = '' +for line in infile: + if warningpattern.match(line): + if line != lastmatchedline: + classifywarning(line) + lastmatchedline = line + else: + # save a little bit of time by only doing this for the first few lines + if linecounter < 50: + linecounter +=1 + m = re.search('(?<=^PLATFORM_VERSION=).*', line) + if m != None: + platformversion = m.group(0) + m = re.search('(?<=^TARGET_PRODUCT=).*', line) + if m != None: + targetproduct = m.group(0) + m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line) + if m != None: + targetvariant = m.group(0) + + +# dump the html output to stdout +dumphtmlprologue('Warnings for ' + platformversion + ' - ' + targetproduct + ' - ' + targetvariant) +dumpstats() +dumpseverity(severity.FIXMENOW) +dumpseverity(severity.HIGH) +dumpseverity(severity.MEDIUM) +dumpseverity(severity.LOW) +dumpseverity(severity.HARMLESS) +dumpseverity(severity.UNKNOWN) +dumpfixed() + diff --git a/tools/zipalign/Android.mk b/tools/zipalign/Android.mk index e23b699..f90a31c 100644 --- a/tools/zipalign/Android.mk +++ b/tools/zipalign/Android.mk @@ -8,7 +8,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - ZipAlign.cpp + ZipAlign.cpp \ + ZipEntry.cpp \ + ZipFile.cpp LOCAL_C_INCLUDES += external/zlib diff --git a/tools/zipalign/README.txt b/tools/zipalign/README.txt index a2e1a5e..9c7d07e 100644 --- a/tools/zipalign/README.txt +++ b/tools/zipalign/README.txt @@ -1,7 +1,9 @@ zipalign -- zip archive alignment tool usage: zipalign [-f] [-v] <align> infile.zip outfile.zip + zipalign -c [-v] <align> infile.zip + -c : check alignment only (does not modify file) -f : overwrite existing outfile.zip -v : verbose output <align> is in bytes, e.g. "4" provides 32-bit alignment @@ -29,3 +31,5 @@ entries. Files added to an "aligned" archive will not be aligned. By default, zipalign will not overwrite an existing output file. With the "-f" flag, an existing file will be overwritten. +You can use the "-c" flag to test whether a zip archive is properly aligned. + diff --git a/tools/zipalign/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp index 058f9ed..c2d8159 100644 --- a/tools/zipalign/ZipAlign.cpp +++ b/tools/zipalign/ZipAlign.cpp @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Zip alignment tool */ -#include "utils/ZipFile.h" +#include "ZipFile.h" #include <stdlib.h> #include <stdio.h> @@ -29,9 +30,15 @@ using namespace android; void usage(void) { fprintf(stderr, "Zip alignment utility\n"); + fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n"); fprintf(stderr, "Usage: zipalign [-f] [-v] <align> infile.zip outfile.zip\n" - " zipalign -c [-v] <align> infile.zip\n" ); + " zipalign -c [-v] <align> infile.zip\n\n" ); + fprintf(stderr, + " <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n"); + fprintf(stderr, " -c: check alignment only (does not modify file)\n"); + fprintf(stderr, " -f: overwrite existing outfile.zip\n"); + fprintf(stderr, " -v: verbose output\n"); } /* diff --git a/tools/zipalign/ZipEntry.cpp b/tools/zipalign/ZipEntry.cpp new file mode 100644 index 0000000..bed0333 --- /dev/null +++ b/tools/zipalign/ZipEntry.cpp @@ -0,0 +1,696 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access to entries in a Zip archive. +// + +#define LOG_TAG "zip" + +#include "ZipEntry.h" +#include <utils/Log.h> + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +using namespace android; + +/* + * Initialize a new ZipEntry structure from a FILE* positioned at a + * CentralDirectoryEntry. + * + * On exit, the file pointer will be at the start of the next CDE or + * at the EOCD. + */ +status_t ZipEntry::initFromCDE(FILE* fp) +{ + status_t result; + long posn; + bool hasDD; + + //LOGV("initFromCDE ---\n"); + + /* read the CDE */ + result = mCDE.read(fp); + if (result != NO_ERROR) { + LOGD("mCDE.read failed\n"); + return result; + } + + //mCDE.dump(); + + /* using the info in the CDE, go load up the LFH */ + posn = ftell(fp); + if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { + LOGD("local header seek failed (%ld)\n", + mCDE.mLocalHeaderRelOffset); + return UNKNOWN_ERROR; + } + + result = mLFH.read(fp); + if (result != NO_ERROR) { + LOGD("mLFH.read failed\n"); + return result; + } + + if (fseek(fp, posn, SEEK_SET) != 0) + return UNKNOWN_ERROR; + + //mLFH.dump(); + + /* + * We *might* need to read the Data Descriptor at this point and + * integrate it into the LFH. If this bit is set, the CRC-32, + * compressed size, and uncompressed size will be zero. In practice + * these seem to be rare. + */ + hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; + if (hasDD) { + // do something clever + //LOGD("+++ has data descriptor\n"); + } + + /* + * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr" + * flag is set, because the LFH is incomplete. (Not a problem, since we + * prefer the CDE values.) + */ + if (!hasDD && !compareHeaders()) { + LOGW("WARNING: header mismatch\n"); + // keep going? + } + + /* + * If the mVersionToExtract is greater than 20, we may have an + * issue unpacking the record -- could be encrypted, compressed + * with something we don't support, or use Zip64 extensions. We + * can defer worrying about that to when we're extracting data. + */ + + return NO_ERROR; +} + +/* + * Initialize a new entry. Pass in the file name and an optional comment. + * + * Initializes the CDE and the LFH. + */ +void ZipEntry::initNew(const char* fileName, const char* comment) +{ + assert(fileName != NULL && *fileName != '\0'); // name required + + /* most fields are properly initialized by constructor */ + mCDE.mVersionMadeBy = kDefaultMadeBy; + mCDE.mVersionToExtract = kDefaultVersion; + mCDE.mCompressionMethod = kCompressStored; + mCDE.mFileNameLength = strlen(fileName); + if (comment != NULL) + mCDE.mFileCommentLength = strlen(comment); + mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does + + if (mCDE.mFileNameLength > 0) { + mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; + strcpy((char*) mCDE.mFileName, fileName); + } + if (mCDE.mFileCommentLength > 0) { + /* TODO: stop assuming null-terminated ASCII here? */ + mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; + strcpy((char*) mCDE.mFileComment, comment); + } + + copyCDEtoLFH(); +} + +/* + * Initialize a new entry, starting with the ZipEntry from a different + * archive. + * + * Initializes the CDE and the LFH. + */ +status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, + const ZipEntry* pEntry) +{ + /* + * Copy everything in the CDE over, then fix up the hairy bits. + */ + memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); + + if (mCDE.mFileNameLength > 0) { + mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; + if (mCDE.mFileName == NULL) + return NO_MEMORY; + strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); + } + if (mCDE.mFileCommentLength > 0) { + mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; + if (mCDE.mFileComment == NULL) + return NO_MEMORY; + strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); + } + if (mCDE.mExtraFieldLength > 0) { + /* we null-terminate this, though it may not be a string */ + mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; + if (mCDE.mExtraField == NULL) + return NO_MEMORY; + memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, + mCDE.mExtraFieldLength+1); + } + + /* construct the LFH from the CDE */ + copyCDEtoLFH(); + + /* + * The LFH "extra" field is independent of the CDE "extra", so we + * handle it here. + */ + assert(mLFH.mExtraField == NULL); + mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; + if (mLFH.mExtraFieldLength > 0) { + mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; + if (mLFH.mExtraField == NULL) + return NO_MEMORY; + memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, + mLFH.mExtraFieldLength+1); + } + + return NO_ERROR; +} + +/* + * Insert pad bytes in the LFH by tweaking the "extra" field. This will + * potentially confuse something that put "extra" data in here earlier, + * but I can't find an actual problem. + */ +status_t ZipEntry::addPadding(int padding) +{ + if (padding <= 0) + return INVALID_OPERATION; + + //LOGI("HEY: adding %d pad bytes to existing %d in %s\n", + // padding, mLFH.mExtraFieldLength, mCDE.mFileName); + + if (mLFH.mExtraFieldLength > 0) { + /* extend existing field */ + unsigned char* newExtra; + + newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; + if (newExtra == NULL) + return NO_MEMORY; + memset(newExtra + mLFH.mExtraFieldLength, 0, padding); + memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); + + delete[] mLFH.mExtraField; + mLFH.mExtraField = newExtra; + mLFH.mExtraFieldLength += padding; + } else { + /* create new field */ + mLFH.mExtraField = new unsigned char[padding]; + memset(mLFH.mExtraField, 0, padding); + mLFH.mExtraFieldLength = padding; + } + + return NO_ERROR; +} + +/* + * Set the fields in the LFH equal to the corresponding fields in the CDE. + * + * This does not touch the LFH "extra" field. + */ +void ZipEntry::copyCDEtoLFH(void) +{ + mLFH.mVersionToExtract = mCDE.mVersionToExtract; + mLFH.mGPBitFlag = mCDE.mGPBitFlag; + mLFH.mCompressionMethod = mCDE.mCompressionMethod; + mLFH.mLastModFileTime = mCDE.mLastModFileTime; + mLFH.mLastModFileDate = mCDE.mLastModFileDate; + mLFH.mCRC32 = mCDE.mCRC32; + mLFH.mCompressedSize = mCDE.mCompressedSize; + mLFH.mUncompressedSize = mCDE.mUncompressedSize; + mLFH.mFileNameLength = mCDE.mFileNameLength; + // the "extra field" is independent + + delete[] mLFH.mFileName; + if (mLFH.mFileNameLength > 0) { + mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; + strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); + } else { + mLFH.mFileName = NULL; + } +} + +/* + * Set some information about a file after we add it. + */ +void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, + int compressionMethod) +{ + mCDE.mCompressionMethod = compressionMethod; + mCDE.mCRC32 = crc32; + mCDE.mCompressedSize = compLen; + mCDE.mUncompressedSize = uncompLen; + mCDE.mCompressionMethod = compressionMethod; + if (compressionMethod == kCompressDeflated) { + mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used + } + copyCDEtoLFH(); +} + +/* + * See if the data in mCDE and mLFH match up. This is mostly useful for + * debugging these classes, but it can be used to identify damaged + * archives. + * + * Returns "false" if they differ. + */ +bool ZipEntry::compareHeaders(void) const +{ + if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { + LOGV("cmp: VersionToExtract\n"); + return false; + } + if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { + LOGV("cmp: GPBitFlag\n"); + return false; + } + if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { + LOGV("cmp: CompressionMethod\n"); + return false; + } + if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { + LOGV("cmp: LastModFileTime\n"); + return false; + } + if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { + LOGV("cmp: LastModFileDate\n"); + return false; + } + if (mCDE.mCRC32 != mLFH.mCRC32) { + LOGV("cmp: CRC32\n"); + return false; + } + if (mCDE.mCompressedSize != mLFH.mCompressedSize) { + LOGV("cmp: CompressedSize\n"); + return false; + } + if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { + LOGV("cmp: UncompressedSize\n"); + return false; + } + if (mCDE.mFileNameLength != mLFH.mFileNameLength) { + LOGV("cmp: FileNameLength\n"); + return false; + } +#if 0 // this seems to be used for padding, not real data + if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { + LOGV("cmp: ExtraFieldLength\n"); + return false; + } +#endif + if (mCDE.mFileName != NULL) { + if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { + LOGV("cmp: FileName\n"); + return false; + } + } + + return true; +} + + +/* + * Convert the DOS date/time stamp into a UNIX time stamp. + */ +time_t ZipEntry::getModWhen(void) const +{ + struct tm parts; + + parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; + parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; + parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; + parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); + parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; + parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; + parts.tm_wday = parts.tm_yday = 0; + parts.tm_isdst = -1; // DST info "not available" + + return mktime(&parts); +} + +/* + * Set the CDE/LFH timestamp from UNIX time. + */ +void ZipEntry::setModWhen(time_t when) +{ +#ifdef HAVE_LOCALTIME_R + struct tm tmResult; +#endif + time_t even; + unsigned short zdate, ztime; + + struct tm* ptm; + + /* round up to an even number of seconds */ + even = (time_t)(((unsigned long)(when) + 1) & (~1)); + + /* expand */ +#ifdef HAVE_LOCALTIME_R + ptm = localtime_r(&even, &tmResult); +#else + ptm = localtime(&even); +#endif + + int year; + year = ptm->tm_year; + if (year < 80) + year = 80; + + zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; + ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; + + mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; + mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; +} + + +/* + * =========================================================================== + * ZipEntry::LocalFileHeader + * =========================================================================== + */ + +/* + * Read a local file header. + * + * On entry, "fp" points to the signature at the start of the header. + * On exit, "fp" points to the start of data. + */ +status_t ZipEntry::LocalFileHeader::read(FILE* fp) +{ + status_t result = NO_ERROR; + unsigned char buf[kLFHLen]; + + assert(mFileName == NULL); + assert(mExtraField == NULL); + + if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { + result = UNKNOWN_ERROR; + goto bail; + } + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOGD("whoops: didn't find expected signature\n"); + result = UNKNOWN_ERROR; + goto bail; + } + + mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); + mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); + mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); + mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); + mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); + mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); + mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); + mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); + mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); + mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); + + // TODO: validate sizes + + /* grab filename */ + if (mFileNameLength != 0) { + mFileName = new unsigned char[mFileNameLength+1]; + if (mFileName == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mFileName[mFileNameLength] = '\0'; + } + + /* grab extra field */ + if (mExtraFieldLength != 0) { + mExtraField = new unsigned char[mExtraFieldLength+1]; + if (mExtraField == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mExtraField[mExtraFieldLength] = '\0'; + } + +bail: + return result; +} + +/* + * Write a local file header. + */ +status_t ZipEntry::LocalFileHeader::write(FILE* fp) +{ + unsigned char buf[kLFHLen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); + ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); + ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); + ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); + ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); + ZipEntry::putLongLE(&buf[0x0e], mCRC32); + ZipEntry::putLongLE(&buf[0x12], mCompressedSize); + ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); + ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); + ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); + + if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) + return UNKNOWN_ERROR; + + /* write filename */ + if (mFileNameLength != 0) { + if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) + return UNKNOWN_ERROR; + } + + /* write "extra field" */ + if (mExtraFieldLength != 0) { + if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + + +/* + * Dump the contents of a LocalFileHeader object. + */ +void ZipEntry::LocalFileHeader::dump(void) const +{ + LOGD(" LocalFileHeader contents:\n"); + LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n", + mVersionToExtract, mGPBitFlag, mCompressionMethod); + LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", + mLastModFileTime, mLastModFileDate, mCRC32); + LOGD(" compressedSize=%lu uncompressedSize=%lu\n", + mCompressedSize, mUncompressedSize); + LOGD(" filenameLen=%u extraLen=%u\n", + mFileNameLength, mExtraFieldLength); + if (mFileName != NULL) + LOGD(" filename: '%s'\n", mFileName); +} + + +/* + * =========================================================================== + * ZipEntry::CentralDirEntry + * =========================================================================== + */ + +/* + * Read the central dir entry that appears next in the file. + * + * On entry, "fp" should be positioned on the signature bytes for the + * entry. On exit, "fp" will point at the signature word for the next + * entry or for the EOCD. + */ +status_t ZipEntry::CentralDirEntry::read(FILE* fp) +{ + status_t result = NO_ERROR; + unsigned char buf[kCDELen]; + + /* no re-use */ + assert(mFileName == NULL); + assert(mExtraField == NULL); + assert(mFileComment == NULL); + + if (fread(buf, 1, kCDELen, fp) != kCDELen) { + result = UNKNOWN_ERROR; + goto bail; + } + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOGD("Whoops: didn't find expected signature\n"); + result = UNKNOWN_ERROR; + goto bail; + } + + mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); + mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); + mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); + mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); + mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); + mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); + mCRC32 = ZipEntry::getLongLE(&buf[0x10]); + mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); + mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); + mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); + mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); + mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); + mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); + mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); + mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); + mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); + + // TODO: validate sizes and offsets + + /* grab filename */ + if (mFileNameLength != 0) { + mFileName = new unsigned char[mFileNameLength+1]; + if (mFileName == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mFileName[mFileNameLength] = '\0'; + } + + /* read "extra field" */ + if (mExtraFieldLength != 0) { + mExtraField = new unsigned char[mExtraFieldLength+1]; + if (mExtraField == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mExtraField[mExtraFieldLength] = '\0'; + } + + + /* grab comment, if any */ + if (mFileCommentLength != 0) { + mFileComment = new unsigned char[mFileCommentLength+1]; + if (mFileComment == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) + { + result = UNKNOWN_ERROR; + goto bail; + } + mFileComment[mFileCommentLength] = '\0'; + } + +bail: + return result; +} + +/* + * Write a central dir entry. + */ +status_t ZipEntry::CentralDirEntry::write(FILE* fp) +{ + unsigned char buf[kCDELen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); + ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); + ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); + ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); + ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); + ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); + ZipEntry::putLongLE(&buf[0x10], mCRC32); + ZipEntry::putLongLE(&buf[0x14], mCompressedSize); + ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); + ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); + ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); + ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); + ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); + ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); + ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); + ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); + + if (fwrite(buf, 1, kCDELen, fp) != kCDELen) + return UNKNOWN_ERROR; + + /* write filename */ + if (mFileNameLength != 0) { + if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) + return UNKNOWN_ERROR; + } + + /* write "extra field" */ + if (mExtraFieldLength != 0) { + if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) + return UNKNOWN_ERROR; + } + + /* write comment */ + if (mFileCommentLength != 0) { + if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +/* + * Dump the contents of a CentralDirEntry object. + */ +void ZipEntry::CentralDirEntry::dump(void) const +{ + LOGD(" CentralDirEntry contents:\n"); + LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", + mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); + LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", + mLastModFileTime, mLastModFileDate, mCRC32); + LOGD(" compressedSize=%lu uncompressedSize=%lu\n", + mCompressedSize, mUncompressedSize); + LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n", + mFileNameLength, mExtraFieldLength, mFileCommentLength); + LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", + mDiskNumberStart, mInternalAttrs, mExternalAttrs, + mLocalHeaderRelOffset); + + if (mFileName != NULL) + LOGD(" filename: '%s'\n", mFileName); + if (mFileComment != NULL) + LOGD(" comment: '%s'\n", mFileComment); +} + diff --git a/tools/zipalign/ZipEntry.h b/tools/zipalign/ZipEntry.h new file mode 100644 index 0000000..7f721b4 --- /dev/null +++ b/tools/zipalign/ZipEntry.h @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Zip archive entries. +// +// The ZipEntry class is tightly meshed with the ZipFile class. +// +#ifndef __LIBS_ZIPENTRY_H +#define __LIBS_ZIPENTRY_H + +#include <utils/Errors.h> + +#include <stdlib.h> +#include <stdio.h> + +namespace android { + +class ZipFile; + +/* + * ZipEntry objects represent a single entry in a Zip archive. + * + * You can use one of these to get or set information about an entry, but + * there are no functions here for accessing the data itself. (We could + * tuck a pointer to the ZipFile in here for convenience, but that raises + * the likelihood of using ZipEntry objects after discarding the ZipFile.) + * + * File information is stored in two places: next to the file data (the Local + * File Header, and possibly a Data Descriptor), and at the end of the file + * (the Central Directory Entry). The two must be kept in sync. + */ +class ZipEntry { +public: + friend class ZipFile; + + ZipEntry(void) + : mDeleted(false), mMarked(false) + {} + ~ZipEntry(void) {} + + /* + * Returns "true" if the data is compressed. + */ + bool isCompressed(void) const { + return mCDE.mCompressionMethod != kCompressStored; + } + int getCompressionMethod(void) const { return mCDE.mCompressionMethod; } + + /* + * Return the uncompressed length. + */ + off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; } + + /* + * Return the compressed length. For uncompressed data, this returns + * the same thing as getUncompresesdLen(). + */ + off_t getCompressedLen(void) const { return mCDE.mCompressedSize; } + + /* + * Return the absolute file offset of the start of the compressed or + * uncompressed data. + */ + off_t getFileOffset(void) const { + return mCDE.mLocalHeaderRelOffset + + LocalFileHeader::kLFHLen + + mLFH.mFileNameLength + + mLFH.mExtraFieldLength; + } + + /* + * Return the data CRC. + */ + unsigned long getCRC32(void) const { return mCDE.mCRC32; } + + /* + * Return file modification time in UNIX seconds-since-epoch. + */ + time_t getModWhen(void) const; + + /* + * Return the archived file name. + */ + const char* getFileName(void) const { return (const char*) mCDE.mFileName; } + + /* + * Application-defined "mark". Can be useful when synchronizing the + * contents of an archive with contents on disk. + */ + bool getMarked(void) const { return mMarked; } + void setMarked(bool val) { mMarked = val; } + + /* + * Some basic functions for raw data manipulation. "LE" means + * Little Endian. + */ + static inline unsigned short getShortLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8); + } + static inline unsigned long getLongLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + static inline void putShortLE(unsigned char* buf, short val) { + buf[0] = (unsigned char) val; + buf[1] = (unsigned char) (val >> 8); + } + static inline void putLongLE(unsigned char* buf, long val) { + buf[0] = (unsigned char) val; + buf[1] = (unsigned char) (val >> 8); + buf[2] = (unsigned char) (val >> 16); + buf[3] = (unsigned char) (val >> 24); + } + + /* defined for Zip archives */ + enum { + kCompressStored = 0, // no compression + // shrunk = 1, + // reduced 1 = 2, + // reduced 2 = 3, + // reduced 3 = 4, + // reduced 4 = 5, + // imploded = 6, + // tokenized = 7, + kCompressDeflated = 8, // standard deflate + // Deflate64 = 9, + // lib imploded = 10, + // reserved = 11, + // bzip2 = 12, + }; + + /* + * Deletion flag. If set, the entry will be removed on the next + * call to "flush". + */ + bool getDeleted(void) const { return mDeleted; } + +protected: + /* + * Initialize the structure from the file, which is pointing at + * our Central Directory entry. + */ + status_t initFromCDE(FILE* fp); + + /* + * Initialize the structure for a new file. We need the filename + * and comment so that we can properly size the LFH area. The + * filename is mandatory, the comment is optional. + */ + void initNew(const char* fileName, const char* comment); + + /* + * Initialize the structure with the contents of a ZipEntry from + * another file. + */ + status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry); + + /* + * Add some pad bytes to the LFH. We do this by adding or resizing + * the "extra" field. + */ + status_t addPadding(int padding); + + /* + * Set information about the data for this entry. + */ + void setDataInfo(long uncompLen, long compLen, unsigned long crc32, + int compressionMethod); + + /* + * Set the modification date. + */ + void setModWhen(time_t when); + + /* + * Return the offset of the local file header. + */ + off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; } + + /* + * Set the offset of the local file header, relative to the start of + * the current file. + */ + void setLFHOffset(off_t offset) { + mCDE.mLocalHeaderRelOffset = (long) offset; + } + + /* mark for deletion; used by ZipFile::remove() */ + void setDeleted(void) { mDeleted = true; } + +private: + /* these are private and not defined */ + ZipEntry(const ZipEntry& src); + ZipEntry& operator=(const ZipEntry& src); + + /* returns "true" if the CDE and the LFH agree */ + bool compareHeaders(void) const; + void copyCDEtoLFH(void); + + bool mDeleted; // set if entry is pending deletion + bool mMarked; // app-defined marker + + /* + * Every entry in the Zip archive starts off with one of these. + */ + class LocalFileHeader { + public: + LocalFileHeader(void) : + mVersionToExtract(0), + mGPBitFlag(0), + mCompressionMethod(0), + mLastModFileTime(0), + mLastModFileDate(0), + mCRC32(0), + mCompressedSize(0), + mUncompressedSize(0), + mFileNameLength(0), + mExtraFieldLength(0), + mFileName(NULL), + mExtraField(NULL) + {} + virtual ~LocalFileHeader(void) { + delete[] mFileName; + delete[] mExtraField; + } + + status_t read(FILE* fp); + status_t write(FILE* fp); + + // unsigned long mSignature; + unsigned short mVersionToExtract; + unsigned short mGPBitFlag; + unsigned short mCompressionMethod; + unsigned short mLastModFileTime; + unsigned short mLastModFileDate; + unsigned long mCRC32; + unsigned long mCompressedSize; + unsigned long mUncompressedSize; + unsigned short mFileNameLength; + unsigned short mExtraFieldLength; + unsigned char* mFileName; + unsigned char* mExtraField; + + enum { + kSignature = 0x04034b50, + kLFHLen = 30, // LocalFileHdr len, excl. var fields + }; + + void dump(void) const; + }; + + /* + * Every entry in the Zip archive has one of these in the "central + * directory" at the end of the file. + */ + class CentralDirEntry { + public: + CentralDirEntry(void) : + mVersionMadeBy(0), + mVersionToExtract(0), + mGPBitFlag(0), + mCompressionMethod(0), + mLastModFileTime(0), + mLastModFileDate(0), + mCRC32(0), + mCompressedSize(0), + mUncompressedSize(0), + mFileNameLength(0), + mExtraFieldLength(0), + mFileCommentLength(0), + mDiskNumberStart(0), + mInternalAttrs(0), + mExternalAttrs(0), + mLocalHeaderRelOffset(0), + mFileName(NULL), + mExtraField(NULL), + mFileComment(NULL) + {} + virtual ~CentralDirEntry(void) { + delete[] mFileName; + delete[] mExtraField; + delete[] mFileComment; + } + + status_t read(FILE* fp); + status_t write(FILE* fp); + + // unsigned long mSignature; + unsigned short mVersionMadeBy; + unsigned short mVersionToExtract; + unsigned short mGPBitFlag; + unsigned short mCompressionMethod; + unsigned short mLastModFileTime; + unsigned short mLastModFileDate; + unsigned long mCRC32; + unsigned long mCompressedSize; + unsigned long mUncompressedSize; + unsigned short mFileNameLength; + unsigned short mExtraFieldLength; + unsigned short mFileCommentLength; + unsigned short mDiskNumberStart; + unsigned short mInternalAttrs; + unsigned long mExternalAttrs; + unsigned long mLocalHeaderRelOffset; + unsigned char* mFileName; + unsigned char* mExtraField; + unsigned char* mFileComment; + + void dump(void) const; + + enum { + kSignature = 0x02014b50, + kCDELen = 46, // CentralDirEnt len, excl. var fields + }; + }; + + enum { + //kDataDescriptorSignature = 0x08074b50, // currently unused + kDataDescriptorLen = 16, // four 32-bit fields + + kDefaultVersion = 20, // need deflate, nothing much else + kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3 + kUsesDataDescr = 0x0008, // GPBitFlag bit 3 + }; + + LocalFileHeader mLFH; + CentralDirEntry mCDE; +}; + +}; // namespace android + +#endif // __LIBS_ZIPENTRY_H diff --git a/tools/zipalign/ZipFile.cpp b/tools/zipalign/ZipFile.cpp new file mode 100644 index 0000000..62c9383 --- /dev/null +++ b/tools/zipalign/ZipFile.cpp @@ -0,0 +1,1297 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access to Zip archives. +// + +#define LOG_TAG "zip" + +#include <utils/ZipUtils.h> +#include <utils/Log.h> + +#include "ZipFile.h" + +#include <zlib.h> +#define DEF_MEM_LEVEL 8 // normally in zutil.h? + +#include <memory.h> +#include <sys/stat.h> +#include <errno.h> +#include <assert.h> + +using namespace android; + +/* + * Some environments require the "b", some choke on it. + */ +#define FILE_OPEN_RO "rb" +#define FILE_OPEN_RW "r+b" +#define FILE_OPEN_RW_CREATE "w+b" + +/* should live somewhere else? */ +static status_t errnoToStatus(int err) +{ + if (err == ENOENT) + return NAME_NOT_FOUND; + else if (err == EACCES) + return PERMISSION_DENIED; + else + return UNKNOWN_ERROR; +} + +/* + * Open a file and parse its guts. + */ +status_t ZipFile::open(const char* zipFileName, int flags) +{ + bool newArchive = false; + + assert(mZipFp == NULL); // no reopen + + if ((flags & kOpenTruncate)) + flags |= kOpenCreate; // trunc implies create + + if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite)) + return INVALID_OPERATION; // not both + if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite))) + return INVALID_OPERATION; // not neither + if ((flags & kOpenCreate) && !(flags & kOpenReadWrite)) + return INVALID_OPERATION; // create requires write + + if (flags & kOpenTruncate) { + newArchive = true; + } else { + newArchive = (access(zipFileName, F_OK) != 0); + if (!(flags & kOpenCreate) && newArchive) { + /* not creating, must already exist */ + LOGD("File %s does not exist", zipFileName); + return NAME_NOT_FOUND; + } + } + + /* open the file */ + const char* openflags; + if (flags & kOpenReadWrite) { + if (newArchive) + openflags = FILE_OPEN_RW_CREATE; + else + openflags = FILE_OPEN_RW; + } else { + openflags = FILE_OPEN_RO; + } + mZipFp = fopen(zipFileName, openflags); + if (mZipFp == NULL) { + int err = errno; + LOGD("fopen failed: %d\n", err); + return errnoToStatus(err); + } + + status_t result; + if (!newArchive) { + /* + * Load the central directory. If that fails, then this probably + * isn't a Zip archive. + */ + result = readCentralDir(); + } else { + /* + * Newly-created. The EndOfCentralDir constructor actually + * sets everything to be the way we want it (all zeroes). We + * set mNeedCDRewrite so that we create *something* if the + * caller doesn't add any files. (We could also just unlink + * the file if it's brand new and nothing was added, but that's + * probably doing more than we really should -- the user might + * have a need for empty zip files.) + */ + mNeedCDRewrite = true; + result = NO_ERROR; + } + + if (flags & kOpenReadOnly) + mReadOnly = true; + else + assert(!mReadOnly); + + return result; +} + +/* + * Return the Nth entry in the archive. + */ +ZipEntry* ZipFile::getEntryByIndex(int idx) const +{ + if (idx < 0 || idx >= (int) mEntries.size()) + return NULL; + + return mEntries[idx]; +} + +/* + * Find an entry by name. + */ +ZipEntry* ZipFile::getEntryByName(const char* fileName) const +{ + /* + * Do a stupid linear string-compare search. + * + * There are various ways to speed this up, especially since it's rare + * to intermingle changes to the archive with "get by name" calls. We + * don't want to sort the mEntries vector itself, however, because + * it's used to recreate the Central Directory. + * + * (Hash table works, parallel list of pointers in sorted order is good.) + */ + int idx; + + for (idx = mEntries.size()-1; idx >= 0; idx--) { + ZipEntry* pEntry = mEntries[idx]; + if (!pEntry->getDeleted() && + strcmp(fileName, pEntry->getFileName()) == 0) + { + return pEntry; + } + } + + return NULL; +} + +/* + * Empty the mEntries vector. + */ +void ZipFile::discardEntries(void) +{ + int count = mEntries.size(); + + while (--count >= 0) + delete mEntries[count]; + + mEntries.clear(); +} + + +/* + * Find the central directory and read the contents. + * + * The fun thing about ZIP archives is that they may or may not be + * readable from start to end. In some cases, notably for archives + * that were written to stdout, the only length information is in the + * central directory at the end of the file. + * + * Of course, the central directory can be followed by a variable-length + * comment field, so we have to scan through it backwards. The comment + * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff + * itself, plus apparently sometimes people throw random junk on the end + * just for the fun of it. + * + * This is all a little wobbly. If the wrong value ends up in the EOCD + * area, we're hosed. This appears to be the way that everbody handles + * it though, so we're in pretty good company if this fails. + */ +status_t ZipFile::readCentralDir(void) +{ + status_t result = NO_ERROR; + unsigned char* buf = NULL; + off_t fileLength, seekStart; + long readAmount; + int i; + + fseek(mZipFp, 0, SEEK_END); + fileLength = ftell(mZipFp); + rewind(mZipFp); + + /* too small to be a ZIP archive? */ + if (fileLength < EndOfCentralDir::kEOCDLen) { + LOGD("Length is %ld -- too small\n", (long)fileLength); + result = INVALID_OPERATION; + goto bail; + } + + buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; + if (buf == NULL) { + LOGD("Failure allocating %d bytes for EOCD search", + EndOfCentralDir::kMaxEOCDSearch); + result = NO_MEMORY; + goto bail; + } + + if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { + seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; + readAmount = EndOfCentralDir::kMaxEOCDSearch; + } else { + seekStart = 0; + readAmount = (long) fileLength; + } + if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { + LOGD("Failure seeking to end of zip at %ld", (long) seekStart); + result = UNKNOWN_ERROR; + goto bail; + } + + /* read the last part of the file into the buffer */ + if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { + LOGD("short file? wanted %ld\n", readAmount); + result = UNKNOWN_ERROR; + goto bail; + } + + /* find the end-of-central-dir magic */ + for (i = readAmount - 4; i >= 0; i--) { + if (buf[i] == 0x50 && + ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) + { + LOGV("+++ Found EOCD at buf+%d\n", i); + break; + } + } + if (i < 0) { + LOGD("EOCD not found, not Zip\n"); + result = INVALID_OPERATION; + goto bail; + } + + /* extract eocd values */ + result = mEOCD.readBuf(buf + i, readAmount - i); + if (result != NO_ERROR) { + LOGD("Failure reading %ld bytes of EOCD values", readAmount - i); + goto bail; + } + //mEOCD.dump(); + + if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || + mEOCD.mNumEntries != mEOCD.mTotalNumEntries) + { + LOGD("Archive spanning not supported\n"); + result = INVALID_OPERATION; + goto bail; + } + + /* + * So far so good. "mCentralDirSize" is the size in bytes of the + * central directory, so we can just seek back that far to find it. + * We can also seek forward mCentralDirOffset bytes from the + * start of the file. + * + * We're not guaranteed to have the rest of the central dir in the + * buffer, nor are we guaranteed that the central dir will have any + * sort of convenient size. We need to skip to the start of it and + * read the header, then the other goodies. + * + * The only thing we really need right now is the file comment, which + * we're hoping to preserve. + */ + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + LOGD("Failure seeking to central dir offset %ld\n", + mEOCD.mCentralDirOffset); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * Loop through and read the central dir entries. + */ + LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries); + int entry; + for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { + ZipEntry* pEntry = new ZipEntry; + + result = pEntry->initFromCDE(mZipFp); + if (result != NO_ERROR) { + LOGD("initFromCDE failed\n"); + delete pEntry; + goto bail; + } + + mEntries.add(pEntry); + } + + + /* + * If all went well, we should now be back at the EOCD. + */ + { + unsigned char checkBuf[4]; + if (fread(checkBuf, 1, 4, mZipFp) != 4) { + LOGD("EOCD check read failed\n"); + result = INVALID_OPERATION; + goto bail; + } + if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { + LOGD("EOCD read check failed\n"); + result = UNKNOWN_ERROR; + goto bail; + } + LOGV("+++ EOCD read check passed\n"); + } + +bail: + delete[] buf; + return result; +} + + +/* + * Add a new file to the archive. + * + * This requires creating and populating a ZipEntry structure, and copying + * the data into the file at the appropriate position. The "appropriate + * position" is the current location of the central directory, which we + * casually overwrite (we can put it back later). + * + * If we were concerned about safety, we would want to make all changes + * in a temp file and then overwrite the original after everything was + * safely written. Not really a concern for us. + */ +status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size, + const char* storageName, int sourceType, int compressionMethod, + ZipEntry** ppEntry) +{ + ZipEntry* pEntry = NULL; + status_t result = NO_ERROR; + long lfhPosn, startPosn, endPosn, uncompressedLen; + FILE* inputFp = NULL; + unsigned long crc; + time_t modWhen; + + if (mReadOnly) + return INVALID_OPERATION; + + assert(compressionMethod == ZipEntry::kCompressDeflated || + compressionMethod == ZipEntry::kCompressStored); + + /* make sure we're in a reasonable state */ + assert(mZipFp != NULL); + assert(mEntries.size() == mEOCD.mTotalNumEntries); + + /* make sure it doesn't already exist */ + if (getEntryByName(storageName) != NULL) + return ALREADY_EXISTS; + + if (!data) { + inputFp = fopen(fileName, FILE_OPEN_RO); + if (inputFp == NULL) + return errnoToStatus(errno); + } + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + + pEntry = new ZipEntry; + pEntry->initNew(storageName, NULL); + + /* + * From here on out, failures are more interesting. + */ + mNeedCDRewrite = true; + + /* + * Write the LFH, even though it's still mostly blank. We need it + * as a place-holder. In theory the LFH isn't necessary, but in + * practice some utilities demand it. + */ + lfhPosn = ftell(mZipFp); + pEntry->mLFH.write(mZipFp); + startPosn = ftell(mZipFp); + + /* + * Copy the data in, possibly compressing it as we go. + */ + if (sourceType == ZipEntry::kCompressStored) { + if (compressionMethod == ZipEntry::kCompressDeflated) { + bool failed = false; + result = compressFpToFp(mZipFp, inputFp, data, size, &crc); + if (result != NO_ERROR) { + LOGD("compression failed, storing\n"); + failed = true; + } else { + /* + * Make sure it has compressed "enough". This probably ought + * to be set through an API call, but I don't expect our + * criteria to change over time. + */ + long src = inputFp ? ftell(inputFp) : size; + long dst = ftell(mZipFp) - startPosn; + if (dst + (dst / 10) > src) { + LOGD("insufficient compression (src=%ld dst=%ld), storing\n", + src, dst); + failed = true; + } + } + + if (failed) { + compressionMethod = ZipEntry::kCompressStored; + if (inputFp) rewind(inputFp); + fseek(mZipFp, startPosn, SEEK_SET); + /* fall through to kCompressStored case */ + } + } + /* handle "no compression" request, or failed compression from above */ + if (compressionMethod == ZipEntry::kCompressStored) { + if (inputFp) { + result = copyFpToFp(mZipFp, inputFp, &crc); + } else { + result = copyDataToFp(mZipFp, data, size, &crc); + } + if (result != NO_ERROR) { + // don't need to truncate; happens in CDE rewrite + LOGD("failed copying data in\n"); + goto bail; + } + } + + // currently seeked to end of file + uncompressedLen = inputFp ? ftell(inputFp) : size; + } else if (sourceType == ZipEntry::kCompressDeflated) { + /* we should support uncompressed-from-compressed, but it's not + * important right now */ + assert(compressionMethod == ZipEntry::kCompressDeflated); + + bool scanResult; + int method; + long compressedLen; + + scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen, + &compressedLen, &crc); + if (!scanResult || method != ZipEntry::kCompressDeflated) { + LOGD("this isn't a deflated gzip file?"); + result = UNKNOWN_ERROR; + goto bail; + } + + result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL); + if (result != NO_ERROR) { + LOGD("failed copying gzip data in\n"); + goto bail; + } + } else { + assert(false); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * We could write the "Data Descriptor", but there doesn't seem to + * be any point since we're going to go back and write the LFH. + * + * Update file offsets. + */ + endPosn = ftell(mZipFp); // seeked to end of compressed data + + /* + * Success! Fill out new values. + */ + pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, + compressionMethod); + modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); + pEntry->setModWhen(modWhen); + pEntry->setLFHOffset(lfhPosn); + mEOCD.mNumEntries++; + mEOCD.mTotalNumEntries++; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + mEOCD.mCentralDirOffset = endPosn; + + /* + * Go back and write the LFH. + */ + if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + pEntry->mLFH.write(mZipFp); + + /* + * Add pEntry to the list. + */ + mEntries.add(pEntry); + if (ppEntry != NULL) + *ppEntry = pEntry; + pEntry = NULL; + +bail: + if (inputFp != NULL) + fclose(inputFp); + delete pEntry; + return result; +} + +/* + * Add an entry by copying it from another zip file. If "padding" is + * nonzero, the specified number of bytes will be added to the "extra" + * field in the header. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ +status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, + int padding, ZipEntry** ppEntry) +{ + ZipEntry* pEntry = NULL; + status_t result; + long lfhPosn, endPosn; + + if (mReadOnly) + return INVALID_OPERATION; + + /* make sure we're in a reasonable state */ + assert(mZipFp != NULL); + assert(mEntries.size() == mEOCD.mTotalNumEntries); + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + + pEntry = new ZipEntry; + if (pEntry == NULL) { + result = NO_MEMORY; + goto bail; + } + + result = pEntry->initFromExternal(pSourceZip, pSourceEntry); + if (result != NO_ERROR) + goto bail; + if (padding != 0) { + result = pEntry->addPadding(padding); + if (result != NO_ERROR) + goto bail; + } + + /* + * From here on out, failures are more interesting. + */ + mNeedCDRewrite = true; + + /* + * Write the LFH. Since we're not recompressing the data, we already + * have all of the fields filled out. + */ + lfhPosn = ftell(mZipFp); + pEntry->mLFH.write(mZipFp); + + /* + * Copy the data over. + * + * If the "has data descriptor" flag is set, we want to copy the DD + * fields as well. This is a fixed-size area immediately following + * the data. + */ + if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) + { + result = UNKNOWN_ERROR; + goto bail; + } + + off_t copyLen; + copyLen = pSourceEntry->getCompressedLen(); + if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) + copyLen += ZipEntry::kDataDescriptorLen; + + if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) + != NO_ERROR) + { + LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * Update file offsets. + */ + endPosn = ftell(mZipFp); + + /* + * Success! Fill out new values. + */ + pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset + mEOCD.mNumEntries++; + mEOCD.mTotalNumEntries++; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + mEOCD.mCentralDirOffset = endPosn; + + /* + * Add pEntry to the list. + */ + mEntries.add(pEntry); + if (ppEntry != NULL) + *ppEntry = pEntry; + pEntry = NULL; + + result = NO_ERROR; + +bail: + delete pEntry; + return result; +} + +/* + * Copy all of the bytes in "src" to "dst". + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the data. + */ +status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32) +{ + unsigned char tmpBuf[32768]; + size_t count; + + *pCRC32 = crc32(0L, Z_NULL, 0); + + while (1) { + count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); + if (ferror(srcFp) || ferror(dstFp)) + return errnoToStatus(errno); + if (count == 0) + break; + + *pCRC32 = crc32(*pCRC32, tmpBuf, count); + + if (fwrite(tmpBuf, 1, count, dstFp) != count) { + LOGD("fwrite %d bytes failed\n", (int) count); + return UNKNOWN_ERROR; + } + } + + return NO_ERROR; +} + +/* + * Copy all of the bytes in "src" to "dst". + * + * On exit, "dstFp" will be seeked immediately past the data. + */ +status_t ZipFile::copyDataToFp(FILE* dstFp, + const void* data, size_t size, unsigned long* pCRC32) +{ + size_t count; + + *pCRC32 = crc32(0L, Z_NULL, 0); + if (size > 0) { + *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); + if (fwrite(data, 1, size, dstFp) != size) { + LOGD("fwrite %d bytes failed\n", (int) size); + return UNKNOWN_ERROR; + } + } + + return NO_ERROR; +} + +/* + * Copy some of the bytes in "src" to "dst". + * + * If "pCRC32" is NULL, the CRC will not be computed. + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the data just written. + */ +status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, + unsigned long* pCRC32) +{ + unsigned char tmpBuf[32768]; + size_t count; + + if (pCRC32 != NULL) + *pCRC32 = crc32(0L, Z_NULL, 0); + + while (length) { + long readSize; + + readSize = sizeof(tmpBuf); + if (readSize > length) + readSize = length; + + count = fread(tmpBuf, 1, readSize, srcFp); + if ((long) count != readSize) { // error or unexpected EOF + LOGD("fread %d bytes failed\n", (int) readSize); + return UNKNOWN_ERROR; + } + + if (pCRC32 != NULL) + *pCRC32 = crc32(*pCRC32, tmpBuf, count); + + if (fwrite(tmpBuf, 1, count, dstFp) != count) { + LOGD("fwrite %d bytes failed\n", (int) count); + return UNKNOWN_ERROR; + } + + length -= readSize; + } + + return NO_ERROR; +} + +/* + * Compress all of the data in "srcFp" and write it to "dstFp". + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the compressed data. + */ +status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, + const void* data, size_t size, unsigned long* pCRC32) +{ + status_t result = NO_ERROR; + const size_t kBufSize = 32768; + unsigned char* inBuf = NULL; + unsigned char* outBuf = NULL; + z_stream zstream; + bool atEof = false; // no feof() aviailable yet + unsigned long crc; + int zerr; + + /* + * Create an input buffer and an output buffer. + */ + inBuf = new unsigned char[kBufSize]; + outBuf = new unsigned char[kBufSize]; + if (inBuf == NULL || outBuf == NULL) { + result = NO_MEMORY; + goto bail; + } + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + zstream.data_type = Z_UNKNOWN; + + zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (zerr != Z_OK) { + result = UNKNOWN_ERROR; + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + crc = crc32(0L, Z_NULL, 0); + + /* + * Loop while we have data. + */ + do { + size_t getSize; + int flush; + + /* only read if the input buffer is empty */ + if (zstream.avail_in == 0 && !atEof) { + LOGV("+++ reading %d bytes\n", (int)kBufSize); + if (data) { + getSize = size > kBufSize ? kBufSize : size; + memcpy(inBuf, data, getSize); + data = ((const char*)data) + getSize; + size -= getSize; + } else { + getSize = fread(inBuf, 1, kBufSize, srcFp); + if (ferror(srcFp)) { + LOGD("deflate read failed (errno=%d)\n", errno); + goto z_bail; + } + } + if (getSize < kBufSize) { + LOGV("+++ got %d bytes, EOF reached\n", + (int)getSize); + atEof = true; + } + + crc = crc32(crc, inBuf, getSize); + + zstream.next_in = inBuf; + zstream.avail_in = getSize; + } + + if (atEof) + flush = Z_FINISH; /* tell zlib that we're done */ + else + flush = Z_NO_FLUSH; /* more to come! */ + + zerr = deflate(&zstream, flush); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib deflate call failed (zerr=%d)\n", zerr); + result = UNKNOWN_ERROR; + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize)) + { + LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf)); + if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) != + (size_t)(zstream.next_out - outBuf)) + { + LOGD("write %d failed in deflate\n", + (int) (zstream.next_out - outBuf)); + goto z_bail; + } + + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + *pCRC32 = crc; + +z_bail: + deflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] inBuf; + delete[] outBuf; + + return result; +} + +/* + * Mark an entry as deleted. + * + * We will eventually need to crunch the file down, but if several files + * are being removed (perhaps as part of an "update" process) we can make + * things considerably faster by deferring the removal to "flush" time. + */ +status_t ZipFile::remove(ZipEntry* pEntry) +{ + /* + * Should verify that pEntry is actually part of this archive, and + * not some stray ZipEntry from a different file. + */ + + /* mark entry as deleted, and mark archive as dirty */ + pEntry->setDeleted(); + mNeedCDRewrite = true; + return NO_ERROR; +} + +/* + * Flush any pending writes. + * + * In particular, this will crunch out deleted entries, and write the + * Central Directory and EOCD if we have stomped on them. + */ +status_t ZipFile::flush(void) +{ + status_t result = NO_ERROR; + long eocdPosn; + int i, count; + + if (mReadOnly) + return INVALID_OPERATION; + if (!mNeedCDRewrite) + return NO_ERROR; + + assert(mZipFp != NULL); + + result = crunchArchive(); + if (result != NO_ERROR) + return result; + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) + return UNKNOWN_ERROR; + + count = mEntries.size(); + for (i = 0; i < count; i++) { + ZipEntry* pEntry = mEntries[i]; + pEntry->mCDE.write(mZipFp); + } + + eocdPosn = ftell(mZipFp); + mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; + + mEOCD.write(mZipFp); + + /* + * If we had some stuff bloat up during compression and get replaced + * with plain files, or if we deleted some entries, there's a lot + * of wasted space at the end of the file. Remove it now. + */ + if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { + LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); + // not fatal + } + + /* should we clear the "newly added" flag in all entries now? */ + + mNeedCDRewrite = false; + return NO_ERROR; +} + +/* + * Crunch deleted files out of an archive by shifting the later files down. + * + * Because we're not using a temp file, we do the operation inside the + * current file. + */ +status_t ZipFile::crunchArchive(void) +{ + status_t result = NO_ERROR; + int i, count; + long delCount, adjust; + +#if 0 + printf("CONTENTS:\n"); + for (i = 0; i < (int) mEntries.size(); i++) { + printf(" %d: lfhOff=%ld del=%d\n", + i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); + } + printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); +#endif + + /* + * Roll through the set of files, shifting them as appropriate. We + * could probably get a slight performance improvement by sliding + * multiple files down at once (because we could use larger reads + * when operating on batches of small files), but it's not that useful. + */ + count = mEntries.size(); + delCount = adjust = 0; + for (i = 0; i < count; i++) { + ZipEntry* pEntry = mEntries[i]; + long span; + + if (pEntry->getLFHOffset() != 0) { + long nextOffset; + + /* Get the length of this entry by finding the offset + * of the next entry. Directory entries don't have + * file offsets, so we need to find the next non-directory + * entry. + */ + nextOffset = 0; + for (int ii = i+1; nextOffset == 0 && ii < count; ii++) + nextOffset = mEntries[ii]->getLFHOffset(); + if (nextOffset == 0) + nextOffset = mEOCD.mCentralDirOffset; + span = nextOffset - pEntry->getLFHOffset(); + + assert(span >= ZipEntry::LocalFileHeader::kLFHLen); + } else { + /* This is a directory entry. It doesn't have + * any actual file contents, so there's no need to + * move anything. + */ + span = 0; + } + + //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", + // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); + + if (pEntry->getDeleted()) { + adjust += span; + delCount++; + + delete pEntry; + mEntries.removeAt(i); + + /* adjust loop control */ + count--; + i--; + } else if (span != 0 && adjust > 0) { + /* shuffle this entry back */ + //printf("+++ Shuffling '%s' back %ld\n", + // pEntry->getFileName(), adjust); + result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, + pEntry->getLFHOffset(), span); + if (result != NO_ERROR) { + /* this is why you use a temp file */ + LOGE("error during crunch - archive is toast\n"); + return result; + } + + pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); + } + } + + /* + * Fix EOCD info. We have to wait until the end to do some of this + * because we use mCentralDirOffset to determine "span" for the + * last entry. + */ + mEOCD.mCentralDirOffset -= adjust; + mEOCD.mNumEntries -= delCount; + mEOCD.mTotalNumEntries -= delCount; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + + assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); + assert(mEOCD.mNumEntries == count); + + return result; +} + +/* + * Works like memmove(), but on pieces of a file. + */ +status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) +{ + if (dst == src || n <= 0) + return NO_ERROR; + + unsigned char readBuf[32768]; + + if (dst < src) { + /* shift stuff toward start of file; must read from start */ + while (n != 0) { + size_t getSize = sizeof(readBuf); + if (getSize > n) + getSize = n; + + if (fseek(fp, (long) src, SEEK_SET) != 0) { + LOGD("filemove src seek %ld failed\n", (long) src); + return UNKNOWN_ERROR; + } + + if (fread(readBuf, 1, getSize, fp) != getSize) { + LOGD("filemove read %ld off=%ld failed\n", + (long) getSize, (long) src); + return UNKNOWN_ERROR; + } + + if (fseek(fp, (long) dst, SEEK_SET) != 0) { + LOGD("filemove dst seek %ld failed\n", (long) dst); + return UNKNOWN_ERROR; + } + + if (fwrite(readBuf, 1, getSize, fp) != getSize) { + LOGD("filemove write %ld off=%ld failed\n", + (long) getSize, (long) dst); + return UNKNOWN_ERROR; + } + + src += getSize; + dst += getSize; + n -= getSize; + } + } else { + /* shift stuff toward end of file; must read from end */ + assert(false); // write this someday, maybe + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + + +/* + * Get the modification time from a file descriptor. + */ +time_t ZipFile::getModTime(int fd) +{ + struct stat sb; + + if (fstat(fd, &sb) < 0) { + LOGD("HEY: fstat on fd %d failed\n", fd); + return (time_t) -1; + } + + return sb.st_mtime; +} + + +#if 0 /* this is a bad idea */ +/* + * Get a copy of the Zip file descriptor. + * + * We don't allow this if the file was opened read-write because we tend + * to leave the file contents in an uncertain state between calls to + * flush(). The duplicated file descriptor should only be valid for reads. + */ +int ZipFile::getZipFd(void) const +{ + if (!mReadOnly) + return INVALID_OPERATION; + assert(mZipFp != NULL); + + int fd; + fd = dup(fileno(mZipFp)); + if (fd < 0) { + LOGD("didn't work, errno=%d\n", errno); + } + + return fd; +} +#endif + + +#if 0 +/* + * Expand data. + */ +bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const +{ + return false; +} +#endif + +// free the memory when you're done +void* ZipFile::uncompress(const ZipEntry* entry) +{ + size_t unlen = entry->getUncompressedLen(); + size_t clen = entry->getCompressedLen(); + + void* buf = malloc(unlen); + if (buf == NULL) { + return NULL; + } + + fseek(mZipFp, 0, SEEK_SET); + + off_t offset = entry->getFileOffset(); + if (fseek(mZipFp, offset, SEEK_SET) != 0) { + goto bail; + } + + switch (entry->getCompressionMethod()) + { + case ZipEntry::kCompressStored: { + ssize_t amt = fread(buf, 1, unlen, mZipFp); + if (amt != (ssize_t)unlen) { + goto bail; + } +#if 0 + printf("data...\n"); + const unsigned char* p = (unsigned char*)buf; + const unsigned char* end = p+unlen; + for (int i=0; i<32 && p < end; i++) { + printf("0x%08x ", (int)(offset+(i*0x10))); + for (int j=0; j<0x10 && p < end; j++) { + printf(" %02x", *p); + p++; + } + printf("\n"); + } +#endif + + } + break; + case ZipEntry::kCompressDeflated: { + if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { + goto bail; + } + } + break; + default: + goto bail; + } + return buf; + +bail: + free(buf); + return NULL; +} + + +/* + * =========================================================================== + * ZipFile::EndOfCentralDir + * =========================================================================== + */ + +/* + * Read the end-of-central-dir fields. + * + * "buf" should be positioned at the EOCD signature, and should contain + * the entire EOCD area including the comment. + */ +status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) +{ + /* don't allow re-use */ + assert(mComment == NULL); + + if (len < kEOCDLen) { + /* looks like ZIP file got truncated */ + LOGD(" Zip EOCD: expected >= %d bytes, found %d\n", + kEOCDLen, len); + return INVALID_OPERATION; + } + + /* this should probably be an assert() */ + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) + return UNKNOWN_ERROR; + + mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); + mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); + mNumEntries = ZipEntry::getShortLE(&buf[0x08]); + mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); + mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); + mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); + mCommentLen = ZipEntry::getShortLE(&buf[0x14]); + + // TODO: validate mCentralDirOffset + + if (mCommentLen > 0) { + if (kEOCDLen + mCommentLen > len) { + LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n", + kEOCDLen, mCommentLen, len); + return UNKNOWN_ERROR; + } + mComment = new unsigned char[mCommentLen]; + memcpy(mComment, buf + kEOCDLen, mCommentLen); + } + + return NO_ERROR; +} + +/* + * Write an end-of-central-directory section. + */ +status_t ZipFile::EndOfCentralDir::write(FILE* fp) +{ + unsigned char buf[kEOCDLen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mDiskNumber); + ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); + ZipEntry::putShortLE(&buf[0x08], mNumEntries); + ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); + ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); + ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); + ZipEntry::putShortLE(&buf[0x14], mCommentLen); + + if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) + return UNKNOWN_ERROR; + if (mCommentLen > 0) { + assert(mComment != NULL); + if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +/* + * Dump the contents of an EndOfCentralDir object. + */ +void ZipFile::EndOfCentralDir::dump(void) const +{ + LOGD(" EndOfCentralDir contents:\n"); + LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n", + mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); + LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n", + mCentralDirSize, mCentralDirOffset, mCommentLen); +} + diff --git a/tools/zipalign/ZipFile.h b/tools/zipalign/ZipFile.h new file mode 100644 index 0000000..dbbd072 --- /dev/null +++ b/tools/zipalign/ZipFile.h @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// General-purpose Zip archive access. This class allows both reading and +// writing to Zip archives, including deletion of existing entries. +// +#ifndef __LIBS_ZIPFILE_H +#define __LIBS_ZIPFILE_H + +#include <utils/Vector.h> +#include <utils/Errors.h> +#include <stdio.h> + +#include "ZipEntry.h" + +namespace android { + +/* + * Manipulate a Zip archive. + * + * Some changes will not be visible in the until until "flush" is called. + * + * The correct way to update a file archive is to make all changes to a + * copy of the archive in a temporary file, and then unlink/rename over + * the original after everything completes. Because we're only interested + * in using this for packaging, we don't worry about such things. Crashing + * after making changes and before flush() completes could leave us with + * an unusable Zip archive. + */ +class ZipFile { +public: + ZipFile(void) + : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false) + {} + ~ZipFile(void) { + if (!mReadOnly) + flush(); + if (mZipFp != NULL) + fclose(mZipFp); + discardEntries(); + } + + /* + * Open a new or existing archive. + */ + typedef enum { + kOpenReadOnly = 0x01, + kOpenReadWrite = 0x02, + kOpenCreate = 0x04, // create if it doesn't exist + kOpenTruncate = 0x08, // if it exists, empty it + }; + status_t open(const char* zipFileName, int flags); + + /* + * Add a file to the end of the archive. Specify whether you want the + * library to try to store it compressed. + * + * If "storageName" is specified, the archive will use that instead + * of "fileName". + * + * If there is already an entry with the same name, the call fails. + * Existing entries with the same name must be removed first. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const char* fileName, int compressionMethod, + ZipEntry** ppEntry) + { + return add(fileName, fileName, compressionMethod, ppEntry); + } + status_t add(const char* fileName, const char* storageName, + int compressionMethod, ZipEntry** ppEntry) + { + return addCommon(fileName, NULL, 0, storageName, + ZipEntry::kCompressStored, + compressionMethod, ppEntry); + } + + /* + * Add a file that is already compressed with gzip. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t addGzip(const char* fileName, const char* storageName, + ZipEntry** ppEntry) + { + return addCommon(fileName, NULL, 0, storageName, + ZipEntry::kCompressDeflated, + ZipEntry::kCompressDeflated, ppEntry); + } + + /* + * Add a file from an in-memory data buffer. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const void* data, size_t size, const char* storageName, + int compressionMethod, ZipEntry** ppEntry) + { + return addCommon(NULL, data, size, storageName, + ZipEntry::kCompressStored, + compressionMethod, ppEntry); + } + + /* + * Add an entry by copying it from another zip file. If "padding" is + * nonzero, the specified number of bytes will be added to the "extra" + * field in the header. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, + int padding, ZipEntry** ppEntry); + + /* + * Mark an entry as having been removed. It is not actually deleted + * from the archive or our internal data structures until flush() is + * called. + */ + status_t remove(ZipEntry* pEntry); + + /* + * Flush changes. If mNeedCDRewrite is set, this writes the central dir. + */ + status_t flush(void); + + /* + * Expand the data into the buffer provided. The buffer must hold + * at least <uncompressed len> bytes. Variation expands directly + * to a file. + * + * Returns "false" if an error was encountered in the compressed data. + */ + //bool uncompress(const ZipEntry* pEntry, void* buf) const; + //bool uncompress(const ZipEntry* pEntry, FILE* fp) const; + void* uncompress(const ZipEntry* pEntry); + + /* + * Get an entry, by name. Returns NULL if not found. + * + * Does not return entries pending deletion. + */ + ZipEntry* getEntryByName(const char* fileName) const; + + /* + * Get the Nth entry in the archive. + * + * This will return an entry that is pending deletion. + */ + int getNumEntries(void) const { return mEntries.size(); } + ZipEntry* getEntryByIndex(int idx) const; + +private: + /* these are private and not defined */ + ZipFile(const ZipFile& src); + ZipFile& operator=(const ZipFile& src); + + class EndOfCentralDir { + public: + EndOfCentralDir(void) : + mDiskNumber(0), + mDiskWithCentralDir(0), + mNumEntries(0), + mTotalNumEntries(0), + mCentralDirSize(0), + mCentralDirOffset(0), + mCommentLen(0), + mComment(NULL) + {} + virtual ~EndOfCentralDir(void) { + delete[] mComment; + } + + status_t readBuf(const unsigned char* buf, int len); + status_t write(FILE* fp); + + //unsigned long mSignature; + unsigned short mDiskNumber; + unsigned short mDiskWithCentralDir; + unsigned short mNumEntries; + unsigned short mTotalNumEntries; + unsigned long mCentralDirSize; + unsigned long mCentralDirOffset; // offset from first disk + unsigned short mCommentLen; + unsigned char* mComment; + + enum { + kSignature = 0x06054b50, + kEOCDLen = 22, // EndOfCentralDir len, excl. comment + + kMaxCommentLen = 65535, // longest possible in ushort + kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen, + + }; + + void dump(void) const; + }; + + + /* read all entries in the central dir */ + status_t readCentralDir(void); + + /* crunch deleted entries out */ + status_t crunchArchive(void); + + /* clean up mEntries */ + void discardEntries(void); + + /* common handler for all "add" functions */ + status_t addCommon(const char* fileName, const void* data, size_t size, + const char* storageName, int sourceType, int compressionMethod, + ZipEntry** ppEntry); + + /* copy all of "srcFp" into "dstFp" */ + status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32); + /* copy all of "data" into "dstFp" */ + status_t copyDataToFp(FILE* dstFp, + const void* data, size_t size, unsigned long* pCRC32); + /* copy some of "srcFp" into "dstFp" */ + status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, + unsigned long* pCRC32); + /* like memmove(), but on parts of a single file */ + status_t filemove(FILE* fp, off_t dest, off_t src, size_t n); + /* compress all of "srcFp" into "dstFp", using Deflate */ + status_t compressFpToFp(FILE* dstFp, FILE* srcFp, + const void* data, size_t size, unsigned long* pCRC32); + + /* get modification date from a file descriptor */ + time_t getModTime(int fd); + + /* + * We use stdio FILE*, which gives us buffering but makes dealing + * with files >2GB awkward. Until we support Zip64, we're fine. + */ + FILE* mZipFp; // Zip file pointer + + /* one of these per file */ + EndOfCentralDir mEOCD; + + /* did we open this read-only? */ + bool mReadOnly; + + /* set this when we trash the central dir */ + bool mNeedCDRewrite; + + /* + * One ZipEntry per entry in the zip file. I'm using pointers instead + * of objects because it's easier than making operator= work for the + * classes and sub-classes. + */ + Vector<ZipEntry*> mEntries; +}; + +}; // namespace android + +#endif // __LIBS_ZIPFILE_H |