AI 145655: am: CL 145618 am: CL 145333 Cloned from CL 144759 by 'g4 patch'.
  Original change by joeo@abreu on 2009/04/06 19:54:13.
  Implement SDK add-ons in the build system.
  - Add an option to use the standard javadoc doclet instead
  of droiddoc, since droiddocs non-sdk templates aren't
  ready for prime time.
  - Add the notion of a stubs for a library.  It's only
  implemented for java libraries, but when we do native
  libraries in the NDK or sdk-addons, it will work there too.
  Original author: joeo
  Merged from: //branches/cupcake/...
  Original author: android-build

Automated import of CL 145655
diff --git a/core/Makefile b/core/Makefile
index cd96630..a79d7b4 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -15,9 +15,9 @@
 # <dest file> is relative to $(PRODUCT_OUT), so it should look like,
 # e.g., "system/etc/file.xml".
 $(foreach cf,$(PRODUCT_COPY_FILES), \
-  $(eval _w := $(subst :,$(space),$(cf))) \
-  $(eval _src := $(word 1,$(_w))) \
-  $(eval _dest := $(subst //,/,$(PRODUCT_OUT)/$(word 2,$(_w)))) \
+  $(eval _src := $(call word-colon,1,$(cf))) \
+  $(eval _dest := $(call \
+          append-path,$(PRODUCT_OUT),$(call word-colon,2,$(cf)))) \
   $(eval $(call copy-one-file,$(_src),$(_dest))) \
   $(eval ALL_DEFAULT_INSTALLED_MODULES += $(_dest)) \
  )
@@ -243,7 +243,10 @@
 	$(ALL_GENERATED_SOURCES) \
 	$(ALL_DEFAULT_INSTALLED_MODULES))
 
-INSTALLED_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img
+BUILT_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img
+
+# We just build this directly to the install location.
+INSTALLED_RAMDISK_TARGET := $(BUILT_RAMDISK_TARGET)
 $(INSTALLED_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_RAMDISK_FILES)
 	$(call pretty,"Target ram disk: $@")
 	$(hide) $(MKBOOTFS) $(TARGET_ROOT_OUT) | gzip > $@
@@ -575,7 +578,10 @@
 endef
 endif # TARGET_USERIMAGES_USE_EXT2
 
-INSTALLED_USERDATAIMAGE_TARGET := $(PRODUCT_OUT)/userdata.img
+BUILT_USERDATAIMAGE_TARGET := $(PRODUCT_OUT)/userdata.img
+
+# We just build this directly to the install location.
+INSTALLED_USERDATAIMAGE_TARGET := $(BUILT_USERDATAIMAGE_TARGET)
 $(INSTALLED_USERDATAIMAGE_TARGET): $(INTERNAL_MKUSERFS) \
                                    $(INTERNAL_USERDATAIMAGE_FILES)
 	$(build-userdataimage-target)
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index f090507..b2e95b4 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -49,6 +49,7 @@
 LOCAL_JAVA_LIBRARIES:=
 LOCAL_NO_STANDARD_LIBRARIES:=
 LOCAL_CLASSPATH:=
+LOCAL_DROIDDOC_USE_STANDARD_DOCLET:=
 LOCAL_DROIDDOC_SOURCE_PATH:=
 LOCAL_DROIDDOC_TEMPLATE_DIR:=
 LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=
diff --git a/core/definitions.mk b/core/definitions.mk
index 3efef8d..17ec646 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -96,6 +96,17 @@
 endef
 
 ###########################################################
+## Evaluates to true if the string contains the word true,
+## and empty otherwise
+## $(1): a var to test
+###########################################################
+
+define true-or-empty
+$(filter true, $(1))
+endef
+
+
+###########################################################
 ## Retrieve the directory of the current makefile
 ###########################################################
 
@@ -406,6 +417,31 @@
 endef
 
 ###########################################################
+## Convert a list of short modules names (e.g., "framework", "Browser")
+## into the list of files that should be used when linking
+## against that module as a public API.
+## TODO: Allow this for more than JAVA_LIBRARIES modules
+## NOTE: this won't return reliable results until after all
+## sub-makefiles have been included.
+## $(1): target list
+###########################################################
+
+define module-stubs-files
+$(foreach module,$(1),$(ALL_MODULES.$(module).STUBS))
+endef
+
+###########################################################
+## Evaluates to the timestamp file for a doc module, which
+## is the dependency that should be used.
+## $(1): doc module
+###########################################################
+
+define doc-timestamp-for
+$(OUT_DOCS)/$(strip $(1))-timestamp
+endef
+
+
+###########################################################
 ## Convert "framework framework-res ext" to "out/.../javalib.jar ..."
 ## This lets us treat framework-res as a normal library.
 ## $(1): library list
@@ -465,6 +501,21 @@
 endef
 
 ###########################################################
+## Read the word out of a colon-separated list of words.
+## This has the same behavior as the built-in function
+## $(word n,str).
+##
+## The individual words may not contain spaces.
+##
+## $(1): 1 based index
+## $(2): value of the form a:b:c...
+###########################################################
+
+define word-colon
+$(word $(1),$(subst :,$(space),$(2)))
+endef
+
+###########################################################
 ## Convert "a=b c= d e = f" into "a=b c=d e=f"
 ##
 ## $(1): list to collapse
@@ -478,7 +529,6 @@
     $(subst $(_cpSEP), $(_cpSEP) ,$(1))))
 endef
 
-
 ###########################################################
 ## MODULE_TAG set operations
 ###########################################################
@@ -510,6 +560,18 @@
 	    $(call modules-for-tag-list,$(1)))
 endef
 
+###########################################################
+## Append a leaf to a base path.  Properly deals with
+## base paths ending in /.
+##
+## $(1): base path
+## $(2): leaf path
+###########################################################
+
+define append-path
+$(subst //,/,$(1)/$(2))
+endef
+
 
 ###########################################################
 ## Package filtering
@@ -1117,17 +1179,18 @@
   done
 endef
 
-# below we write the list of java files to java-source-list to avoid argument list length problems with Cygwin
-# we filter out duplicate java file names because eclipse's compiler doesn't like them.
+# below we write the list of java files to java-source-list to avoid argument
+# list length problems with Cygwin we filter out duplicate java file names
+# because eclipse's compiler doesn't like them.
 define transform-java-to-classes.jar
 @echo "target Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))"
-@rm -f $@
-@rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
-@mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR)
+$(hide) rm -f $@
+$(hide) rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
+$(hide) mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR)
 $(call unzip-jar-files,$(PRIVATE_STATIC_JAVA_LIBRARIES), \
     $(PRIVATE_CLASS_INTERMEDIATES_DIR))
 $(call dump-words-to-file,$(PRIVATE_JAVA_SOURCES),$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list)
-@if [ -d "$(PRIVATE_SOURCE_INTERMEDIATES_DIR)" ]; then \
+$(hide) if [ -d "$(PRIVATE_SOURCE_INTERMEDIATES_DIR)" ]; then \
 	    find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list; \
 fi
 $(hide) tr ' ' '\n' < $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list \
@@ -1139,12 +1202,12 @@
     -extdirs "" -d $(PRIVATE_CLASS_INTERMEDIATES_DIR) \
     \@$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq \
     || ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 )
-@ rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list
-@ rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq
-@mkdir -p $(dir $@)
+$(hide) rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list
+$(hide) rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq
+$(hide) mkdir -p $(dir $@)
 $(hide) jar $(if $(strip $(PRIVATE_JAR_MANIFEST)),-cfm,-cf) \
     $@ $(PRIVATE_JAR_MANIFEST) -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .
-@rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
+$(hide) rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
 endef
 
 define transform-classes.jar-to-emma
diff --git a/core/droiddoc.mk b/core/droiddoc.mk
index a279c82..f6a8114 100644
--- a/core/droiddoc.mk
+++ b/core/droiddoc.mk
@@ -1,9 +1,27 @@
-###########################################################
-## Standard rules for building documentation
-###########################################################
+#
+# 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.
+#
 
-LOCAL_IS_HOST_MODULE := $(strip $(LOCAL_IS_HOST_MODULE))
-ifdef LOCAL_IS_HOST_MODULE
+##
+##
+## Common to both droiddoc and javadoc
+##
+##
+
+LOCAL_IS_HOST_MODULE := $(call true-or-empty,$(LOCAL_IS_HOST_MODULE))
+ifeq ($(LOCAL_IS_HOST_MODULE),true)
 my_prefix:=HOST_
 else
 my_prefix:=TARGET_
@@ -16,7 +34,7 @@
 
 full_src_files := $(patsubst %,$(LOCAL_PATH)/%,$(LOCAL_SRC_FILES))
 out_dir := $(OUT_DOCS)/$(LOCAL_MODULE)
-full_target := $(OUT_DOCS)/$(LOCAL_MODULE)-timestamp
+full_target := $(call doc-timestamp-for,$(LOCAL_MODULE))
 
 ifeq ($(LOCAL_DROIDDOC_SOURCE_PATH),)
 LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)
@@ -36,22 +54,10 @@
 LOCAL_DROIDDOC_CUSTOM_ASSET_DIR := assets
 endif
 
-droiddoc_templates := \
-    $(shell find $(LOCAL_DROIDDOC_TEMPLATE_DIR) -type f) \
-    $(shell find $(LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR) -type f)
-
-droiddoc := \
-	$(HOST_JDK_TOOLS_JAR) \
-	$(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX) \
-	$(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX) \
-	$(HOST_OUT_SHARED_LIBRARIES)/libclearsilver-jni$(HOST_JNILIB_SUFFIX)
-
-intermediates := $(call local-intermediates-dir)
-
 $(full_target): PRIVATE_CLASSPATH:=$(LOCAL_CLASSPATH)
 full_java_lib_deps :=
 
-ifndef LOCAL_IS_HOST_MODULE
+ifneq ($(LOCAL_IS_HOST_MODULE),true)
 
 ifeq ($(LOCAL_JAVA_LIBRARIES),)
 LOCAL_JAVA_LIBRARIES := core ext framework
@@ -72,13 +78,59 @@
 
 endif # !LOCAL_IS_HOST_MODULE
 
-$(full_target): PRIVATE_DOCLETPATH := $(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX)
+intermediates := $(call local-intermediates-dir)
+
+$(full_target): PRIVATE_SOURCE_PATH := $(call normalize-path-list,$(LOCAL_DROIDDOC_SOURCE_PATH))
 $(full_target): PRIVATE_JAVA_FILES := $(filter %.java,$(full_src_files))
 $(full_target): PRIVATE_JAVA_FILES += $(addprefix $($(my_prefix)OUT_COMMON_INTERMEDIATES)/, $(filter %.java,$(LOCAL_INTERMEDIATE_SOURCES)))
-$(full_target): PRIVATE_CURRENT_BUILD := -hdf page.build $(BUILD_ID)-$(BUILD_NUMBER)
-$(full_target): PRIVATE_CURRENT_TIME :=  -hdf page.now "$(shell date "+%d %b %Y %k:%M")"
+$(full_target): PRIVATE_SOURCE_INTERMEDIATES_DIR := $(intermediates)/src
+$(full_target): PRIVATE_SRC_LIST_FILE := $(intermediates)/droiddoc-src-list
+
+ifneq ($(strip $(LOCAL_ADDITIONAL_JAVA_DIR)),)
+$(full_target): PRIVATE_ADDITIONAL_JAVA_DIR := $(LOCAL_ADDITIONAL_JAVA_DIR)
+endif
+
 $(full_target): PRIVATE_OUT_DIR := $(out_dir)
 $(full_target): PRIVATE_DROIDDOC_OPTIONS := $(LOCAL_DROIDDOC_OPTIONS)
+
+# Lists the input files for the doc build into a text file
+# suitable for the @ syntax of javadoc.
+# $(1): the file to create
+# $(2): files to include
+# $(3): list of directories to search for java files in
+define prepare-doc-source-list
+$(hide) mkdir -p $(dir $(1))
+$(hide) echo $(2)
+$(call dump-words-to-file, $(2), $(1))
+$(hide) for d in $(3) ; do find $$d -name '*.java' >> $(1) 2> /dev/null ; done ; true
+endef
+
+ifeq (a,b)
+$(full_target): PRIVATE_PROFILING_OPTIONS := \
+    -J-agentlib:jprofilerti=port=8849 -J-Xbootclasspath/a:/Applications/jprofiler5/bin/agent.jar
+endif
+
+
+ifneq ($(strip $(LOCAL_DROIDDOC_USE_STANDARD_DOCLET)),true)
+##
+##
+## droiddoc only
+##
+##
+
+droiddoc_templates := \
+    $(shell find $(LOCAL_DROIDDOC_TEMPLATE_DIR) -type f) \
+    $(shell find $(LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR) -type f)
+
+droiddoc := \
+	$(HOST_JDK_TOOLS_JAR) \
+	$(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX) \
+	$(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX) \
+	$(HOST_OUT_SHARED_LIBRARIES)/libclearsilver-jni$(HOST_JNILIB_SUFFIX)
+
+$(full_target): PRIVATE_DOCLETPATH := $(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(full_target): PRIVATE_CURRENT_BUILD := -hdf page.build $(BUILD_ID)-$(BUILD_NUMBER)
+$(full_target): PRIVATE_CURRENT_TIME :=  -hdf page.now "$(shell date "+%d %b %Y %k:%M")"
 $(full_target): PRIVATE_TEMPLATE_DIR := $(LOCAL_DROIDDOC_TEMPLATE_DIR)
 $(full_target): PRIVATE_CUSTOM_TEMPLATE_DIR := $(LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR)
 $(full_target): PRIVATE_IN_ASSET_DIR := $(LOCAL_DROIDDOC_TEMPLATE_DIR)/$(LOCAL_DROIDDOC_ASSET_DIR)
@@ -90,33 +142,20 @@
 else
 $(full_target): PRIVATE_DROIDDOC_HTML_DIR := 
 endif
-$(full_target): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
-$(full_target): PRIVATE_SOURCE_PATH := $(call normalize-path-list,$(LOCAL_DROIDDOC_SOURCE_PATH))
-$(full_target): PRIVATE_SOURCE_INTERMEDIATES_DIR := $(intermediates)/src
-$(full_target): PRIVATE_SRC_LIST_FILE := $(intermediates)/droiddoc-src-list
 
-ifneq ($(strip $(LOCAL_ADDITIONAL_JAVA_DIR)),)
-$(full_target): PRIVATE_ADDITIONAL_JAVA_DIR := $(LOCAL_ADDITIONAL_JAVA_DIR)
-endif
+# TODO: not clear if this is used any more
+$(full_target): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
 
 html_dir_files := $(shell find $(LOCAL_PATH)/$(LOCAL_DROIDDOC_HTML_DIR) -type f)
 
-ifeq (a,b)
-$(full_target): PRIVATE_PROFILING_OPTIONS := \
-    -J-agentlib:jprofilerti=port=8849 -J-Xbootclasspath/a:/Applications/jprofiler5/bin/agent.jar
-endif
-
 $(full_target): $(full_src_files) $(droiddoc_templates) $(droiddoc) $(html_dir_files) $(full_java_lib_deps)
 	@echo Docs droiddoc: $(PRIVATE_OUT_DIR)
-	@mkdir -p $(dir $(full_target))
-	@mkdir -p $(dir $(PRIVATE_SRC_LIST_FILE))
-	$(call dump-words-to-file, $(PRIVATE_JAVA_FILES), $(PRIVATE_SRC_LIST_FILE))
-	$(hide) find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(PRIVATE_SRC_LIST_FILE) 2> /dev/null || true
-	$(hide) if [ "$(PRIVATE_ADDITIONAL_JAVA_DIR)" != "" ] ; then ( find $(PRIVATE_ADDITIONAL_JAVA_DIR) -name '*.java' >> $(PRIVATE_SRC_LIST_FILE) 2> /dev/null || true ) fi
+	$(hide) mkdir -p $(dir $(full_target))
+	$(call prepare-doc-source-list,$(PRIVATE_SRC_LIST_FILE),$(PRIVATE_JAVA_FILES), \
+			$(PRIVATE_SOURCE_INTERMEDIATES_DIR) $(PRIVATE_ADDITIONAL_JAVA_DIR))
 	$(hide) ( \
-        \
-            LD_LIBRARY_PATH=$(HOST_OUT_SHARED_LIBRARIES) \
-            javadoc \
+		LD_LIBRARY_PATH=$(HOST_OUT_SHARED_LIBRARIES) \
+		javadoc \
                 \@$(PRIVATE_SRC_LIST_FILE) \
                 -J-Xmx768m \
                 -J-Djava.library.path=$(HOST_OUT_SHARED_LIBRARIES) \
@@ -141,6 +180,41 @@
         && touch -f $@ \
     ) || (rm -rf $(PRIVATE_OUT_DIR) $(PRIVATE_SRC_LIST_FILE); exit 45)
 
+
+
+else
+##
+##
+## standard doclet only
+##
+##
+$(full_target): $(full_src_files) $(full_java_lib_deps)
+	@echo Docs javadoc: $(PRIVATE_OUT_DIR)
+	@mkdir -p $(dir $(full_target))
+	$(call prepare-doc-source-list,$(PRIVATE_SRC_LIST_FILE),$(PRIVATE_JAVA_FILES), \
+			$(PRIVATE_SOURCE_INTERMEDIATES_DIR) $(PRIVATE_ADDITIONAL_JAVA_DIR))
+	$(hide) ( \
+		javadoc \
+                $(PRIVATE_DROIDDOC_OPTIONS) \
+                \@$(PRIVATE_SRC_LIST_FILE) \
+                -J-Xmx768m \
+                $(PRIVATE_PROFILING_OPTIONS) \
+                $(addprefix -classpath ,$(PRIVATE_CLASSPATH)) \
+                -sourcepath $(PRIVATE_SOURCE_PATH)$(addprefix :,$(PRIVATE_CLASSPATH)) \
+                -d $(PRIVATE_OUT_DIR) \
+                -quiet \
+        && touch -f $@ \
+    ) || (rm -rf $(PRIVATE_OUT_DIR) $(PRIVATE_SRC_LIST_FILE); exit 45)
+
+
+endif
+##
+##
+## Common to both droiddoc and javadoc
+##
+##
+
+
 ALL_DOCS += $(full_target)
 
 .PHONY: $(LOCAL_MODULE)-docs
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 0c24ea9..ba93549 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -185,6 +185,7 @@
 HOST_OUT_EXECUTABLES:= $(HOST_OUT)/bin
 HOST_OUT_SHARED_LIBRARIES:= $(HOST_OUT)/lib
 HOST_OUT_JAVA_LIBRARIES:= $(HOST_OUT)/framework
+HOST_OUT_SDK_ADDON := $(HOST_OUT)/sdk_addon
 
 HOST_OUT_INTERMEDIATES := $(HOST_OUT)/obj
 HOST_OUT_HEADERS:= $(HOST_OUT_INTERMEDIATES)/include
diff --git a/core/java.mk b/core/java.mk
index b1ded8a..9150a5c 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -66,6 +66,21 @@
 # variable definitions.
 full_classes_jar := $(intermediates.COMMON)/classes.jar
 
+# Droiddoc isn't currently able to generate stubs for modules, so we're just
+# allowing it to use the classes.jar as the "stubs" that would be use to link
+# against, for the cases where someone needs the jar to link against.
+# - Use the classes.jar instead of the handful of other intermediates that
+#   we have, because it's the most processed, but still hasn't had dex run on
+#   it, so it's closest to what's on the device.
+# - This extra copy, with the dependency on LOCAL_BUILT_MODULE allows the
+#   PRIVATE_ vars to be preserved.
+full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar
+$(full_classes_stubs_jar): PRIVATE_SOURCE_FILE := $(full_classes_jar)
+$(full_classes_stubs_jar) : $(LOCAL_BUILT_MODULE) | $(ACP)
+	@echo Copying $(PRIVATE_SOURCE_FILE)
+	$(hide) $(ACP) -fp $(PRIVATE_SOURCE_FILE) $@
+ALL_MODULES.$(LOCAL_MODULE).STUBS := $(full_classes_stubs_jar)
+
 # Emma source code coverage
 ifneq ($(EMMA_INSTRUMENT),true) 
 LOCAL_NO_EMMA_INSTRUMENT := true
diff --git a/core/product.mk b/core/product.mk
index 2e85f04..adc81c3 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -64,7 +64,11 @@
     PRODUCT_PACKAGE_OVERLAYS \
     DEVICE_PACKAGE_OVERLAYS \
     PRODUCT_CONTRIBUTORS_FILE \
-    PRODUCT_TAGS
+    PRODUCT_TAGS \
+    PRODUCT_SDK_ADDON_NAME \
+    PRODUCT_SDK_ADDON_COPY_FILES \
+    PRODUCT_SDK_ADDON_COPY_MODULES \
+    PRODUCT_SDK_ADDON_DOC_MODULE
 
 define dump-product
 $(info ==== $(1) ====)\
diff --git a/core/product_config.mk b/core/product_config.mk
index e34ad02..525c9b9 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -49,6 +49,21 @@
  )
 endef
 
+###########################################################
+## List all of the files in a subdirectory in a format
+## suitable for PRODUCT_COPY_FILES and
+## PRODUCT_SDK_ADDON_COPY_FILES
+##
+## $(1): Glob to match file name
+## $(2): Source directory
+## $(3): Target base directory
+###########################################################
+
+define find-copy-subdir-files
+$(shell find $(2) -name "$(1)" | sed -E "s:($(2)/?(.*)):\\1\\:$(3)/\\2:" | sed "s://:/:g")
+endef
+
+# ---------------------------------------------------------------
 
 # These are the valid values of TARGET_BUILD_VARIANT.  Also, if anything else is passed
 # as the variant in the PRODUCT-$TARGET_BUILD_PRODUCT-$TARGET_BUILD_VARIANT form,
diff --git a/core/tasks/sdk-addon.mk b/core/tasks/sdk-addon.mk
new file mode 100644
index 0000000..e0b4c70
--- /dev/null
+++ b/core/tasks/sdk-addon.mk
@@ -0,0 +1,87 @@
+# 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.
+
+
+# If they didn't define PRODUCT_SDK_ADDON_NAME, then we won't define
+# any of these rules.
+addon_name := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SDK_ADDON_NAME))
+ifneq ($(addon_name),)
+
+intermediates := $(HOST_OUT_INTERMEDIATES)/SDK_ADDON/$(addon_name)_intermediates
+full_target := $(HOST_OUT_SDK_ADDON)/$(addon_name).zip
+staging := $(intermediates)/$(addon_name)
+
+sdk_addon_deps :=
+files_to_copy :=
+
+# Files that are built and then copied into the sdk-addon
+ifneq ($(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SDK_ADDON_COPY_MODULES)),)
+$(foreach cf,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SDK_ADDON_COPY_MODULES), \
+  $(eval _src := $(call module-stubs-files,$(call word-colon,1,$(cf)))) \
+  $(if $(_src),,$(eval $(error Unknown or unlinkable module: $(call word-colon,1,$(cf)). Requested by $(INTERNAL_PRODUCT)))) \
+  $(eval _dest := $(call word-colon,2,$(cf))) \
+  $(eval files_to_copy += $(_src):$(_dest)) \
+ )
+endif
+
+# Files that are copied directly into the sdk-addon
+files_to_copy += $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SDK_ADDON_COPY_FILES)
+
+# All SDK add-ons have these files
+files_to_copy += \
+        $(BUILT_SYSTEMIMAGE):images/system.img \
+        $(BUILT_USERDATAIMAGE_TARGET):images/userdata.img \
+        $(BUILT_RAMDISK_TARGET):images/ramdisk.img \
+        $(target_notice_file_txt):images/NOTICE.txt
+
+# Generate rules to copy the requested files
+$(foreach cf,$(files_to_copy), \
+  $(eval _src := $(call word-colon,1,$(cf))) \
+  $(eval _dest := $(call append-path,$(staging),$(call word-colon,2,$(cf)))) \
+  $(eval $(call copy-one-file,$(_src),$(_dest))) \
+  $(eval sdk_addon_deps += $(_dest)) \
+ )
+
+# We don't know about all of the docs files, so depend on the timestamp for
+# that, and record the directory, and the packaging rule will just copy the
+# whole thing.
+doc_module := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SDK_ADDON_DOC_MODULE))
+ifneq ($(doc_module),)
+  doc_timestamp := $(call doc-timestamp-for, $(doc_module))
+  sdk_addon_deps += $(doc_timestamp)
+  $(full_target): PRIVATE_DOCS_DIR := $(OUT_DOCS)/$(doc_module)
+else
+  $(full_target): PRIVATE_DOCS_DIR :=
+endif
+
+$(full_target): PRIVATE_STAGING_DIR := $(staging)
+
+$(full_target): $(sdk_addon_deps) | $(ACP)
+	@echo Packaging SDK Addon: $@
+	$(hide) mkdir -p $(PRIVATE_STAGING_DIR)/docs/reference
+	$(hide) if [ -n "$(PRIVATE_DOCS_DIR)" ] ; then \
+	    $(ACP) -r $(PRIVATE_DOCS_DIR)/* $(PRIVATE_STAGING_DIR)/docs/reference ;\
+	  fi
+	$(hide) mkdir -p $(dir $@)
+	$(hide) ( F=$$(pwd)/$@ ; cd $(PRIVATE_STAGING_DIR) && zip -rq $$F * )
+
+.PHONY: sdk_addon
+sdk_addon: $(full_target)
+
+else # addon_name
+ifneq ($(filter sdk_addon,$(MAKECMDGOALS)),)
+$(error Trying to build sdk_addon, but product '$(INTERNAL_PRODUCT)' does not define one)
+endif
+endif # addon_name
+