new search results layout. no more custom suggestions; just API matches and organic results. am: 38b0e22
am: 4f9a67d

* commit '4f9a67dbb37abda3d6ccea540c4fa827141ca2dc':

Change-Id: I9225b324395c0ecf0de29a5b955cf2f335615105
diff --git a/core/Makefile b/core/Makefile
index 22dcee1..07d1c94 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -523,14 +523,14 @@
 $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(VBOOT_SIGNER)
 	$(call pretty,"Target boot image: $@")
 	$(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@.unsigned
-	$(VBOOT_SIGNER) $(FUTILITY) $@.unsigned $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbpubk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbprivk $@.keyblock $@
+	$(VBOOT_SIGNER) $(FUTILITY) $@.unsigned $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbpubk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbprivk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_SUBKEY).vbprivk $@.keyblock $@
 	$(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE))
 
 .PHONY: bootimage-nodeps
 bootimage-nodeps: $(MKBOOTIMG) $(VBOOT_SIGNER)
 	@echo "make $@: ignoring dependencies"
 	$(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_BOOTIMAGE_TARGET).unsigned
-	$(VBOOT_SIGNER) $(FUTILITY) $(INSTALLED_BOOTIMAGE_TARGET).unsigned $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbpubk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbprivk $(INSTALLED_BOOTIMAGE_TARGET).keyblock $(INSTALLED_BOOTIMAGE_TARGET)
+	$(VBOOT_SIGNER) $(FUTILITY) $(INSTALLED_BOOTIMAGE_TARGET).unsigned $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbpubk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbprivk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_SUBKEY).vbprivk $(INSTALLED_BOOTIMAGE_TARGET).keyblock $(INSTALLED_BOOTIMAGE_TARGET)
 	$(hide) $(call assert-max-image-size,$(INSTALLED_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE))
 
 else # PRODUCT_SUPPORTS_VBOOT != true
@@ -757,6 +757,7 @@
 $(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VENDOR_VERITY_PARTITION),$(hide) echo "vendor_verity_block_device=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VENDOR_VERITY_PARTITION)" >> $(1))
 $(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT)" >> $(1))
 $(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot_key=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY)" >> $(1))
+$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot_subkey=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_SUBKEY)" >> $(1))
 $(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "futility=$(FUTILITY)" >> $(1))
 $(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot_signer_cmd=$(VBOOT_SIGNER)" >> $(1))
 $(if $(filter true,$(BOARD_BUILD_SYSTEM_ROOT_IMAGE)),\
@@ -893,7 +894,7 @@
   $(if $(filter true,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_BOOT_SIGNER)),\
     $(BOOT_SIGNER) /recovery $(1) $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).pk8 $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $(1))
   $(if $(filter true,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT)), \
-    $(VBOOT_SIGNER) $(FUTILITY) $(1).unsigned $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbpubk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbprivk $(1).keyblock $(1))
+    $(VBOOT_SIGNER) $(FUTILITY) $(1).unsigned $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbpubk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbprivk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_SUBKEY).vbprivk $(1).keyblock $(1))
   $(hide) $(call assert-max-image-size,$(1),$(BOARD_RECOVERYIMAGE_PARTITION_SIZE))
   @echo ----- Made recovery image: $(1) --------
 endef
diff --git a/core/definitions.mk b/core/definitions.mk
index 78b0a39..9dea18c 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -175,16 +175,53 @@
 endef
 
 ###########################################################
+## Find all of the directories under the named directories with
+## the specified name.
+## Meant to be used like:
+##    INC_DIRS := $(call all-named-dirs-under,inc,.)
+###########################################################
+
+define all-named-dirs-under
+$(call find-subdir-files,$(2) -type d -name "$(1)")
+endef
+
+###########################################################
+## Find all the directories under the current directory that
+## haves name that match $(1)
+###########################################################
+
+define all-subdir-named-dirs
+$(call all-named-dirs-under,$(1),.)
+endef
+
+###########################################################
+## Find all of the files under the named directories with
+## the specified name.
+## Meant to be used like:
+##    SRC_FILES := $(call all-named-files-under,*.h,src tests)
+###########################################################
+
+define all-named-files-under
+$(call find-files-in-subdirs,$(LOCAL_PATH),"$(1)",$(2))
+endef
+
+###########################################################
+## Find all of the files under the current directory with
+## the specified name.
+###########################################################
+
+define all-subdir-named-files
+$(call all-named-files-under,$(1),.)
+endef
+
+###########################################################
 ## Find all of the java files under the named directories.
 ## Meant to be used like:
 ##    SRC_FILES := $(call all-java-files-under,src tests)
 ###########################################################
 
 define all-java-files-under
-$(patsubst ./%,%, \
-  $(shell cd $(LOCAL_PATH) ; \
-          find -L $(1) -name "*.java" -and -not -name ".*") \
- )
+$(call all-named-files-under,*.java,$(1))
 endef
 
 ###########################################################
@@ -203,10 +240,7 @@
 ###########################################################
 
 define all-c-files-under
-$(patsubst ./%,%, \
-  $(shell cd $(LOCAL_PATH) ; \
-          find -L $(1) -name "*.c" -and -not -name ".*") \
- )
+$(call all-named-files-under,*.c,$(1))
 endef
 
 ###########################################################
@@ -219,16 +253,36 @@
 endef
 
 ###########################################################
+## Find all of the cpp files under the named directories.
+## LOCAL_CPP_EXTENSION is respected if set.
+## Meant to be used like:
+##    SRC_FILES := $(call all-cpp-files-under,src tests)
+###########################################################
+
+define all-cpp-files-under
+$(sort $(patsubst ./%,%, \
+  $(shell cd $(LOCAL_PATH) ; \
+          find -L $(1) -name "*$(or $(LOCAL_CPP_EXTENSION),.cpp)" -and -not -name ".*") \
+ ))
+endef
+
+###########################################################
+## Find all of the cpp files from here.  Meant to be used like:
+##    SRC_FILES := $(call all-subdir-cpp-files)
+###########################################################
+
+define all-subdir-cpp-files
+$(call all-cpp-files-under,.)
+endef
+
+###########################################################
 ## Find all files named "I*.aidl" under the named directories,
 ## which must be relative to $(LOCAL_PATH).  The returned list
 ## is relative to $(LOCAL_PATH).
 ###########################################################
 
 define all-Iaidl-files-under
-$(patsubst ./%,%, \
-  $(shell cd $(LOCAL_PATH) ; \
-          find -L $(1) -name "I*.aidl" -and -not -name ".*") \
- )
+$(call all-named-files-under,I*.aidl,$(1))
 endef
 
 ###########################################################
@@ -246,10 +300,7 @@
 ###########################################################
 
 define all-logtags-files-under
-$(patsubst ./%,%, \
-  $(shell cd $(LOCAL_PATH) ; \
-          find -L $(1) -name "*.logtags" -and -not -name ".*") \
-  )
+$(call all-named-files-under,*.logtags,$(1))
 endef
 
 ###########################################################
@@ -259,10 +310,7 @@
 ###########################################################
 
 define all-proto-files-under
-$(patsubst ./%,%, \
-  $(shell cd $(LOCAL_PATH) ; \
-          find -L $(1) -name "*.proto" -and -not -name ".*") \
-  )
+$(call all-named-files-under,*.proto,$(1))
 endef
 
 ###########################################################
@@ -272,10 +320,7 @@
 ###########################################################
 
 define all-renderscript-files-under
-$(patsubst ./%,%, \
-  $(shell cd $(LOCAL_PATH) ; \
-          find -L $(1) \( -name "*.rs" -or -name "*.fs" \) -and -not -name ".*") \
-  )
+$(call find-subdir-files,$(1) \( -name "*.rs" -or -name "*.fs" \) -and -not -name ".*")
 endef
 
 ###########################################################
@@ -285,10 +330,7 @@
 ###########################################################
 
 define all-S-files-under
-$(patsubst ./%,%, \
-  $(shell cd $(LOCAL_PATH) ; \
-          find -L $(1) -name "*.S" -and -not -name ".*") \
- )
+$(call all-named-files-under,*.S,$(1))
 endef
 
 ###########################################################
@@ -298,10 +340,7 @@
 ###########################################################
 
 define all-html-files-under
-$(patsubst ./%,%, \
-  $(shell cd $(LOCAL_PATH) ; \
-          find -L $(1) -name "*.html" -and -not -name ".*") \
- )
+$(call all-named-files-under,*.html,$(1))
 endef
 
 ###########################################################
@@ -319,7 +358,7 @@
 ###########################################################
 
 define find-subdir-files
-$(patsubst ./%,%,$(shell cd $(LOCAL_PATH) ; find -L $(1)))
+$(sort $(patsubst ./%,%,$(shell cd $(LOCAL_PATH) ; find -L $(1))))
 endef
 
 ###########################################################
@@ -331,8 +370,8 @@
 ###########################################################
 
 define find-subdir-subdir-files
-$(filter-out $(patsubst %,$(1)/%,$(3)),$(patsubst ./%,%,$(shell cd \
-            $(LOCAL_PATH) ; find -L $(1) -maxdepth 1 -name $(2))))
+$(sort $(filter-out $(patsubst %,$(1)/%,$(3)),$(patsubst ./%,%,$(shell cd \
+            $(LOCAL_PATH) ; find -L $(1) -maxdepth 1 -name $(2)))))
 endef
 
 ###########################################################
@@ -341,10 +380,10 @@
 ###########################################################
 
 define find-subdir-assets
-$(if $(1),$(patsubst ./%,%, \
+$(sort $(if $(1),$(patsubst ./%,%, \
 	$(shell if [ -d $(1) ] ; then cd $(1) ; find ./ -not -name '.*' -and -type f -and -not -type l ; fi)), \
 	$(warning Empty argument supplied to find-subdir-assets) \
-)
+))
 endef
 
 ###########################################################
@@ -352,11 +391,11 @@
 ###########################################################
 
 define find-other-java-files
-	$(call find-subdir-files,$(1) -name "*.java" -and -not -name ".*")
+$(call all-java-files-under,$(1))
 endef
 
 define find-other-html-files
-	$(call find-subdir-files,$(1) -name "*.html" -and -not -name ".*")
+$(call all-html-files-under,$(1))
 endef
 
 ###########################################################
@@ -369,10 +408,10 @@
 ###########################################################
 
 define find-files-in-subdirs
-$(patsubst ./%,%, \
+$(sort $(patsubst ./%,%, \
   $(shell cd $(1) ; \
           find -L $(3) -name $(2) -and -not -name ".*") \
- )
+ ))
 endef
 
 ###########################################################
@@ -1633,8 +1672,12 @@
 ## Commands for running javac to make .class files
 ###########################################################
 
-#@echo "Source intermediates dir: $(PRIVATE_SOURCE_INTERMEDIATES_DIR)"
-#@echo "Source intermediates: $$(find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java')"
+# Add BUILD_NUMBER to apps default version name if it's unbundled build.
+ifdef TARGET_BUILD_APPS
+APPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION)-$(BUILD_NUMBER)
+else
+APPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION)
+endif
 
 # TODO: Right now we generate the asset resources twice, first as part
 # of generating the Java classes, then at the end when packaging the final
@@ -1661,8 +1704,8 @@
     $(addprefix -G , $(PRIVATE_PROGUARD_OPTIONS_FILE)) \
     $(addprefix --min-sdk-version , $(PRIVATE_DEFAULT_APP_TARGET_SDK)) \
     $(addprefix --target-sdk-version , $(PRIVATE_DEFAULT_APP_TARGET_SDK)) \
-    $(if $(filter --version-code,$(PRIVATE_AAPT_FLAGS)),,$(addprefix --version-code , $(PLATFORM_SDK_VERSION))) \
-    $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,$(addprefix --version-name , $(PLATFORM_VERSION)-$(BUILD_NUMBER))) \
+    $(if $(filter --version-code,$(PRIVATE_AAPT_FLAGS)),,--version-code $(PLATFORM_SDK_VERSION)) \
+    $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,--version-name $(APPS_DEFAULT_VERSION_NAME)) \
     $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
     $(addprefix --rename-instrumentation-target-package , $(PRIVATE_MANIFEST_INSTRUMENTATION_FOR)) \
     --skip-symbols-without-default-localization
@@ -2010,8 +2053,8 @@
     $(addprefix --min-sdk-version , $(PRIVATE_DEFAULT_APP_TARGET_SDK)) \
     $(addprefix --target-sdk-version , $(PRIVATE_DEFAULT_APP_TARGET_SDK)) \
     $(if $(filter --product,$(PRIVATE_AAPT_FLAGS)),,$(addprefix --product , $(TARGET_AAPT_CHARACTERISTICS))) \
-    $(if $(filter --version-code,$(PRIVATE_AAPT_FLAGS)),,$(addprefix --version-code , $(PLATFORM_SDK_VERSION))) \
-    $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,$(addprefix --version-name , $(PLATFORM_VERSION)-$(BUILD_NUMBER))) \
+    $(if $(filter --version-code,$(PRIVATE_AAPT_FLAGS)),,--version-code $(PLATFORM_SDK_VERSION)) \
+    $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,--version-name $(APPS_DEFAULT_VERSION_NAME)) \
     $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
     $(addprefix --rename-instrumentation-target-package , $(PRIVATE_MANIFEST_INSTRUMENTATION_FOR)) \
     --skip-symbols-without-default-localization \
diff --git a/core/main.mk b/core/main.mk
index 4f22b93..5b6e1e9 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -939,6 +939,7 @@
   $(call dist-for-goals, droidcore, \
     $(INTERNAL_UPDATE_PACKAGE_TARGET) \
     $(INTERNAL_OTA_PACKAGE_TARGET) \
+    $(BUILT_OTATOOLS_PACKAGE) \
     $(SYMBOLS_ZIP) \
     $(INSTALLED_FILES_FILE) \
     $(INSTALLED_BUILD_PROP_TARGET) \
diff --git a/core/product.mk b/core/product.mk
index 4e8bff1..e97cba4 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -104,6 +104,7 @@
     PRODUCT_SYSTEM_PROPERTY_BLACKLIST \
     PRODUCT_SYSTEM_SERVER_JARS \
     PRODUCT_VBOOT_SIGNING_KEY \
+    PRODUCT_VBOOT_SIGNING_SUBKEY \
     PRODUCT_VERITY_SIGNING_KEY \
     PRODUCT_SYSTEM_VERITY_PARTITION \
     PRODUCT_VENDOR_VERITY_PARTITION \
diff --git a/core/product_config.mk b/core/product_config.mk
index 5240ae7..94449c2 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -117,7 +117,7 @@
   # which really means TARGET_PRODUCT=dream make installclean.
   ifneq ($(filter-out $(INTERNAL_VALID_VARIANTS),$(TARGET_BUILD_VARIANT)),)
     MAKECMDGOALS := $(MAKECMDGOALS) $(TARGET_BUILD_VARIANT)
-    TARGET_BUILD_VARIANT := eng
+    TARGET_BUILD_VARIANT := userdebug
     default_goal_substitution :=
   else
     default_goal_substitution := $(DEFAULT_GOAL)
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index 483c8f2..a67a82e 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -42,7 +42,7 @@
   # 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 := 6.0
+  PLATFORM_VERSION := 6.0.1
 endif
 
 ifeq "" "$(PLATFORM_SDK_VERSION)"
@@ -103,7 +103,7 @@
   # Can be an arbitrary string, but must be a single word.
   #
   # If there is no $PLATFORM_SECURITY_PATCH set, keep it empty.
-  PLATFORM_SECURITY_PATCH :=
+  PLATFORM_SECURITY_PATCH := 2015-12-01
 endif
 
 ifeq "" "$(PLATFORM_BASE_OS)"
diff --git a/envsetup.sh b/envsetup.sh
index dba64ee..6ad3a9e 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -1355,14 +1355,20 @@
         return
     fi
     T=$(gettop)
-    if [[ ! -f $T/filelist ]]; then
+    if [ ! "$OUT_DIR" = "" ]; then
+        mkdir -p $OUT_DIR
+        FILELIST=$OUT_DIR/filelist
+    else
+        FILELIST=$T/filelist
+    fi
+    if [[ ! -f $FILELIST ]]; then
         echo -n "Creating index..."
-        (\cd $T; find . -wholename ./out -prune -o -wholename ./.repo -prune -o -type f > filelist)
+        (\cd $T; find . -wholename ./out -prune -o -wholename ./.repo -prune -o -type f > $FILELIST)
         echo " Done"
         echo ""
     fi
     local lines
-    lines=($(\grep "$1" $T/filelist | sed -e 's/\/[^/]*$//' | sort | uniq))
+    lines=($(\grep "$1" $FILELIST | sed -e 's/\/[^/]*$//' | sort | uniq))
     if [[ ${#lines[@]} = 0 ]]; then
         echo "Not found"
         return
diff --git a/target/board/generic/sepolicy/property_contexts b/target/board/generic/sepolicy/property_contexts
index 09b9b06..a0a4020 100644
--- a/target/board/generic/sepolicy/property_contexts
+++ b/target/board/generic/sepolicy/property_contexts
@@ -1,2 +1,4 @@
 qemu.                   u:object_r:qemu_prop:s0
+emu.                    u:object_r:qemu_prop:s0
+emulator.               u:object_r:qemu_prop:s0
 radio.noril             u:object_r:radio_noril_prop:s0
diff --git a/target/product/base.mk b/target/product/base.mk
index 1699156..4c49e86 100644
--- a/target/product/base.mk
+++ b/target/product/base.mk
@@ -111,6 +111,7 @@
     run-as \
     schedtest \
     sdcard \
+    secdiscard \
     services \
     settings \
     sgdisk \
diff --git a/target/product/core.mk b/target/product/core.mk
index 519dbb8..d453303 100644
--- a/target/product/core.mk
+++ b/target/product/core.mk
@@ -23,7 +23,6 @@
 PRODUCT_PACKAGES += \
     BasicDreams \
     Browser \
-    Calculator \
     Calendar \
     CalendarProvider \
     CaptivePortalLogin \
@@ -33,6 +32,7 @@
     DocumentsUI \
     DownloadProviderUi \
     Email \
+    ExactCalculator \
     Exchange2 \
     ExternalStorageProvider \
     FusedLocation \
diff --git a/target/product/core_tiny.mk b/target/product/core_tiny.mk
index 0a92275..2be0507 100644
--- a/target/product/core_tiny.mk
+++ b/target/product/core_tiny.mk
@@ -57,6 +57,7 @@
     gatekeeperd \
     keystore \
     keystore.default \
+    ld.mc \
     libOpenMAXAL \
     libOpenSLES \
     libdownmix \
diff --git a/target/product/embedded.mk b/target/product/embedded.mk
index c40de4f..25a8975 100644
--- a/target/product/embedded.mk
+++ b/target/product/embedded.mk
@@ -89,6 +89,7 @@
 
 PRODUCT_COPY_FILES += \
     system/core/rootdir/init.usb.rc:root/init.usb.rc \
+    system/core/rootdir/init.usb.configfs.rc:root/init.usb.configfs.rc \
     system/core/rootdir/init.trace.rc:root/init.trace.rc \
     system/core/rootdir/ueventd.rc:root/ueventd.rc \
     system/core/rootdir/etc/hosts:system/etc/hosts
diff --git a/target/product/emulator.mk b/target/product/emulator.mk
index 982f7da..7394d4f 100644
--- a/target/product/emulator.mk
+++ b/target/product/emulator.mk
@@ -50,6 +50,7 @@
     sensors.ranchu
 
 PRODUCT_COPY_FILES += \
+    frameworks/native/data/etc/android.hardware.ethernet.xml:system/etc/permissions/android.hardware.ethernet.xml \
     device/generic/goldfish/fstab.goldfish:root/fstab.goldfish \
     device/generic/goldfish/init.goldfish.rc:root/init.goldfish.rc \
     device/generic/goldfish/init.goldfish.sh:system/etc/init.goldfish.sh \
diff --git a/target/product/full_base.mk b/target/product/full_base.mk
index 3b547c8..65bdf0f 100644
--- a/target/product/full_base.mk
+++ b/target/product/full_base.mk
@@ -45,9 +45,6 @@
 # Put en_US first in the list, so make it default.
 PRODUCT_LOCALES := en_US
 
-# Include drawables for all densities
-PRODUCT_AAPT_CONFIG := normal
-
 # Get some sounds
 $(call inherit-product-if-exists, frameworks/base/data/sounds/AllAudio.mk)
 
diff --git a/target/product/vboot.mk b/target/product/vboot.mk
index e4b1144..48a4883 100644
--- a/target/product/vboot.mk
+++ b/target/product/vboot.mk
@@ -22,3 +22,4 @@
 # We expect this file to exist with the suffixes ".vbprivk" and ".vbpupk".
 # TODO: find a proper location for this
 PRODUCT_VBOOT_SIGNING_KEY := external/vboot_reference/tests/devkeys/kernel_data_key
+PRODUCT_VBOOT_SIGNING_SUBKEY := external/vboot_reference/tests/devkeys/kernel_subkey
diff --git a/tools/droiddoc/templates-sdk-dev/assets/css/default.css b/tools/droiddoc/templates-sdk-dev/assets/css/default.css
index 04f4b90..6fef241 100644
--- a/tools/droiddoc/templates-sdk-dev/assets/css/default.css
+++ b/tools/droiddoc/templates-sdk-dev/assets/css/default.css
@@ -6573,6 +6573,10 @@
   background: #00bcd4;
 }
 
+.dac-studio {
+  background: #424242;
+}
+
 .dac-search-mode .dac-header {
   background: #b0bec5;
   -webkit-transition: background 200ms;
@@ -6712,6 +6716,18 @@
   outline: 0;
 }
 
+.dac-studio .dac-header-console-btn {
+  color:#fff;
+  background:rgba(255, 255, 255, 0.3);
+}
+.dac-studio .dac-header-console-btn:hover {
+  background:rgba(255, 255, 255, 0.5);
+}
+.dac-studio .dac-header-console-btn:focus {
+  background:rgba(255, 255, 255, 0.7);
+  color:#000;
+}
+
 @media (max-width: 719px) {
   .dac-header {
     text-align: center;
@@ -6823,6 +6839,10 @@
     transform-origin: right center;
     width: 64px;
   }
+  .dac-studio .dac-header-search-inner::after {
+    background: -webkit-linear-gradient(right, #424242, rgba(66, 66, 66, 0));
+    background: linear-gradient(to left, #424242, rgba(66, 66, 66, 0));
+  }
 
   .dac-search-mode .dac-header-search-inner::after {
     opacity: 0;
@@ -6854,6 +6874,13 @@
   width: 100%;
 }
 
+.dac-studio .dac-header-search {
+  right:150px;
+}
+.dac-studio .dac-header-search-input {
+  background: rgba(255, 255, 255, 0.3);
+}
+
 .dac-header-search-close, .dac-header-search-clear {
   background: none;
   border: none;
@@ -7267,6 +7294,12 @@
   width: 100%;
 }
 
+.dac-studio .dac-nav-hamburger-top,
+.dac-studio .dac-nav-hamburger-mid,
+.dac-studio .dac-nav-hamburger-bot {
+  background: rgba(256, 256, 256, 0.4);
+}
+
 .dac-nav-animating .dac-nav-hamburger-top, .dac-nav-animating .dac-nav-hamburger-mid, .dac-nav-animating .dac-nav-hamburger-bot {
   -webkit-transition: opacity .3s;
   transition: opacity .3s;
@@ -7793,6 +7826,20 @@
   text-align: center;
 }
 
+/* Android Studio download page */
+section#features {
+  padding-top:0;
+}
+.wrap.feature {
+  margin:80px auto;
+}
+.dac-section-links.feature-more {
+  margin-top:-20px;
+}
+.dac-toggle-content .wrap.feature {
+  margin-top:0;
+}
+
 @media (max-width: 719px) {
   .dac-hero-figure {
     height: 150px;
@@ -7802,6 +7849,17 @@
   .dac-hero-figure img {
     max-height: 150px;
   }
+
+  /* Android Studio download page */
+  .feature .dac-hero-figure,
+  .feature .dac-hero-figure img {
+    height:auto;
+    max-height:none;
+  }
+  .feature .dac-hero-figure img {
+    width:90%;
+    margin:0 auto;
+  }
 }
 
 .dac-hero-carousel {
@@ -9829,3 +9887,33 @@
   position: absolute;
   top: -4px;
 }
+
+/** CSS Fixes for DevSite (akassay@) */
+.dac-button-social,
+.dac-fab:not('.dac-scroll-button') {
+  position: relative;
+}
+
+.dac-button-social .dac-sprite,
+.dac-fab .dac-sprite,
+.play-button .dac-sprite {
+  margin-top: -7px;
+  position: relative;
+  top: 50%;
+}
+
+.dac-fab .dac-sprite.dac-arrow-down-gray {
+  margin-top: -3px;
+}
+
+.dac-button-social .dac-sprite.dac-gplus {
+  margin-top: -17px;
+}
+
+.play-button .dac-sprite {
+  margin-top: -10px;
+}
+
+.dac-nav-link-forward {
+  padding: 9px 0;
+}
diff --git a/tools/droiddoc/templates-sdk-dev/assets/js/docs.js b/tools/droiddoc/templates-sdk-dev/assets/js/docs.js
index f64b41e..9afed71 100644
--- a/tools/droiddoc/templates-sdk-dev/assets/js/docs.js
+++ b/tools/droiddoc/templates-sdk-dev/assets/js/docs.js
@@ -15,7 +15,8 @@
 $(document).ready(function() {
 
   // prep nav expandos
-  var pagePath = document.location.pathname;
+  var pagePath = devsite ?
+      location.href.replace(location.hash, '') : document.location.pathname;
   // account for intl docs by removing the intl/*/ path
   if (pagePath.indexOf("/intl/") == 0) {
     pagePath = pagePath.substr(pagePath.indexOf("/", 6)); // start after intl/ to get last /
@@ -3378,12 +3379,22 @@
   })();
   var localeTarget = (function() {
     var localeTarget = locale;
-    if (location.pathname.substring(0,6) == "/intl/") {
-      var target = location.pathname.split('/')[2];
-      if (!(target === 0) || (LANGUAGES.indexOf(target) === -1)) {
-        localeTarget = target;
+    if (window.devsite) {
+      if (getQueryVariable('hl')) {
+        var target = getQueryVariable('hl');
+        if (!(target === 0) || (LANGUAGES.indexOf(target) === -1)) {
+          localeTarget = target;
+        }
+      }
+    } else {
+      if (location.pathname.substring(0,6) == "/intl/") {
+        var target = location.pathname.split('/')[2];
+        if (!(target === 0) || (LANGUAGES.indexOf(target) === -1)) {
+          localeTarget = target;
+        }
       }
     }
+
     return localeTarget;
   })();
 
@@ -3891,6 +3902,11 @@
     this.el.removeClass('dac-active');
     $('body').removeClass('dac-modal-open');
     this.isOpen = false;
+    // When closing the modal for Android Studio downloads, reload the page
+    // because otherwise we might get stuck with post-download dialog state
+    if ($("[data-modal='studio_tos']").length) {
+      location.reload();
+    }
   };
 
   Modal.prototype.open_ = function() {
@@ -4383,6 +4399,14 @@
       } else if (body.hasClass('downloads')) {
         selected = navEl.find('> li.downloads > a').addClass('selected');
       }
+    } else if (body.hasClass('studio')) {
+      if (body.hasClass('features')) {
+        selected = navEl.find('> li.features > a').addClass('selected');
+      } else if (body.hasClass('guide')) {
+        selected = navEl.find('> li.guide > a').addClass('selected');
+      } else if (body.hasClass('preview')) {
+        selected = navEl.find('> li.preview > a').addClass('selected');
+      }
     } else if (body.hasClass('design')) {
       selected = navEl.find('> li.design > a').addClass('selected');
       // highlight Home nav
@@ -5344,7 +5368,8 @@
     return $.get('https://content.googleapis.com/customsearch/v1?' +  $.param(searchParams));
   }
 
-  function renderResults(el, results) {
+  function renderResults(el, results, searchAppliance) {
+    var referenceResults = searchAppliance.getReferenceResults();
     if (!results.items) {
       el.append($('<div>').text('No results'));
       return;
@@ -5352,27 +5377,37 @@
 
     for (var i = 0; i < results.items.length; i++) {
       var item = results.items[i];
-      var hasImage = item.pagemap && item.pagemap.cse_thumbnail;
-      var sectionMatch = item.link.match(/developer\.android\.com\/(\w*)/);
-      var section = (sectionMatch && sectionMatch[1]) || 'blog';
+      var isDuplicate = false;
+      $(referenceResults.android).each(function(index, result) {
+        if (item.link.indexOf(result.link) > -1) {
+          isDuplicate = true;
+          return false;
+        }
+      });
 
-      var entry = $('<div>').addClass('dac-custom-search-entry cols');
+      if (!isDuplicate) {
+        var hasImage = item.pagemap && item.pagemap.cse_thumbnail;
+        var sectionMatch = item.link.match(/developer\.android\.com\/(\w*)/);
+        var section = (sectionMatch && sectionMatch[1]) || 'blog';
 
-      if (hasImage) {
-        var image = item.pagemap.cse_thumbnail[0];
-        entry.append($('<div>').addClass('dac-custom-search-image-wrapper')
-          .append($('<div>').addClass('dac-custom-search-image').css('background-image', 'url(' + image.src + ')')));
+        var entry = $('<div>').addClass('dac-custom-search-entry cols');
+
+        if (hasImage) {
+          var image = item.pagemap.cse_thumbnail[0];
+          entry.append($('<div>').addClass('dac-custom-search-image-wrapper')
+            .append($('<div>').addClass('dac-custom-search-image').css('background-image', 'url(' + image.src + ')')));
+        }
+
+        entry.append($('<div>').addClass('dac-custom-search-text-wrapper')
+          .append($('<p>').addClass('dac-custom-search-section').text(section))
+          .append(
+            $('<a>').text(item.title).attr('href', item.link).wrap('<h2>').parent().addClass('dac-custom-search-title')
+          )
+          .append($('<p>').addClass('dac-custom-search-snippet').html(item.htmlSnippet.replace(/<br>/g, '')))
+          .append($('<a>').addClass('dac-custom-search-link').text(item.formattedUrl).attr('href', item.link)));
+
+        el.append(entry);
       }
-
-      entry.append($('<div>').addClass('dac-custom-search-text-wrapper')
-        .append($('<p>').addClass('dac-custom-search-section').text(section))
-        .append(
-          $('<a>').text(item.title).attr('href', item.link).wrap('<h2>').parent().addClass('dac-custom-search-title')
-        )
-        .append($('<p>').addClass('dac-custom-search-snippet').html(item.htmlSnippet.replace(/<br>/g, '')))
-        .append($('<a>').addClass('dac-custom-search-link').text(item.formattedUrl).attr('href', item.link)));
-
-      el.append(entry);
     }
 
     if (results.queries.nextPage) {
@@ -5380,32 +5415,32 @@
         .addClass('dac-custom-search-load-more')
         .text('Load more')
         .click(function() {
-          loadMoreResults(el, results);
+          loadMoreResults(el, results, searchAppliance);
         });
 
       el.append(loadMoreButton);
     }
-  }
+  };
 
-  function loadMoreResults(el, results) {
-    var query = results.queries.request.searchTerms;
-    var start = results.queries.nextPage.startIndex;
+  function loadMoreResults(el, results, searchAppliance) {
+    var query = results.queries.request[0].searchTerms;
+    var start = results.queries.nextPage[0].startIndex;
     var loadMoreButton = el.find('#dac-custom-search-load-more');
 
     loadMoreButton.text('Loading more...');
 
     customSearch(query, start).then(function(results) {
       loadMoreButton.remove();
-      renderResults(el, results);
+      renderResults(el, results, searchAppliance);
     });
   }
 
-  $.fn.customSearch = function(query) {
+  $.fn.customSearch = function(query, searchAppliance) {
     var el = $(this);
 
     customSearch(query).then(function(results) {
       el.empty();
-      renderResults(el, results);
+      renderResults(el, results, searchAppliance);
     });
   };
 })(jQuery);
@@ -5612,10 +5647,11 @@
     this.searchResultsHero = $('#dac-search-results-hero');
     this.searchResultsReference = $('#dac-search-results-reference');
     this.searchHeader = $('[data-search]').data('search-input.dac');
+    this.currQueryReferenceResults = {};
   }
 
   Search.prototype.init = function() {
-    if (this.checkRedirectToIndex()) { return; }
+    if (!devsite && this.checkRedirectToIndex()) { return; }
 
     this.searchHistory = window.dacStore('search-history');
 
@@ -5626,9 +5662,8 @@
     this.searchClose.click(this.close.bind(this));
 
     this.customSearch = $.fn.debounce(function(query) {
-      $('#dac-custom-search-results').customSearch(query);
-    }, 1000);
-
+      $('#dac-custom-search-results').customSearch(query, this);
+    }.bind(this), 1000);
     // Start search shortcut (/)
     $('body').keyup(function(event) {
       if (event.which === 191 && $(event.target).is(':not(:input)')) {
@@ -5751,6 +5786,10 @@
     return this.searchInput.val().replace(/(^ +)|( +$)/g, '');
   };
 
+  Search.prototype.getReferenceResults = function() {
+    return this.currQueryReferenceResults;
+  };
+
   Search.prototype.onSearchChanged = function() {
     var query = this.getQuery();
 
@@ -5767,10 +5806,19 @@
 
     this.lastQuery = query;
     this.searchResultsFor.text(query);
+
+    // CSE results lag behind the metadata/reference results. We need to empty
+    // the CSE results and add 'Loading' text so user's aren't looking at two
+    // different sets of search results at one time.
+    var $loadingEl =
+        $('<div class="loadingCustomSearchResults">Loading Results...</div>');
+    $('#dac-custom-search-results').empty().prepend($loadingEl);
+
     this.customSearch(query);
     var metadataResults = metadata.search(query);
     this.searchResultsResources.dacSearchRenderResources(metadataResults.resources, query);
     this.searchResultsReference.dacSearchRenderReferences(metadataResults, query);
+    this.currQueryReferenceResults = metadataResults;
     var hasHero = this.searchResultsHero.dacSearchRenderHero(metadataResults.resources, query);
     var hasQuery = !!query;
 
@@ -6489,3 +6537,22 @@
     initWideTable();
   });
 })(jQuery);
+
+/** Utilities */
+
+/* returns the given string with all HTML brackets converted to entities
+    TODO: move this to the site's JS library */
+function escapeHTML(string) {
+  return string.replace(/</g,"&lt;")
+                .replace(/>/g,"&gt;");
+};
+
+function getQueryVariable(variable) {
+  var query = window.location.search.substring(1);
+  var vars = query.split("&");
+  for (var i=0;i<vars.length;i++) {
+    var pair = vars[i].split("=");
+    if(pair[0] == variable){return pair[1];}
+  }
+  return(false);
+};
diff --git a/tools/droiddoc/templates-sdk-dev/sdkpage.cs b/tools/droiddoc/templates-sdk-dev/sdkpage.cs
index 94b6276..30d6d04 100644
--- a/tools/droiddoc/templates-sdk-dev/sdkpage.cs
+++ b/tools/droiddoc/templates-sdk-dev/sdkpage.cs
@@ -12,16 +12,19 @@
 <?cs else ?>
   <?cs include:"head_tag.cs" ?>
 <?cs /if ?>
-<body class="gc-documentation <?cs
-  if:(guide||develop||training||reference||tools||sdk) ?> develop<?cs
-  elif:design ?> design<?cs
-  elif:distribute ?> distribute<?cs
-  elif:ndk ?> ndk<?cs
-    if:downloads ?> downloads<?cs /if ?><?cs
+<body class="gc-documentation
+  <?cs if:(guide||develop||training||reference||tools||sdk) ?>develop<?cs
+  elif:design ?>design<?cs
+  elif:distribute ?>distribute<?cs
   /if ?>" itemscope itemtype="http://schema.org/CreativeWork">
   <a name="top"></a>
 <?cs include:"header.cs" ?>
 
+
+<div <?cs if:fullpage
+?><?cs else
+?>class="col-13" id="doc-col"<?cs /if ?> >
+
 <?cs if:sdk.redirect ?>
 
 <div class="g-unit">
@@ -41,15 +44,7 @@
 # The following is for SDK/NDK pages
 #
 #
-?><?cs
-if:ndk ?>
-  <ul class="dac-header-crumbs">
-    <?cs # More <li> elements added here with javascript ?>
-  </ul>
-
-  <!-- Breadcrumb Setup -->
-  <p><script>$('.dac-nav-list').dacCurrentPage().dacCrumbs();</script></p><?cs
-/if ?>
+?>
 
 <?cs if:header.hide ?><?cs else ?>
 <h1 itemprop="name"><?cs var:page.title ?></h1>
@@ -86,20 +81,20 @@
       <th>Platform</th>
       <th>Package</th>
       <th style="white-space:nowrap">Size (Bytes)</th>
-      <th>MD5 Checksum</th>
+      <th>SHA1 Checksum</th>
   </tr>
   <tr>
     <td>Windows 32-bit</td>
     <td>
-  <a onClick="return onDownload(this)"
-     href="http://dl.google.com/android/ndk/<?cs var:ndk.win32_download ?>"><?cs var:ndk.win32_download ?></a>
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
+     href="http://dl.google.com/android/repository/<?cs var:ndk.win32_download ?>"><?cs var:ndk.win32_download ?></a>
     </td>
     <td><?cs var:ndk.win32_bytes ?></td>
     <td><?cs var:ndk.win32_checksum ?></td>
   </tr>
  <!-- <tr>
    <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/ndk/<?cs var:ndk.win32.legacy_download ?>"><?cs var:ndk.win32.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.win32.legacy_bytes ?></td>
@@ -108,16 +103,16 @@
   <tr>
     <td>Windows 64-bit</td>
     <td>
-  <a onClick="return onDownload(this)"
-     href="http://dl.google.com/android/ndk/<?cs var:ndk.win64_download ?>"><?cs var:ndk.win64_download ?></a>
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
+     href="http://dl.google.com/android/repository/<?cs var:ndk.win64_download ?>"><?cs var:ndk.win64_download ?></a>
     </td>
     <td><?cs var:ndk.win64_bytes ?></td>
     <td><?cs var:ndk.win64_checksum ?></td>
   </tr>
  <!--  <tr>
     <td>
-  <a onClick="return onDownload(this)"
-     href="http://dl.google.com/android/ndk/<?cs var:ndk.win64.legacy_download ?>"><?cs var:ndk.win64.legacy_download ?></a>
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
+     href="http://dl.google.com/android/repository/<?cs var:ndk.win64.legacy_download ?>"><?cs var:ndk.win64.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.win64.legacy_bytes ?></td>
     <td><?cs var:ndk.win64.legacy_checksum ?></td>
@@ -126,8 +121,8 @@
   <tr>
     <td>Mac OS X 32-bit</td>
     <td>
-  <a onClick="return onDownload(this)"
-     href="http://dl.google.com/android/ndk/<?cs var:ndk.mac32_download ?>"><?cs var:ndk.mac32_download ?></a>
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
+     href="http://dl.google.com/android/repository/<?cs var:ndk.mac32_download ?>"><?cs var:ndk.mac32_download ?></a>
     </td>
     <td><?cs var:ndk.mac32_bytes ?></td>
     <td><?cs var:ndk.mac32_checksum ?></td>
@@ -135,41 +130,32 @@
  <!-- (this item is deprecated)
   <tr>
     <td>
-  <a onClick="return onDownload(this)"
-     href="http://dl.google.com/android/ndk/<?cs var:ndk.mac32.legacy_download ?>"><?cs var:ndk.mac32.legacy_download ?></a>
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
+     href="http://dl.google.com/android/repository/<?cs var:ndk.mac32.legacy_download ?>"><?cs var:ndk.mac32.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.mac32.legacy_bytes ?></td>
     <td><?cs var:ndk.mac32.legacy_checksum ?></td>
   </tr> -->
     <td>Mac OS X 64-bit</td>
     <td>
-  <a onClick="return onDownload(this)"
-     href="http://dl.google.com/android/ndk/<?cs var:ndk.mac64_download ?>"><?cs var:ndk.mac64_download ?></a>
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
+     href="http://dl.google.com/android/repository/<?cs var:ndk.mac64_download ?>"><?cs var:ndk.mac64_download ?></a>
     </td>
     <td><?cs var:ndk.mac64_bytes ?></td>
     <td><?cs var:ndk.mac64_checksum ?></td>
   </tr>
  <!--  <tr>
     <td>
-  <a onClick="return onDownload(this)"
-     href="http://dl.google.com/android/ndk/<?cs var:ndk.mac64.legacy_download ?>"><?cs var:ndk.mac64.legacy_download ?></a>
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
+     href="http://dl.google.com/android/repository/<?cs var:ndk.mac64.legacy_download ?>"><?cs var:ndk.mac64.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.mac64.legacy_bytes ?></td>
     <td><?cs var:ndk.mac64.legacy_checksum ?></td>
   </tr> -->
-  <tr>
-    <td>Linux 32-bit (x86)</td>
-    <td>
-  <a onClick="return onDownload(this)"
-     href="http://dl.google.com/android/ndk/<?cs var:ndk.linux32_download ?>"><?cs var:ndk.linux32_download ?></a>
-    </td>
-    <td><?cs var:ndk.linux32_bytes ?></td>
-    <td><?cs var:ndk.linux32_checksum ?></td>
-  </tr>
  <!--  <tr>
     <td>
-  <a onClick="return onDownload(this)"
-     href="http://dl.google.com/android/ndk/<?cs var:ndk.linux32.legacy_download ?>"><?cs var:ndk.linux32.legacy_download ?></a>
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
+     href="http://dl.google.com/android/repository/<?cs var:ndk.linux32.legacy_download ?>"><?cs var:ndk.linux32.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.linux32.legacy_bytes ?></td>
     <td><?cs var:ndk.linux32.legacy_checksum ?></td>
@@ -177,16 +163,16 @@
   <tr>
     <td>Linux 64-bit (x86)</td>
     <td>
-  <a onClick="return onDownload(this)"
-     href="http://dl.google.com/android/ndk/<?cs var:ndk.linux64_download ?>"><?cs var:ndk.linux64_download ?></a>
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
+     href="http://dl.google.com/android/repository/<?cs var:ndk.linux64_download ?>"><?cs var:ndk.linux64_download ?></a>
     </td>
     <td><?cs var:ndk.linux64_bytes ?></td>
     <td><?cs var:ndk.linux64_checksum ?></td>
   </tr>
   <!--  <tr>
     <td>
-  <a onClick="return onDownload(this)"
-     href="http://dl.google.com/android/ndk/<?cs var:ndk.linux64.legacy_download ?>"><?cs var:ndk.linux64.legacy_download ?></a>
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
+     href="http://dl.google.com/android/repository/<?cs var:ndk.linux64.legacy_download ?>"><?cs var:ndk.linux64.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.linux64.legacy_bytes ?></td>
     <td><?cs var:ndk.linux64.legacy_checksum ?></td>
@@ -198,16 +184,13 @@
   <?cs call:tag_list(root.descr) ?>
 
 
-
+<?cs ########  The NDK version of the download script ######### ?>
 <script>
   function onDownload(link) {
 
     $("#downloadForRealz").html("Download " + $(link).text());
     $("#downloadForRealz").attr('href',$(link).attr('href'));
 
-    $("#tos").fadeIn('slow');
-
-    location.hash = "download";
     return false;
   }
 
@@ -220,25 +203,28 @@
     }
   }
 
-  function onDownloadNdkForRealz(link) {
+
+  function onDownloadForRealz(link) {
     if ($("input#agree").is(':checked')) {
-      $("#tos").fadeOut('slow');
-
-      $('html, body').animate({
-          scrollTop: $("#Installing").offset().top
-        }, 800, function() {
-          $("#Installing").click();
+      $("div.sdk-terms").slideUp();
+      $("h2#tos-header").text('Now downloading...');
+      $(".sdk-terms-intro").text('Your download is in progress.');
+      $("#sdk-terms-form").fadeOut('slow', function() {
+        setTimeout(function() {
+          // close the dialog
+          $('#ndk_tos').trigger('modal-close');
+          // reload to refresh the tos or optionally forward the user
+           location.reload();
+        }, 3000);
       });
-
+      ga('send', 'event', 'SDK', 'NDK tools', $("#downloadForRealz").html());
       return true;
     } else {
-      $("label#agreeLabel").parent().stop().animate({color: "#258AAF"}, 200,
-        function() {$("label#agreeLabel").parent().stop().animate({color: "#222"}, 200)}
-      );
       return false;
     }
   }
 
+
   $(window).hashchange( function(){
     if (location.hash == "") {
       location.reload();
@@ -270,153 +256,173 @@
 <?cs call:tag_list(root.descr) ?>
 
 
+<section id="downloads" class="dac-section dac-small">
+<div class="wrap">
+
+<h2 class="norule">Start using Android Studio today</h2>
+
+<p>Android Studio includes all the tools you need to build apps for Android.</p>
+
+<div style="float:left;margin-right:40px;width:auto;">
+<p>
+  <a class="landing-button green download-bundle-button"
+    data-modal-toggle="studio_tos">Download Android Studio 2.0<br>
+  <span class="small"></span></a>
+</p>
+</div>
+
+<div style="float:left;width:auto;margin-bottom:40px">
+<ul class="nolist" style="text-transform: uppercase;margin:8px 0">
+<li>Version: <?cs var:studio.version ?></li>
+<li>Release date: <?cs var:studio.release.date ?></li>
+</ul>
+</div>
 
 
-<div class="pax col-13 online" style="margin:0;">
 
-
-<h3>SDK Tools Only</h3>
-
-<p>If you prefer to use a different IDE or run the tools from the
-command line or with build scripts, you can instead download the stand-alone Android SDK Tools.
-These packages provide the basic SDK tools for app development, without an IDE.
-Also see the <a href="<?cs var:toroot ?>tools/sdk/tools-notes.html">SDK tools release notes</a>.</p>
+<h4 style="clear:left;margin-top:40px">Select a different platform</h4>
 
   <table class="download">
     <tr>
       <th>Platform</th>
-      <th>Package</th>
+      <th>Android Studio package</th>
       <th>Size</th>
-      <th>SHA-1 Checksum</th>
+      <th>SHA-1 checksum</th>
   </tr>
   <tr>
-    <td rowspan="2">Windows</td>
-    <td>
-  <a onclick="return onDownload(this)" id="win-tools" href="http://dl.google.com/android/<?cs
-var:sdk.win_installer
-?>"><?cs var:sdk.win_installer ?></a> (Recommended)
-    </td>
-    <td><?cs var:sdk.win_installer_bytes ?> bytes</td>
-    <td><?cs var:sdk.win_installer_checksum ?></td>
-  </tr>
-  <tr>
-    <!-- blank TD from Windows rowspan -->
-    <td>
-  <a onclick="return onDownload(this)" href="http://dl.google.com/android/<?cs var:sdk.win_download
-?>"><?cs var:sdk.win_download ?></a>
-    </td>
-    <td><?cs var:sdk.win_bytes ?> bytes</td>
-    <td><?cs var:sdk.win_checksum ?></td>
-  </tr>
-  <tr>
-    <td><nobr>Mac OS X</nobr></td>
-    <td>
-  <a onclick="return onDownload(this)" id="mac-tools" href="http://dl.google.com/android/<?cs
-var:sdk.mac_download
-?>"><?cs var:sdk.mac_download ?></a>
-    </td>
-    <td><?cs var:sdk.mac_bytes ?> bytes</td>
-    <td><?cs var:sdk.mac_checksum ?></td>
-  </tr>
-  <tr>
-    <td>Linux</td>
-    <td>
-  <a onclick="return onDownload(this)" id="linux-tools" href="http://dl.google.com/android/<?cs
-var:sdk.linux_download
-?>"><?cs var:sdk.linux_download ?></a>
-    </td>
-    <td><?cs var:sdk.linux_bytes ?> bytes</td>
-    <td><?cs var:sdk.linux_checksum ?></td>
-  </tr>
-  </table>
-
-
-
-<h3>All Android Studio Packages</h3>
-
-<p>Select a specific Android Studio package for your platform. Also see the
-<a href="<?cs var:toroot ?>tools/revisions/studio.html">Android Studio release notes</a>.</p>
-
-  <table class="download">
-    <tr>
-      <th>Platform</th>
-      <th>Package</th>
-      <th>Size</th>
-      <th>SHA-1 Checksum</th>
-  </tr>
-
-  <tr>
     <td rowspan="3">Windows</td>
     <td>
-  <a onclick="return onDownload(this,false,true)" id="win-bundle"
+  <a onclick="return onDownload(this,false,true)" id="win-bundle" data-modal-toggle="studio_tos"
     href="https://dl.google.com/dl/android/studio/install/<?cs var:studio.version ?>/<?cs var:studio.win_bundle_exe_download ?>"
-    ><?cs var:studio.win_bundle_exe_download ?></a><br>(Recommended)
+    ><?cs var:studio.win_bundle_exe_download ?></a><br>
+    Includes Windows installer (recommended)
     </td>
-    <td><?cs var:studio.win_bundle_exe_bytes ?> bytes</td>
+    <td id="win-bundle-size"><?cs call:size_in_mb(studio.win_bundle_exe_bytes) ?> MB
+      <br>(<?cs var:studio.win_bundle_exe_bytes ?> bytes)</td>
     <td><?cs var:studio.win_bundle_exe_checksum ?></td>
   </tr>
-
   <tr>
     <!-- blank TD from Windows rowspan -->
     <td>
-  <a onclick="return onDownload(this,false,true)" id="win-bundle-notools"
-    href="https://dl.google.com/dl/android/studio/install/<?cs var:studio.version ?>/<?cs var:studio.win_notools_exe_download ?>"
-    ><?cs var:studio.win_notools_exe_download ?></a><br>(No SDK tools included)
-    </td>
-    <td><?cs var:studio.win_notools_exe_bytes ?> bytes</td>
-    <td><?cs var:studio.win_notools_exe_checksum ?></td>
-  </tr>
-
-  <tr>
-    <!-- blank TD from Windows rowspan -->
-    <td>
-  <a onclick="return onDownload(this,false,true)" id="win-bundle-zip"
+  <a onclick="return onDownload(this,false,true)" id="win-bundle-zip" data-modal-toggle="studio_tos"
     href="https://dl.google.com/dl/android/studio/ide-zips/<?cs var:studio.version ?>/<?cs var:studio.win_bundle_download ?>"
-    ><?cs var:studio.win_bundle_download ?></a>
+    ><?cs var:studio.win_bundle_download ?></a><br>
+    No Windows installer
     </td>
-    <td><?cs var:studio.win_bundle_bytes ?> bytes</td>
+    <td><?cs call:size_in_mb(studio.win_bundle_bytes) ?> MB
+      <br>(<?cs var:studio.win_bundle_bytes ?> bytes)</td>
     <td><?cs var:studio.win_bundle_checksum ?></td>
   </tr>
-
+  <tr>
+    <!-- blank TD from Windows rowspan -->
+    <td>
+  <a onclick="return onDownload(this,false,true)" id="win-bundle-notools" data-modal-toggle="studio_tos"
+    href="https://dl.google.com/dl/android/studio/install/<?cs var:studio.version ?>/<?cs var:studio.win_notools_exe_download ?>"
+    ><?cs var:studio.win_notools_exe_download ?></a><br>
+    Includes Windows installer but no SDK tools
+    </td>
+    <td><?cs call:size_in_mb(studio.win_notools_exe_bytes) ?> MB
+      <br>(<?cs var:studio.win_notools_exe_bytes ?> bytes)</td>
+    <td><?cs var:studio.win_notools_exe_checksum ?></td>
+  </tr>
   <tr>
     <td><nobr>Mac OS X</nobr></td>
     <td>
-  <a onclick="return onDownload(this,false,true)" id="mac-bundle"
+  <a onclick="return onDownload(this,false,true)" id="mac-bundle" data-modal-toggle="studio_tos"
     href="https://dl.google.com/dl/android/studio/install/<?cs var:studio.version ?>/<?cs var:studio.mac_bundle_download ?>"
     ><?cs var:studio.mac_bundle_download ?></a>
     </td>
-    <td><?cs var:studio.mac_bundle_bytes ?> bytes</td>
+    <td id="mac-bundle-size"><?cs call:size_in_mb(studio.mac_bundle_bytes) ?> MB
+      <br>(<?cs var:studio.mac_bundle_bytes ?> bytes)</td>
     <td><?cs var:studio.mac_bundle_checksum ?></td>
   </tr>
-
   <tr>
     <td>Linux</td>
     <td>
-  <a onclick="return onDownload(this,false,true)" id="linux-bundle"
+  <a onclick="return onDownload(this,false,true)" id="linux-bundle" data-modal-toggle="studio_tos"
     href="https://dl.google.com/dl/android/studio/ide-zips/<?cs var:studio.version ?>/<?cs var:studio.linux_bundle_download ?>"
     ><?cs var:studio.linux_bundle_download ?></a>
     </td>
-    <td><?cs var:studio.linux_bundle_bytes ?> bytes</td>
+    <td id="linux-bundle-size"><?cs call:size_in_mb(studio.linux_bundle_bytes) ?> MB
+      <br>(<?cs var:studio.linux_bundle_bytes ?> bytes)</td>
     <td><?cs var:studio.linux_bundle_checksum ?></td>
   </tr>
   </table>
 
 
 
-</div><!-- end pax -->
+<h4 class="norule" style="margin-top:40px">Get just the SDK tools</h4>
+
+<p>If you do not need Android Studio, you can download the basic
+Android SDK tools below.</p>
+
+  <table class="download">
+    <tr>
+      <th>Platform</th>
+      <th>SDK tools package</th>
+      <th>Size</th>
+      <th>SHA-1 checksum</th>
+  </tr>
+  <tr>
+    <td rowspan="2">Windows</td>
+    <td>
+  <a onclick="return onDownload(this)" id="win-tools" data-modal-toggle="studio_tos"
+    href="//dl.google.com/android/<?cs
+var:sdk.win_installer
+?>"><?cs var:sdk.win_installer ?></a><br>
+    Includes Windows installer
+    </td>
+    <td><?cs call:size_in_mb(sdk.win_installer_bytes) ?> MB
+      <br>(<?cs var:sdk.win_installer_bytes ?> bytes)</td>
+    <td><?cs var:sdk.win_installer_checksum ?></td>
+  </tr>
+  <tr>
+    <!-- blank TD from Windows rowspan -->
+    <td>
+  <a onclick="return onDownload(this)" id="win-tools2" data-modal-toggle="studio_tos"
+    href="//dl.google.com/android/<?cs var:sdk.win_download
+?>"><?cs var:sdk.win_download ?></a><br>
+    No Windows installer
+    </td>
+    <td><?cs call:size_in_mb(sdk.win_bytes) ?> MB
+      <br>(<?cs var:sdk.win_bytes ?> bytes)</td>
+    <td><?cs var:sdk.win_checksum ?></td>
+  </tr>
+  <tr>
+    <td><nobr>Mac OS X</nobr></td>
+    <td>
+  <a onclick="return onDownload(this)" id="mac-tools" data-modal-toggle="studio_tos"
+    href="//dl.google.com/android/<?cs
+var:sdk.mac_download
+?>"><?cs var:sdk.mac_download ?></a>
+    </td>
+    <td><?cs call:size_in_mb(sdk.mac_bytes) ?> MB
+      <br>(<?cs var:sdk.mac_bytes ?> bytes)</td>
+    <td><?cs var:sdk.mac_checksum ?></td>
+  </tr>
+  <tr>
+    <td>Linux</td>
+    <td>
+  <a onclick="return onDownload(this)" id="linux-tools" data-modal-toggle="studio_tos"
+    href="//dl.google.com/android/<?cs
+var:sdk.linux_download
+?>"><?cs var:sdk.linux_download ?></a>
+    </td>
+    <td><?cs call:size_in_mb(sdk.linux_bytes) ?> MB
+      <br>(<?cs var:sdk.linux_bytes ?> bytes)</td>
+    <td><?cs var:sdk.linux_checksum ?></td>
+  </tr>
+  </table>
+  <p>
+Also see the <a href="<?cs var:toroot ?>tools/sdk/tools-notes.html">SDK
+tools release notes</a>.</p>
+
+  </div><!-- end wrap -->
+  </section>
 
 
-
-
-
+<?cs ########  The Android Studio version of the download script ######### ?>
 <script>
-
-  if (location.hash == "#Requirements") {
-    $('.reqs').show();
-  } else if (location.hash == "#ExistingIDE") {
-	 $('.ide').show();
-  }
-
   var os;
   var bundlename;
   var $toolslink;
@@ -441,7 +447,9 @@
     $('#not-supported').hide();
 
     /* set up primary Android Studio download button */
-    $('.download-bundle-button > .small').html(" for " + os);
+    idname = bundlename + "-size";
+    sizeMB = $(idname).text().split(' MB')[0];
+    $('.download-bundle-button > .small').html(" for " + os + " <em>(" + sizeMB + " MB)</em>");
     $('.download-bundle-button').click(function() {return onDownload(this,true,true);}).attr('href', bundlename);
   }
 
@@ -459,13 +467,10 @@
     if (bundle && !button) {
       $("a#downloadForRealz").attr("name", "#" + $(link).attr('id'));
     } else {
+      $("h2#tos-header").text('Download the Android SDK Tools');
       $("a#downloadForRealz").attr("name", $(link).attr('href'));
     }
 
-    $("#tos").show();
-    $("#landing").hide();
-
-    location.hash = "top";
     return false;
   }
 
@@ -493,22 +498,24 @@
 
   function onDownloadForRealz(link) {
     if ($("input#agree").is(':checked')) {
-      location.hash = "";
-      location.hash = "top";
       $("div.sdk-terms").slideUp();
-      $("h1#tos-header").text('Now downloading...');
-      $(".sdk-terms-intro").text('You\'ll be redirected to the install instructions in a moment.');
-      $("#sdk-terms-form").fadeOut('slow', function() {
-        setTimeout(function() {
-          if ($("#downloadForRealz").attr('bundle') == 'true') {
-            // User downloaded the studio Bundle
-            window.location = "/sdk/installing/index.html?pkg=studio";
-          } else {
-            // User downloaded the SDK Tools
-            window.location = "/sdk/installing/index.html?pkg=tools";
-          }
-        }, 3000);
-      });
+      if ($("#downloadForRealz").attr('bundle') == 'true') {
+        $("h2#tos-header").text('Now downloading Android Studio!');
+        $(".sdk-terms-intro").text('Redirecting to the install instructions...');
+        $("#sdk-terms-form").slideUp(function() {
+          setTimeout(function() {
+            window.location = "/sdk/installing/index.html";
+          }, 2000);
+        });
+      } else {
+        $("h2#tos-header").text('Now downloading the Android SDK Tools!');
+        $(".sdk-terms-intro").html("<p>Because you've chosen to download " +
+          "only the Android SDK tools (and not Android Studio), there are no " +
+          "setup procedures to follow.</p><p>For information about how to " +
+          "keep your SDK tools up to date, refer to the " +
+          "<a href='/tools/help/sdk-manager.html'>SDK Manager</a> guide.</p>");
+        $("#sdk-terms-form").slideUp();
+      }
       ga('send', 'event', 'SDK', 'IDE and Tools', $("#downloadForRealz").html());
       return true;
     } else {
@@ -526,7 +533,7 @@
 
 
 
-</div><!-- end the wrapper used for relative/absolute positions  -->
+
 <?cs # THIS DIV WAS OPENED IN INDEX.JD ?>
 
 
@@ -573,9 +580,14 @@
 <?cs /if ?>
 
 </div><!-- end g-unit -->
-<?cs if:!devsite ?>
+
 <?cs include:"trailer.cs" ?>
-<?cs /if ?>
+<script src="https://developer.android.com/ytblogger_lists_unified.js?v=17" type="text/javascript"></script>
+<script src="/jd_lists_unified.js?v=17" type="text/javascript"></script>
+<script src="/jd_extras.js?v=17" type="text/javascript"></script>
+<script src="/jd_collections.js?v=17" type="text/javascript"></script>
+<script src="/jd_tag_helpers.js?v=17" type="text/javascript"></script>
+
 <!-- Start of Tag -->
 <script type="text/javascript">
 var axel = Math.random() + "";
diff --git a/tools/droiddoc/templates-sdk/assets/css/default.css b/tools/droiddoc/templates-sdk/assets/css/default.css
index 106ab2f..accf7bf 100644
--- a/tools/droiddoc/templates-sdk/assets/css/default.css
+++ b/tools/droiddoc/templates-sdk/assets/css/default.css
@@ -732,7 +732,7 @@
     text-align:center;
     width: 50%;
   }
-  
+
   .training-nav-top a.prev-page-link {
     padding-left: 15px;
     text-align: left;
@@ -840,7 +840,7 @@
     margin: 0 0 6px;
     line-height: 16px;
   }
-  
+
   /* Class colors */
   ol.class-list li:nth-child(10n+1) .title {
     background: #00bcd4;
@@ -872,7 +872,7 @@
   ol.class-list li:nth-child(10n+10) .title {
     background: #7e57c2;
   }
-  
+
   @media (max-width: 719px) {
     ol.class-list ol,
     ol.class-list .description {
@@ -6376,7 +6376,7 @@
 .dac-button.dac-raised.dac-primary, .landing-secondary, .button {
   background-color: #039bef; }
   .dac-button.dac-raised.dac-primary:hover, .landing-secondary:hover, .button:hover {
-    background-color: #0288d1; 
+    background-color: #0288d1;
     color:#fff; }
   .dac-button.dac-raised.dac-primary:active, .landing-secondary:active, .button:active {
     background-color: #0277bd;
@@ -7787,6 +7787,20 @@
   text-align: center;
 }
 
+/* Android Studio download page */
+section#features {
+  padding-top:0;
+}
+.wrap.feature {
+  margin:80px auto;
+}
+.dac-section-links.feature-more {
+  margin-top:-20px;
+}
+.dac-toggle-content .wrap.feature {
+  margin-top:0;
+}
+
 @media (max-width: 719px) {
   .dac-hero-figure {
     height: 150px;
@@ -7796,6 +7810,17 @@
   .dac-hero-figure img {
     max-height: 150px;
   }
+
+  /* Android Studio download page */
+  .feature .dac-hero-figure,
+  .feature .dac-hero-figure img {
+    height:auto;
+    max-height:none;
+  }
+  .feature .dac-hero-figure img {
+    width:90%;
+    margin:0 auto;
+  }
 }
 
 .dac-hero-carousel {
@@ -8874,4 +8899,85 @@
   .dac-hero-cta.mprev {
   white-space:nowrap;
   }
-}
\ No newline at end of file
+}
+
+/** Custom search API styles */
+.dac-custom-search {
+  background: #fff;
+  margin: 0 -10px;
+  padding: 20px 10px;
+  z-index: 1;
+}
+
+.dac-custom-search-section-title {
+  color: #505050;
+}
+
+.dac-custom-search-entry {
+  margin-bottom: 36px;
+  margin-top: 24px;
+}
+
+.dac-custom-search-image {
+  background-size: cover;
+  height: 112px;
+}
+
+@media (max-width: 719px) {
+  .dac-custom-search-image {
+    display: none;
+  }
+}
+
+.dac-custom-search-title {
+  color: #333;
+  font-size: 14px;
+  font-weight: 700;
+  line-height: 24px;
+  margin: 0;
+  padding: 0;
+}
+
+.dac-custom-search-title a {
+  color: inherit;
+}
+
+.dac-custom-search-section {
+  color: #999;
+  font-size: 16px;
+  font-variant: small-caps;
+  font-weight: 700;
+  margin: -5px 0 0 0;
+}
+
+.dac-custom-search-snippet {
+  color: #666;
+  margin: 0;
+}
+
+.dac-custom-search-link {
+  font-weight: 500;
+  word-wrap: break-word;
+  width: 100%;
+}
+
+.dac-custom-search-load-more {
+  background: none;
+  border: none;
+  color: #333;
+  cursor: pointer;
+  display: block;
+  font-size: 14px;
+  font-weight: 700;
+  margin: 75px auto;
+  outline: none;
+  padding: 10px;
+}
+
+.dac-custom-search-load-more:hover {
+  opacity: 0.7;
+}
+
+.dac-custom-search-no-results {
+  color: #999;
+}
diff --git a/tools/droiddoc/templates-sdk/assets/js/docs.js b/tools/droiddoc/templates-sdk/assets/js/docs.js
index da6695d..c4a8150 100644
--- a/tools/droiddoc/templates-sdk/assets/js/docs.js
+++ b/tools/droiddoc/templates-sdk/assets/js/docs.js
@@ -2548,12 +2548,13 @@
 function submit_search() {
   var query = document.getElementById('search_autocomplete').value;
   location.hash = 'q=' + query;
-  loadSearchResults();
+  searchControl.query = query;
+  searchControl.init();
+  searchControl.trackSearchRequest(query);
   $("#searchResults").slideDown('slow', setStickyTop);
   return false;
 }
 
-
 function hideResults() {
   $("#searchResults").slideUp('fast', setStickyTop);
   $("#search-close").addClass("hide");
@@ -2562,119 +2563,248 @@
   $("#search_autocomplete").val("").blur();
 
   // reset the ajax search callback to nothing, so results don't appear unless ENTER
-  searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
-
-  // forcefully regain key-up event control (previously jacked by search api)
-  $("#search_autocomplete").keyup(function(event) {
-    return search_changed(event, false, toRoot);
-  });
+  searchControl.reset();
 
   return false;
 }
 
-
-
 /* ########################################################## */
 /* ################  CUSTOM SEARCH ENGINE  ################## */
 /* ########################################################## */
+var searchControl = null;
+var dacsearch = dacsearch || {};
 
-var searchControl;
-google.load('search', '1', {"callback" : function() {
-            searchControl = new google.search.SearchControl();
-          } });
+/**
+ * The custom search engine API.
+ * @constructor
+ */
+dacsearch.CustomSearchEngine = function() {
+  /**
+   * The last response from Google CSE.
+   * @private {Object}
+   */
+  this.resultQuery_ = {};
 
-function loadSearchResults() {
-  document.getElementById("search_autocomplete").style.color = "#000";
+  /** @private {?Element} */
+  this.searchResultEl_ = null;
 
-  searchControl = new google.search.SearchControl();
+  /** @private {?Element} */
+  this.searchInputEl_ = null;
 
-  // use our existing search form and use tabs when multiple searchers are used
-  drawOptions = new google.search.DrawOptions();
-  drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
-  drawOptions.setInput(document.getElementById("search_autocomplete"));
+  /** @private {string} */
+  this.query = '';
+};
 
-  // configure search result options
-  searchOptions = new google.search.SearcherOptions();
-  searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
+/**
+ * Initializes DAC's Google custom search engine.
+ * @export
+ */
+dacsearch.CustomSearchEngine.prototype.init = function() {
+  this.searchResultEl_ = $('#leftSearchControl');
+  this.searchResultEl_.empty();
+  this.searchInputEl_ = $('#search_autocomplete');
+  this.searchInputEl_.focus().val(this.query);
+  this.getResults_();
+  this.bindEvents_();
+};
 
-  // configure each of the searchers, for each tab
-  devSiteSearcher = new google.search.WebSearch();
-  devSiteSearcher.setUserDefinedLabel("All");
-  devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
 
-  designSearcher = new google.search.WebSearch();
-  designSearcher.setUserDefinedLabel("Design");
-  designSearcher.setSiteRestriction("http://developer.android.com/design/");
+/**
+ * Binds the keyup event to the search input.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.bindEvents_ = function() {
+  this.searchInputEl_.keyup(this.debounce_(function(e) {
+    var code = e.which;
+    if (code != 13) {
+      this.query = this.searchInputEl_.val();
+      location.hash = 'q=' + encodeURI(this.query);
+      this.searchResultEl_.empty();
+      this.getResults_();
+    }
+  }.bind(this), 250));
+};
 
-  trainingSearcher = new google.search.WebSearch();
-  trainingSearcher.setUserDefinedLabel("Training");
-  trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
 
-  guidesSearcher = new google.search.WebSearch();
-  guidesSearcher.setUserDefinedLabel("Guides");
-  guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
+/**
+ * Resets the search control.
+ */
+dacsearch.CustomSearchEngine.prototype.reset = function() {
+  this.query = '';
+  this.searchInputEl_.off('keyup');
+  this.searchResultEl_.empty();
+  this.updateResultTitle_();
+};
 
-  referenceSearcher = new google.search.WebSearch();
-  referenceSearcher.setUserDefinedLabel("Reference");
-  referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
 
-  googleSearcher = new google.search.WebSearch();
-  googleSearcher.setUserDefinedLabel("Google Services");
-  googleSearcher.setSiteRestriction("http://developer.android.com/google/");
+/**
+ * Updates the search query text at the top of the results.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.updateResultTitle_ = function() {
+  $("#searchTitle").html("Results for <em>" + this.query + "</em>");
+};
 
-  blogSearcher = new google.search.WebSearch();
-  blogSearcher.setUserDefinedLabel("Blog");
-  blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
 
-  // add each searcher to the search control
-  searchControl.addSearcher(devSiteSearcher, searchOptions);
-  searchControl.addSearcher(designSearcher, searchOptions);
-  searchControl.addSearcher(trainingSearcher, searchOptions);
-  searchControl.addSearcher(guidesSearcher, searchOptions);
-  searchControl.addSearcher(referenceSearcher, searchOptions);
-  searchControl.addSearcher(googleSearcher, searchOptions);
-  searchControl.addSearcher(blogSearcher, searchOptions);
+/**
+ * Makes the CSE api call and gets the results.
+ * @param {number=} opt_start The optional start index.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.getResults_ = function(opt_start) {
+  var lang = getLangPref();
+  // Fix zh-cn to be zh-CN.
+  lang = lang.replace(/-\w+/, function(m) { return m.toUpperCase(); });
+  var cseUrl = 'https://content.googleapis.com/customsearch/v1?';
+  var searchParams = {
+    cx: '000521750095050289010:zpcpi1ea4s8',
+    key: 'AIzaSyCFhbGnjW06dYwvRCU8h_zjdpS4PYYbEe8',
+    q: this.query,
+    start: opt_start || 1,
+    num: 6,
+    hl: lang,
+    fields: 'queries,items(pagemap,link,title,htmlSnippet,formattedUrl)'
+  };
 
-  // configure result options
-  searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
-  searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
-  searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
-  searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
+  $.get(cseUrl + $.param(searchParams), function(data) {
+    this.resultQuery_ = data;
+    this.renderResults_(data);
+    this.updateResultTitle_(this.query);
+  }.bind(this));
+};
 
-  // upon ajax search, refresh the url and search title
-  searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
-    updateResultTitle(query);
-    var query = document.getElementById('search_autocomplete').value;
-    location.hash = 'q=' + query;
-  });
 
-  // once search results load, set up click listeners
-  searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
-    addResultClickListeners();
-  });
+/**
+ * Renders the results.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.renderResults_ = function(results) {
+  var el = this.searchResultEl_;
 
-  // draw the search results box
-  searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
+  if (!results.items) {
+    el.append($('<div>').text('No results'));
+    return;
+  }
 
-  // get query and execute the search
-  searchControl.execute(decodeURI(getQuery(location.hash)));
+  for (var i = 0; i < results.items.length; i++) {
+    var item = results.items[i];
+    var hasImage = item.pagemap && item.pagemap.cse_thumbnail;
+    var sectionMatch = item.link.match(/developer\.android\.com\/(\w*)/);
+    var section = (sectionMatch && sectionMatch[1]) || 'blog';
 
-  document.getElementById("search_autocomplete").focus();
-  addTabListeners();
-}
-// End of loadSearchResults
+    var entry = $('<div>').addClass('dac-custom-search-entry cols');
+
+    if (hasImage) {
+      var image = item.pagemap.cse_thumbnail[0];
+      entry.append($('<div>').addClass('col-1of6')
+        .append($('<div>').addClass('dac-custom-search-image').css(
+        'background-image', 'url(' + image.src + ')')));
+    }
+
+    var linkTitleEl = $('<a>').text(item.title).attr('href', item.link);
+    linkTitleEl.click(function(e) {
+      ga('send', 'event', 'Google Custom Search',
+          'clicked: ' + linkTitleEl.attr('href'),
+          'query: ' + $("#search_autocomplete").val().toLowerCase());
+    });
+
+    var linkUrlEl = $('<a>').addClass('dac-custom-search-link').text(
+        item.formattedUrl).attr('href', item.link);
+    linkUrlEl.click(function(e) {
+      ga('send', 'event', 'Google Custom Search',
+          'clicked: ' + linkUrlEl.attr('href'),
+          'query: ' + $("#search_autocomplete").val().toLowerCase());
+    });
+
+
+    entry.append($('<div>').addClass(hasImage ? 'col-5of6' : 'col-6of6')
+      .append($('<p>').addClass('dac-custom-search-section').text(section))
+      .append(
+        linkTitleEl.wrap('<h2>').parent().addClass('dac-custom-search-title'))
+      .append($('<p>').addClass('dac-custom-search-snippet')
+      .html(item.htmlSnippet.replace(/<br>/g, ''))).append(linkUrlEl));
+
+    el.append(entry);
+  }
+
+  if ($('#dac-custom-search-load-more')) {
+    $('#dac-custom-search-load-more').remove();
+  }
+
+  if (results.queries.nextPage) {
+    var loadMoreButton = $('<button id="dac-custom-search-load-more">')
+      .addClass('dac-custom-search-load-more')
+      .text('Load more')
+      .click(function() {
+        this.loadMoreResults_();
+      }.bind(this));
+
+    el.append(loadMoreButton);
+  }
+};
+
+
+/**
+ * Loads more results.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.loadMoreResults_ = function() {
+  this.query = this.resultQuery_.queries.request[0].searchTerms;
+  var start = this.resultQuery_.queries.nextPage[0].startIndex;
+  var loadMoreButton = this.searchResultEl_.find(
+      '#dac-custom-search-load-more');
+  loadMoreButton.text('Loading more...');
+  this.getResults_(start);
+  this.trackSearchRequest(this.query + ' startIndex = ' + start);
+};
+
+
+/**
+ * Tracks a search request.
+ * @param {string} query The query for the request,
+ *                       includes start index if loading more results.
+ */
+dacsearch.CustomSearchEngine.prototype.trackSearchRequest = function(query) {
+  ga('send', 'event', 'Google Custom Search Submit', 'submit search query',
+      'query: ' + query);
+};
+
+
+/**
+ * Returns a function, that, as long as it continues to be invoked, will not
+ * be triggered. The function will be called after it stops being called for
+ * N milliseconds.
+ * @param {Function} func The function to debounce.
+ * @param {number} wait The number of milliseconds to wait before calling the function.
+ * @private
+ */
+dacsearch.CustomSearchEngine.prototype.debounce_ = function(func, wait) {
+  var timeout;
+  return function() {
+    var context = this, args = arguments;
+    var later = function() {
+      timeout = null;
+      func.apply(context, args);
+    };
+   clearTimeout(timeout);
+   timeout = setTimeout(later, wait);
+  };
+};
 
 
 google.setOnLoadCallback(function(){
+  searchControl = new dacsearch.CustomSearchEngine();
   if (location.hash.indexOf("q=") == -1) {
     // if there's no query in the url, don't search and make sure results are hidden
     $('#searchResults').hide();
     return;
   } else {
     // first time loading search results for this page
+    searchControl.query = decodeURI(location.hash.split('q=')[1]);
+    searchControl.init();
+    searchControl.trackSearchRequest(searchControl.query);
     $('#searchResults').slideDown('slow', setStickyTop);
     $("#search-close").removeClass("hide");
-    loadSearchResults();
   }
 }, true);
 
@@ -2703,7 +2833,7 @@
 
   // If the hash isn't a search query or there's an error in the query,
   // then adjust the scroll position to account for sticky header, then exit.
-  if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
+  if ((location.hash.indexOf("q=") == -1) || (searchControl.query == "undefined")) {
     // If the results pane is open, close it.
     if (!$("#searchResults").is(":hidden")) {
       hideResults();
@@ -2712,65 +2842,11 @@
     return;
   }
 
-  // Otherwise, we have a search to do
-  var query = decodeURI(getQuery(location.hash));
-  searchControl.execute(query);
   $('#searchResults').slideDown('slow', setStickyTop);
   $("#search_autocomplete").focus();
   $("#search-close").removeClass("hide");
-
-  updateResultTitle(query);
 });
 
-function updateResultTitle(query) {
-  $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
-}
-
-// forcefully regain key-up event control (previously jacked by search api)
-$("#search_autocomplete").keyup(function(event) {
-  return search_changed(event, false, toRoot);
-});
-
-// add event listeners to each tab so we can track the browser history
-function addTabListeners() {
-  var tabHeaders = $(".gsc-tabHeader");
-  for (var i = 0; i < tabHeaders.length; i++) {
-    $(tabHeaders[i]).attr("id",i).click(function() {
-    /*
-      // make a copy of the page numbers for the search left pane
-      setTimeout(function() {
-        // remove any residual page numbers
-        $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
-        // move the page numbers to the left position; make a clone,
-        // because the element is drawn to the DOM only once
-        // and because we're going to remove it (previous line),
-        // we need it to be available to move again as the user navigates
-        $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
-                        .clone().appendTo('#searchResults .gsc-tabsArea');
-        }, 200);
-      */
-    });
-  }
-  setTimeout(function(){$(tabHeaders[0]).click()},200);
-}
-
-// add analytics tracking events to each result link
-function addResultClickListeners() {
-  $("#searchResults a.gs-title").each(function(index, link) {
-    // When user clicks enter for Google search results, track it
-    $(link).click(function() {
-      ga('send', 'event', 'Google Click', 'clicked: ' + $(this).attr('href'),
-                'query: ' + $("#search_autocomplete").val().toLowerCase());
-    });
-  });
-}
-
-
-function getQuery(hash) {
-  var queryParts = hash.split('=');
-  return queryParts[1];
-}
-
 /* returns the given string with all HTML brackets converted to entities
     TODO: move this to the site's JS library */
 function escapeHTML(string) {
@@ -4868,6 +4944,11 @@
     this.el.removeClass('dac-active');
     $('body').removeClass('dac-modal-open');
     this.isOpen = false;
+    // When closing the modal for Android Studio downloads, reload the page
+    // because otherwise we might get stuck with post-download dialog state
+    if ($("[data-modal='studio_tos']").length) {
+      location.reload();
+    }
   };
 
   Modal.prototype.open_ = function() {
diff --git a/tools/droiddoc/templates-sdk/designpage.cs b/tools/droiddoc/templates-sdk/designpage.cs
index b945a1c..9393b64 100644
--- a/tools/droiddoc/templates-sdk/designpage.cs
+++ b/tools/droiddoc/templates-sdk/designpage.cs
@@ -9,10 +9,10 @@
     <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
     <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Roboto:regular,medium,thin,italic,mediumitalic">
     <link rel="stylesheet" href="<?cs var:toroot ?>assets/yui-3.3.0-reset-min.css">
-    <link rel="stylesheet" href="<?cs var:toroot ?>assets/design/default.css">
+    <link rel="stylesheet" href="<?cs var:toroot ?>assets/design/default.css?v=19">
     <script src="<?cs var:toroot ?>assets/jquery-1.6.2.min.js"></script>
     <script>var SITE_ROOT = '<?cs var:toroot ?>design';</script>
-    <script src="<?cs var:toroot ?>assets/design/default.js"></script>
+    <script src="<?cs var:toroot ?>assets/design/default.js?v=19"></script>
   </head>
   <body class="gc-documentation 
     <?cs if:(guide||develop||training||reference||tools||sdk) ?>develop<?cs
diff --git a/tools/droiddoc/templates-sdk/docpage.cs b/tools/droiddoc/templates-sdk/docpage.cs
index 809409e..d41adaa 100644
--- a/tools/droiddoc/templates-sdk/docpage.cs
+++ b/tools/droiddoc/templates-sdk/docpage.cs
@@ -193,11 +193,11 @@
 </div><!-- end doc-content -->
 
 <?cs include:"trailer.cs" ?>
-  <script src="https://developer.android.com/ytblogger_lists_unified.js?v=18" type="text/javascript"></script>
-  <script src="<?cs var:toroot ?>jd_lists_unified.js?v=18" type="text/javascript"></script>
-  <script src="<?cs var:toroot ?>jd_extras.js?v=18" type="text/javascript"></script>
-  <script src="<?cs var:toroot ?>jd_collections.js?v=18" type="text/javascript"></script>
-  <script src="<?cs var:toroot ?>jd_tag_helpers.js?v=18" type="text/javascript"></script>
+  <script src="https://developer.android.com/ytblogger_lists_unified.js?v=19" type="text/javascript"></script>
+  <script src="<?cs var:toroot ?>jd_lists_unified.js?v=19" type="text/javascript"></script>
+  <script src="<?cs var:toroot ?>jd_extras.js?v=19" type="text/javascript"></script>
+  <script src="<?cs var:toroot ?>jd_collections.js?v=19" type="text/javascript"></script>
+  <script src="<?cs var:toroot ?>jd_tag_helpers.js?v=19" type="text/javascript"></script>
 
 </body>
 </html>
diff --git a/tools/droiddoc/templates-sdk/head_tag.cs b/tools/droiddoc/templates-sdk/head_tag.cs
index fad25ed..27a66cb 100644
--- a/tools/droiddoc/templates-sdk/head_tag.cs
+++ b/tools/droiddoc/templates-sdk/head_tag.cs
@@ -52,7 +52,7 @@
   if:android.whichdoc != 'online' ?>http:<?cs
   /if ?>//fonts.googleapis.com/css?family=Roboto+Mono:400,500,700" title="roboto-mono" type="text/css"><?cs
 /if ?>
-<link href="<?cs var:toroot ?>assets/css/default.css?v=18" rel="stylesheet" type="text/css">
+<link href="<?cs var:toroot ?>assets/css/default.css?v=19" rel="stylesheet" type="text/css">
 
 <?cs if:reference && !(reference.gms || reference.gcm || preview) ?>
 <!-- FULLSCREEN STYLESHEET -->
diff --git a/tools/droiddoc/templates-sdk/sdkpage.cs b/tools/droiddoc/templates-sdk/sdkpage.cs
index 01b4ae2..30d6d04 100644
--- a/tools/droiddoc/templates-sdk/sdkpage.cs
+++ b/tools/droiddoc/templates-sdk/sdkpage.cs
@@ -86,7 +86,7 @@
   <tr>
     <td>Windows 32-bit</td>
     <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/repository/<?cs var:ndk.win32_download ?>"><?cs var:ndk.win32_download ?></a>
     </td>
     <td><?cs var:ndk.win32_bytes ?></td>
@@ -94,7 +94,7 @@
   </tr>
  <!-- <tr>
    <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/ndk/<?cs var:ndk.win32.legacy_download ?>"><?cs var:ndk.win32.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.win32.legacy_bytes ?></td>
@@ -103,7 +103,7 @@
   <tr>
     <td>Windows 64-bit</td>
     <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/repository/<?cs var:ndk.win64_download ?>"><?cs var:ndk.win64_download ?></a>
     </td>
     <td><?cs var:ndk.win64_bytes ?></td>
@@ -111,7 +111,7 @@
   </tr>
  <!--  <tr>
     <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/repository/<?cs var:ndk.win64.legacy_download ?>"><?cs var:ndk.win64.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.win64.legacy_bytes ?></td>
@@ -121,7 +121,7 @@
   <tr>
     <td>Mac OS X 32-bit</td>
     <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/repository/<?cs var:ndk.mac32_download ?>"><?cs var:ndk.mac32_download ?></a>
     </td>
     <td><?cs var:ndk.mac32_bytes ?></td>
@@ -130,7 +130,7 @@
  <!-- (this item is deprecated)
   <tr>
     <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/repository/<?cs var:ndk.mac32.legacy_download ?>"><?cs var:ndk.mac32.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.mac32.legacy_bytes ?></td>
@@ -138,7 +138,7 @@
   </tr> -->
     <td>Mac OS X 64-bit</td>
     <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/repository/<?cs var:ndk.mac64_download ?>"><?cs var:ndk.mac64_download ?></a>
     </td>
     <td><?cs var:ndk.mac64_bytes ?></td>
@@ -146,7 +146,7 @@
   </tr>
  <!--  <tr>
     <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/repository/<?cs var:ndk.mac64.legacy_download ?>"><?cs var:ndk.mac64.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.mac64.legacy_bytes ?></td>
@@ -154,7 +154,7 @@
   </tr> -->
  <!--  <tr>
     <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/repository/<?cs var:ndk.linux32.legacy_download ?>"><?cs var:ndk.linux32.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.linux32.legacy_bytes ?></td>
@@ -163,7 +163,7 @@
   <tr>
     <td>Linux 64-bit (x86)</td>
     <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/repository/<?cs var:ndk.linux64_download ?>"><?cs var:ndk.linux64_download ?></a>
     </td>
     <td><?cs var:ndk.linux64_bytes ?></td>
@@ -171,7 +171,7 @@
   </tr>
   <!--  <tr>
     <td>
-  <a onClick="return onDownload(this)"
+  <a onClick="return onDownload(this)" data-modal-toggle="ndk_tos"
      href="http://dl.google.com/android/repository/<?cs var:ndk.linux64.legacy_download ?>"><?cs var:ndk.linux64.legacy_download ?></a>
     </td>
     <td><?cs var:ndk.linux64.legacy_bytes ?></td>
@@ -184,16 +184,13 @@
   <?cs call:tag_list(root.descr) ?>
 
 
-
+<?cs ########  The NDK version of the download script ######### ?>
 <script>
   function onDownload(link) {
 
     $("#downloadForRealz").html("Download " + $(link).text());
     $("#downloadForRealz").attr('href',$(link).attr('href'));
 
-    $("#tos").fadeIn('slow');
-
-    location.hash = "download";
     return false;
   }
 
@@ -206,25 +203,28 @@
     }
   }
 
-  function onDownloadNdkForRealz(link) {
+
+  function onDownloadForRealz(link) {
     if ($("input#agree").is(':checked')) {
-      $("#tos").fadeOut('slow');
-
-      $('html, body').animate({
-          scrollTop: $("#Installing").offset().top
-        }, 800, function() {
-          $("#Installing").click();
+      $("div.sdk-terms").slideUp();
+      $("h2#tos-header").text('Now downloading...');
+      $(".sdk-terms-intro").text('Your download is in progress.');
+      $("#sdk-terms-form").fadeOut('slow', function() {
+        setTimeout(function() {
+          // close the dialog
+          $('#ndk_tos').trigger('modal-close');
+          // reload to refresh the tos or optionally forward the user
+           location.reload();
+        }, 3000);
       });
-
+      ga('send', 'event', 'SDK', 'NDK tools', $("#downloadForRealz").html());
       return true;
     } else {
-      $("label#agreeLabel").parent().stop().animate({color: "#258AAF"}, 200,
-        function() {$("label#agreeLabel").parent().stop().animate({color: "#222"}, 200)}
-      );
       return false;
     }
   }
 
+
   $(window).hashchange( function(){
     if (location.hash == "") {
       location.reload();
@@ -256,153 +256,173 @@
 <?cs call:tag_list(root.descr) ?>
 
 
+<section id="downloads" class="dac-section dac-small">
+<div class="wrap">
+
+<h2 class="norule">Start using Android Studio today</h2>
+
+<p>Android Studio includes all the tools you need to build apps for Android.</p>
+
+<div style="float:left;margin-right:40px;width:auto;">
+<p>
+  <a class="landing-button green download-bundle-button"
+    data-modal-toggle="studio_tos">Download Android Studio 2.0<br>
+  <span class="small"></span></a>
+</p>
+</div>
+
+<div style="float:left;width:auto;margin-bottom:40px">
+<ul class="nolist" style="text-transform: uppercase;margin:8px 0">
+<li>Version: <?cs var:studio.version ?></li>
+<li>Release date: <?cs var:studio.release.date ?></li>
+</ul>
+</div>
 
 
-<div class="pax col-13 online" style="margin:0;">
 
-
-<h3>SDK Tools Only</h3>
-
-<p>If you prefer to use a different IDE or run the tools from the
-command line or with build scripts, you can instead download the stand-alone Android SDK Tools.
-These packages provide the basic SDK tools for app development, without an IDE.
-Also see the <a href="<?cs var:toroot ?>tools/sdk/tools-notes.html">SDK tools release notes</a>.</p>
+<h4 style="clear:left;margin-top:40px">Select a different platform</h4>
 
   <table class="download">
     <tr>
       <th>Platform</th>
-      <th>Package</th>
+      <th>Android Studio package</th>
       <th>Size</th>
-      <th>SHA-1 Checksum</th>
+      <th>SHA-1 checksum</th>
   </tr>
   <tr>
-    <td rowspan="2">Windows</td>
-    <td>
-  <a onclick="return onDownload(this)" id="win-tools" href="http://dl.google.com/android/<?cs
-var:sdk.win_installer
-?>"><?cs var:sdk.win_installer ?></a> (Recommended)
-    </td>
-    <td><?cs var:sdk.win_installer_bytes ?> bytes</td>
-    <td><?cs var:sdk.win_installer_checksum ?></td>
-  </tr>
-  <tr>
-    <!-- blank TD from Windows rowspan -->
-    <td>
-  <a onclick="return onDownload(this)" href="http://dl.google.com/android/<?cs var:sdk.win_download
-?>"><?cs var:sdk.win_download ?></a>
-    </td>
-    <td><?cs var:sdk.win_bytes ?> bytes</td>
-    <td><?cs var:sdk.win_checksum ?></td>
-  </tr>
-  <tr>
-    <td><nobr>Mac OS X</nobr></td>
-    <td>
-  <a onclick="return onDownload(this)" id="mac-tools" href="http://dl.google.com/android/<?cs
-var:sdk.mac_download
-?>"><?cs var:sdk.mac_download ?></a>
-    </td>
-    <td><?cs var:sdk.mac_bytes ?> bytes</td>
-    <td><?cs var:sdk.mac_checksum ?></td>
-  </tr>
-  <tr>
-    <td>Linux</td>
-    <td>
-  <a onclick="return onDownload(this)" id="linux-tools" href="http://dl.google.com/android/<?cs
-var:sdk.linux_download
-?>"><?cs var:sdk.linux_download ?></a>
-    </td>
-    <td><?cs var:sdk.linux_bytes ?> bytes</td>
-    <td><?cs var:sdk.linux_checksum ?></td>
-  </tr>
-  </table>
-
-
-
-<h3>All Android Studio Packages</h3>
-
-<p>Select a specific Android Studio package for your platform. Also see the
-<a href="<?cs var:toroot ?>tools/revisions/studio.html">Android Studio release notes</a>.</p>
-
-  <table class="download">
-    <tr>
-      <th>Platform</th>
-      <th>Package</th>
-      <th>Size</th>
-      <th>SHA-1 Checksum</th>
-  </tr>
-
-  <tr>
     <td rowspan="3">Windows</td>
     <td>
-  <a onclick="return onDownload(this,false,true)" id="win-bundle"
+  <a onclick="return onDownload(this,false,true)" id="win-bundle" data-modal-toggle="studio_tos"
     href="https://dl.google.com/dl/android/studio/install/<?cs var:studio.version ?>/<?cs var:studio.win_bundle_exe_download ?>"
-    ><?cs var:studio.win_bundle_exe_download ?></a><br>(Recommended)
+    ><?cs var:studio.win_bundle_exe_download ?></a><br>
+    Includes Windows installer (recommended)
     </td>
-    <td><?cs var:studio.win_bundle_exe_bytes ?> bytes</td>
+    <td id="win-bundle-size"><?cs call:size_in_mb(studio.win_bundle_exe_bytes) ?> MB
+      <br>(<?cs var:studio.win_bundle_exe_bytes ?> bytes)</td>
     <td><?cs var:studio.win_bundle_exe_checksum ?></td>
   </tr>
-
   <tr>
     <!-- blank TD from Windows rowspan -->
     <td>
-  <a onclick="return onDownload(this,false,true)" id="win-bundle-notools"
-    href="https://dl.google.com/dl/android/studio/install/<?cs var:studio.version ?>/<?cs var:studio.win_notools_exe_download ?>"
-    ><?cs var:studio.win_notools_exe_download ?></a><br>(No SDK tools included)
-    </td>
-    <td><?cs var:studio.win_notools_exe_bytes ?> bytes</td>
-    <td><?cs var:studio.win_notools_exe_checksum ?></td>
-  </tr>
-
-  <tr>
-    <!-- blank TD from Windows rowspan -->
-    <td>
-  <a onclick="return onDownload(this,false,true)" id="win-bundle-zip"
+  <a onclick="return onDownload(this,false,true)" id="win-bundle-zip" data-modal-toggle="studio_tos"
     href="https://dl.google.com/dl/android/studio/ide-zips/<?cs var:studio.version ?>/<?cs var:studio.win_bundle_download ?>"
-    ><?cs var:studio.win_bundle_download ?></a>
+    ><?cs var:studio.win_bundle_download ?></a><br>
+    No Windows installer
     </td>
-    <td><?cs var:studio.win_bundle_bytes ?> bytes</td>
+    <td><?cs call:size_in_mb(studio.win_bundle_bytes) ?> MB
+      <br>(<?cs var:studio.win_bundle_bytes ?> bytes)</td>
     <td><?cs var:studio.win_bundle_checksum ?></td>
   </tr>
-
+  <tr>
+    <!-- blank TD from Windows rowspan -->
+    <td>
+  <a onclick="return onDownload(this,false,true)" id="win-bundle-notools" data-modal-toggle="studio_tos"
+    href="https://dl.google.com/dl/android/studio/install/<?cs var:studio.version ?>/<?cs var:studio.win_notools_exe_download ?>"
+    ><?cs var:studio.win_notools_exe_download ?></a><br>
+    Includes Windows installer but no SDK tools
+    </td>
+    <td><?cs call:size_in_mb(studio.win_notools_exe_bytes) ?> MB
+      <br>(<?cs var:studio.win_notools_exe_bytes ?> bytes)</td>
+    <td><?cs var:studio.win_notools_exe_checksum ?></td>
+  </tr>
   <tr>
     <td><nobr>Mac OS X</nobr></td>
     <td>
-  <a onclick="return onDownload(this,false,true)" id="mac-bundle"
+  <a onclick="return onDownload(this,false,true)" id="mac-bundle" data-modal-toggle="studio_tos"
     href="https://dl.google.com/dl/android/studio/install/<?cs var:studio.version ?>/<?cs var:studio.mac_bundle_download ?>"
     ><?cs var:studio.mac_bundle_download ?></a>
     </td>
-    <td><?cs var:studio.mac_bundle_bytes ?> bytes</td>
+    <td id="mac-bundle-size"><?cs call:size_in_mb(studio.mac_bundle_bytes) ?> MB
+      <br>(<?cs var:studio.mac_bundle_bytes ?> bytes)</td>
     <td><?cs var:studio.mac_bundle_checksum ?></td>
   </tr>
-
   <tr>
     <td>Linux</td>
     <td>
-  <a onclick="return onDownload(this,false,true)" id="linux-bundle"
+  <a onclick="return onDownload(this,false,true)" id="linux-bundle" data-modal-toggle="studio_tos"
     href="https://dl.google.com/dl/android/studio/ide-zips/<?cs var:studio.version ?>/<?cs var:studio.linux_bundle_download ?>"
     ><?cs var:studio.linux_bundle_download ?></a>
     </td>
-    <td><?cs var:studio.linux_bundle_bytes ?> bytes</td>
+    <td id="linux-bundle-size"><?cs call:size_in_mb(studio.linux_bundle_bytes) ?> MB
+      <br>(<?cs var:studio.linux_bundle_bytes ?> bytes)</td>
     <td><?cs var:studio.linux_bundle_checksum ?></td>
   </tr>
   </table>
 
 
 
-</div><!-- end pax -->
+<h4 class="norule" style="margin-top:40px">Get just the SDK tools</h4>
+
+<p>If you do not need Android Studio, you can download the basic
+Android SDK tools below.</p>
+
+  <table class="download">
+    <tr>
+      <th>Platform</th>
+      <th>SDK tools package</th>
+      <th>Size</th>
+      <th>SHA-1 checksum</th>
+  </tr>
+  <tr>
+    <td rowspan="2">Windows</td>
+    <td>
+  <a onclick="return onDownload(this)" id="win-tools" data-modal-toggle="studio_tos"
+    href="//dl.google.com/android/<?cs
+var:sdk.win_installer
+?>"><?cs var:sdk.win_installer ?></a><br>
+    Includes Windows installer
+    </td>
+    <td><?cs call:size_in_mb(sdk.win_installer_bytes) ?> MB
+      <br>(<?cs var:sdk.win_installer_bytes ?> bytes)</td>
+    <td><?cs var:sdk.win_installer_checksum ?></td>
+  </tr>
+  <tr>
+    <!-- blank TD from Windows rowspan -->
+    <td>
+  <a onclick="return onDownload(this)" id="win-tools2" data-modal-toggle="studio_tos"
+    href="//dl.google.com/android/<?cs var:sdk.win_download
+?>"><?cs var:sdk.win_download ?></a><br>
+    No Windows installer
+    </td>
+    <td><?cs call:size_in_mb(sdk.win_bytes) ?> MB
+      <br>(<?cs var:sdk.win_bytes ?> bytes)</td>
+    <td><?cs var:sdk.win_checksum ?></td>
+  </tr>
+  <tr>
+    <td><nobr>Mac OS X</nobr></td>
+    <td>
+  <a onclick="return onDownload(this)" id="mac-tools" data-modal-toggle="studio_tos"
+    href="//dl.google.com/android/<?cs
+var:sdk.mac_download
+?>"><?cs var:sdk.mac_download ?></a>
+    </td>
+    <td><?cs call:size_in_mb(sdk.mac_bytes) ?> MB
+      <br>(<?cs var:sdk.mac_bytes ?> bytes)</td>
+    <td><?cs var:sdk.mac_checksum ?></td>
+  </tr>
+  <tr>
+    <td>Linux</td>
+    <td>
+  <a onclick="return onDownload(this)" id="linux-tools" data-modal-toggle="studio_tos"
+    href="//dl.google.com/android/<?cs
+var:sdk.linux_download
+?>"><?cs var:sdk.linux_download ?></a>
+    </td>
+    <td><?cs call:size_in_mb(sdk.linux_bytes) ?> MB
+      <br>(<?cs var:sdk.linux_bytes ?> bytes)</td>
+    <td><?cs var:sdk.linux_checksum ?></td>
+  </tr>
+  </table>
+  <p>
+Also see the <a href="<?cs var:toroot ?>tools/sdk/tools-notes.html">SDK
+tools release notes</a>.</p>
+
+  </div><!-- end wrap -->
+  </section>
 
 
-
-
-
+<?cs ########  The Android Studio version of the download script ######### ?>
 <script>
-
-  if (location.hash == "#Requirements") {
-    $('.reqs').show();
-  } else if (location.hash == "#ExistingIDE") {
-	 $('.ide').show();
-  }
-
   var os;
   var bundlename;
   var $toolslink;
@@ -427,7 +447,9 @@
     $('#not-supported').hide();
 
     /* set up primary Android Studio download button */
-    $('.download-bundle-button > .small').html(" for " + os);
+    idname = bundlename + "-size";
+    sizeMB = $(idname).text().split(' MB')[0];
+    $('.download-bundle-button > .small').html(" for " + os + " <em>(" + sizeMB + " MB)</em>");
     $('.download-bundle-button').click(function() {return onDownload(this,true,true);}).attr('href', bundlename);
   }
 
@@ -445,13 +467,10 @@
     if (bundle && !button) {
       $("a#downloadForRealz").attr("name", "#" + $(link).attr('id'));
     } else {
+      $("h2#tos-header").text('Download the Android SDK Tools');
       $("a#downloadForRealz").attr("name", $(link).attr('href'));
     }
 
-    $("#tos").show();
-    $("#landing").hide();
-
-    location.hash = "top";
     return false;
   }
 
@@ -479,22 +498,24 @@
 
   function onDownloadForRealz(link) {
     if ($("input#agree").is(':checked')) {
-      location.hash = "";
-      location.hash = "top";
       $("div.sdk-terms").slideUp();
-      $("h1#tos-header").text('Now downloading...');
-      $(".sdk-terms-intro").text('You\'ll be redirected to the install instructions in a moment.');
-      $("#sdk-terms-form").fadeOut('slow', function() {
-        setTimeout(function() {
-          if ($("#downloadForRealz").attr('bundle') == 'true') {
-            // User downloaded the studio Bundle
-            window.location = "/sdk/installing/index.html?pkg=studio";
-          } else {
-            // User downloaded the SDK Tools
-            window.location = "/sdk/installing/index.html?pkg=tools";
-          }
-        }, 3000);
-      });
+      if ($("#downloadForRealz").attr('bundle') == 'true') {
+        $("h2#tos-header").text('Now downloading Android Studio!');
+        $(".sdk-terms-intro").text('Redirecting to the install instructions...');
+        $("#sdk-terms-form").slideUp(function() {
+          setTimeout(function() {
+            window.location = "/sdk/installing/index.html";
+          }, 2000);
+        });
+      } else {
+        $("h2#tos-header").text('Now downloading the Android SDK Tools!');
+        $(".sdk-terms-intro").html("<p>Because you've chosen to download " +
+          "only the Android SDK tools (and not Android Studio), there are no " +
+          "setup procedures to follow.</p><p>For information about how to " +
+          "keep your SDK tools up to date, refer to the " +
+          "<a href='/tools/help/sdk-manager.html'>SDK Manager</a> guide.</p>");
+        $("#sdk-terms-form").slideUp();
+      }
       ga('send', 'event', 'SDK', 'IDE and Tools', $("#downloadForRealz").html());
       return true;
     } else {
@@ -512,7 +533,7 @@
 
 
 
-</div><!-- end the wrapper used for relative/absolute positions  -->
+
 <?cs # THIS DIV WAS OPENED IN INDEX.JD ?>
 
 
@@ -561,6 +582,11 @@
 </div><!-- end g-unit -->
 
 <?cs include:"trailer.cs" ?>
+<script src="https://developer.android.com/ytblogger_lists_unified.js?v=17" type="text/javascript"></script>
+<script src="/jd_lists_unified.js?v=17" type="text/javascript"></script>
+<script src="/jd_extras.js?v=17" type="text/javascript"></script>
+<script src="/jd_collections.js?v=17" type="text/javascript"></script>
+<script src="/jd_tag_helpers.js?v=17" type="text/javascript"></script>
 
 <!-- Start of Tag -->
 <script type="text/javascript">
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index eab8113..f2bf1e1 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -28,6 +28,7 @@
   print >> sys.stderr, "Python 2.7 or newer is required."
   sys.exit(1)
 
+import datetime
 import errno
 import os
 import tempfile
@@ -40,6 +41,9 @@
 
 OPTIONS.add_missing = False
 OPTIONS.rebuild_recovery = False
+OPTIONS.replace_verity_public_key = False
+OPTIONS.replace_verity_private_key = False
+OPTIONS.verity_signer_path = None
 
 def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None):
   """Turn the contents of SYSTEM into a system image and store it in
@@ -119,6 +123,12 @@
   if fstab:
     image_props["fs_type"] = fstab["/" + what].fs_type
 
+  # Use a fixed timestamp (01/01/2009) when packaging the image.
+  # Bug: 24377993
+  epoch = datetime.datetime.fromtimestamp(0)
+  timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
+  image_props["timestamp"] = int(timestamp)
+
   if what == "system":
     fs_config_prefix = ""
   else:
@@ -170,6 +180,12 @@
 
   print "creating userdata.img..."
 
+  # Use a fixed timestamp (01/01/2009) when packaging the image.
+  # Bug: 24377993
+  epoch = datetime.datetime.fromtimestamp(0)
+  timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
+  image_props["timestamp"] = int(timestamp)
+
   # The name of the directory it is making an image out of matters to
   # mkyaffs2image.  So we create a temp dir, and within it we create an
   # empty dir named "data", and build the image from that.
@@ -207,6 +223,12 @@
 
   print "creating cache.img..."
 
+  # Use a fixed timestamp (01/01/2009) when packaging the image.
+  # Bug: 24377993
+  epoch = datetime.datetime.fromtimestamp(0)
+  timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
+  image_props["timestamp"] = int(timestamp)
+
   # The name of the directory it is making an image out of matters to
   # mkyaffs2image.  So we create a temp dir, and within it we create an
   # empty dir named "cache", and build the image from that.
@@ -296,18 +318,27 @@
   common.ZipClose(output_zip)
 
 def main(argv):
-  def option_handler(o, _):
+  def option_handler(o, a):
     if o in ("-a", "--add_missing"):
       OPTIONS.add_missing = True
     elif o in ("-r", "--rebuild_recovery",):
       OPTIONS.rebuild_recovery = True
+    elif o == "--replace_verity_private_key":
+      OPTIONS.replace_verity_private_key = (True, a)
+    elif o == "--replace_verity_public_key":
+      OPTIONS.replace_verity_public_key = (True, a)
+    elif o == "--verity_signer_path":
+      OPTIONS.verity_signer_path = a
     else:
       return False
     return True
 
   args = common.ParseOptions(
       argv, __doc__, extra_opts="ar",
-      extra_long_opts=["add_missing", "rebuild_recovery"],
+      extra_long_opts=["add_missing", "rebuild_recovery",
+                       "replace_verity_public_key=",
+                       "replace_verity_private_key=",
+                       "verity_signer_path="],
       extra_option_handler=option_handler)
 
 
diff --git a/tools/releasetools/blockimgdiff.py b/tools/releasetools/blockimgdiff.py
index 6ed9ca2..cb6fc85 100644
--- a/tools/releasetools/blockimgdiff.py
+++ b/tools/releasetools/blockimgdiff.py
@@ -16,6 +16,7 @@
 
 from collections import deque, OrderedDict
 from hashlib import sha1
+import common
 import heapq
 import itertools
 import multiprocessing
@@ -105,11 +106,13 @@
     assert not (trim and pad)
 
     partial = len(self.data) % self.blocksize
+    padded = False
     if partial > 0:
       if trim:
         self.data = self.data[:-partial]
       elif pad:
         self.data += '\0' * (self.blocksize - partial)
+        padded = True
       else:
         raise ValueError(("data for DataImage must be multiple of %d bytes "
                           "unless trim or pad is specified") %
@@ -119,14 +122,23 @@
 
     self.total_blocks = len(self.data) / self.blocksize
     self.care_map = RangeSet(data=(0, self.total_blocks))
-    self.clobbered_blocks = RangeSet()
+    # When the last block is padded, we always write the whole block even for
+    # incremental OTAs. Because otherwise the last block may get skipped if
+    # unchanged for an incremental, but would fail the post-install
+    # verification if it has non-zero contents in the padding bytes.
+    # Bug: 23828506
+    if padded:
+      self.clobbered_blocks = RangeSet(
+          data=(self.total_blocks-1, self.total_blocks))
+    else:
+      self.clobbered_blocks = RangeSet()
     self.extended = RangeSet()
 
     zero_blocks = []
     nonzero_blocks = []
     reference = '\0' * self.blocksize
 
-    for i in range(self.total_blocks):
+    for i in range(self.total_blocks-1 if padded else self.total_blocks):
       d = self.data[i*self.blocksize : (i+1)*self.blocksize]
       if d == reference:
         zero_blocks.append(i)
@@ -138,14 +150,18 @@
     self.file_map = {"__ZERO": RangeSet(zero_blocks),
                      "__NONZERO": RangeSet(nonzero_blocks)}
 
+    if self.clobbered_blocks:
+      self.file_map["__COPY"] = self.clobbered_blocks
+
   def ReadRangeSet(self, ranges):
     return [self.data[s*self.blocksize:e*self.blocksize] for (s, e) in ranges]
 
   def TotalSha1(self, include_clobbered_blocks=False):
-    # DataImage always carries empty clobbered_blocks, so
-    # include_clobbered_blocks can be ignored.
-    assert self.clobbered_blocks.size() == 0
-    return sha1(self.data).hexdigest()
+    if not include_clobbered_blocks:
+      ranges = self.care_map.subtract(self.clobbered_blocks)
+      return sha1(self.ReadRangeSet(ranges)).hexdigest()
+    else:
+      return sha1(self.data).hexdigest()
 
 
 class Transfer(object):
@@ -173,6 +189,12 @@
     return (sum(sr.size() for (_, sr) in self.stash_before) -
             sum(sr.size() for (_, sr) in self.use_stash))
 
+  def ConvertToNew(self):
+    assert self.style != "new"
+    self.use_stash = []
+    self.style = "new"
+    self.src_ranges = RangeSet()
+
   def __str__(self):
     return (str(self.id) + ": <" + str(self.src_ranges) + " " + self.style +
             " to " + str(self.tgt_ranges) + ">")
@@ -267,6 +289,10 @@
       self.ReverseBackwardEdges()
       self.ImproveVertexSequence()
 
+    # Ensure the runtime stash size is under the limit.
+    if self.version >= 2 and common.OPTIONS.cache_size is not None:
+      self.ReviseStashSize()
+
     # Double-check our work.
     self.AssertSequenceGood()
 
@@ -286,7 +312,6 @@
     out = []
 
     total = 0
-    performs_read = False
 
     stashes = {}
     stashed_blocks = 0
@@ -398,7 +423,6 @@
         out.append("%s %s\n" % (xf.style, xf.tgt_ranges.to_string_raw()))
         total += tgt_size
       elif xf.style == "move":
-        performs_read = True
         assert xf.tgt_ranges
         assert xf.src_ranges.size() == tgt_size
         if xf.src_ranges != xf.tgt_ranges:
@@ -423,7 +447,6 @@
                 xf.tgt_ranges.to_string_raw(), src_str))
           total += tgt_size
       elif xf.style in ("bsdiff", "imgdiff"):
-        performs_read = True
         assert xf.tgt_ranges
         assert xf.src_ranges
         if self.version == 1:
@@ -460,9 +483,20 @@
       if free_string:
         out.append("".join(free_string))
 
-      # sanity check: abort if we're going to need more than 512 MB if
-      # stash space
-      assert max_stashed_blocks * self.tgt.blocksize < (512 << 20)
+      if self.version >= 2:
+        # Sanity check: abort if we're going to need more stash space than
+        # the allowed size (cache_size * threshold). There are two purposes
+        # of having a threshold here. a) Part of the cache may have been
+        # occupied by some recovery logs. b) It will buy us some time to deal
+        # with the oversize issue.
+        cache_size = common.OPTIONS.cache_size
+        stash_threshold = common.OPTIONS.stash_threshold
+        max_allowed = cache_size * stash_threshold
+        assert max_stashed_blocks * self.tgt.blocksize < max_allowed, \
+               'Stash size %d (%d * %d) exceeds the limit %d (%d * %.2f)' % (
+                   max_stashed_blocks * self.tgt.blocksize, max_stashed_blocks,
+                   self.tgt.blocksize, max_allowed, cache_size,
+                   stash_threshold)
 
     # Zero out extended blocks as a workaround for bug 20881595.
     if self.tgt.extended:
@@ -489,8 +523,81 @@
         f.write(i)
 
     if self.version >= 2:
-      print("max stashed blocks: %d  (%d bytes)\n" % (
-          max_stashed_blocks, max_stashed_blocks * self.tgt.blocksize))
+      max_stashed_size = max_stashed_blocks * self.tgt.blocksize
+      max_allowed = common.OPTIONS.cache_size * common.OPTIONS.stash_threshold
+      print("max stashed blocks: %d  (%d bytes), limit: %d bytes (%.2f%%)\n" % (
+          max_stashed_blocks, max_stashed_size, max_allowed,
+          max_stashed_size * 100.0 / max_allowed))
+
+  def ReviseStashSize(self):
+    print("Revising stash size...")
+    stashes = {}
+
+    # Create the map between a stash and its def/use points. For example, for a
+    # given stash of (idx, sr), stashes[idx] = (sr, def_cmd, use_cmd).
+    for xf in self.transfers:
+      # Command xf defines (stores) all the stashes in stash_before.
+      for idx, sr in xf.stash_before:
+        stashes[idx] = (sr, xf)
+
+      # Record all the stashes command xf uses.
+      for idx, _ in xf.use_stash:
+        stashes[idx] += (xf,)
+
+    # Compute the maximum blocks available for stash based on /cache size and
+    # the threshold.
+    cache_size = common.OPTIONS.cache_size
+    stash_threshold = common.OPTIONS.stash_threshold
+    max_allowed = cache_size * stash_threshold / self.tgt.blocksize
+
+    stashed_blocks = 0
+    new_blocks = 0
+
+    # Now go through all the commands. Compute the required stash size on the
+    # fly. If a command requires excess stash than available, it deletes the
+    # stash by replacing the command that uses the stash with a "new" command
+    # instead.
+    for xf in self.transfers:
+      replaced_cmds = []
+
+      # xf.stash_before generates explicit stash commands.
+      for idx, sr in xf.stash_before:
+        if stashed_blocks + sr.size() > max_allowed:
+          # We cannot stash this one for a later command. Find out the command
+          # that will use this stash and replace the command with "new".
+          use_cmd = stashes[idx][2]
+          replaced_cmds.append(use_cmd)
+          print("%10d  %9s  %s" % (sr.size(), "explicit", use_cmd))
+        else:
+          stashed_blocks += sr.size()
+
+      # xf.use_stash generates free commands.
+      for _, sr in xf.use_stash:
+        stashed_blocks -= sr.size()
+
+      # "move" and "diff" may introduce implicit stashes in BBOTA v3. Prior to
+      # ComputePatches(), they both have the style of "diff".
+      if xf.style == "diff" and self.version >= 3:
+        assert xf.tgt_ranges and xf.src_ranges
+        if xf.src_ranges.overlaps(xf.tgt_ranges):
+          if stashed_blocks + xf.src_ranges.size() > max_allowed:
+            replaced_cmds.append(xf)
+            print("%10d  %9s  %s" % (xf.src_ranges.size(), "implicit", xf))
+
+      # Replace the commands in replaced_cmds with "new"s.
+      for cmd in replaced_cmds:
+        # It no longer uses any commands in "use_stash". Remove the def points
+        # for all those stashes.
+        for idx, sr in cmd.use_stash:
+          def_cmd = stashes[idx][1]
+          assert (idx, sr) in def_cmd.stash_before
+          def_cmd.stash_before.remove((idx, sr))
+          new_blocks += sr.size()
+
+        cmd.ConvertToNew()
+
+    print("  Total %d blocks are packed as new blocks due to insufficient "
+          "cache size." % (new_blocks,))
 
   def ComputePatches(self, prefix):
     print("Reticulating splines...")
@@ -847,6 +954,57 @@
           a.goes_after[b] = size
 
   def FindTransfers(self):
+    """Parse the file_map to generate all the transfers."""
+
+    def AddTransfer(tgt_name, src_name, tgt_ranges, src_ranges, style, by_id,
+                    split=False):
+      """Wrapper function for adding a Transfer().
+
+      For BBOTA v3, we need to stash source blocks for resumable feature.
+      However, with the growth of file size and the shrink of the cache
+      partition source blocks are too large to be stashed. If a file occupies
+      too many blocks (greater than MAX_BLOCKS_PER_DIFF_TRANSFER), we split it
+      into smaller pieces by getting multiple Transfer()s.
+
+      The downside is that after splitting, we can no longer use imgdiff but
+      only bsdiff."""
+
+      MAX_BLOCKS_PER_DIFF_TRANSFER = 1024
+
+      # We care about diff transfers only.
+      if style != "diff" or not split:
+        Transfer(tgt_name, src_name, tgt_ranges, src_ranges, style, by_id)
+        return
+
+      # Change nothing for small files.
+      if (tgt_ranges.size() <= MAX_BLOCKS_PER_DIFF_TRANSFER and
+          src_ranges.size() <= MAX_BLOCKS_PER_DIFF_TRANSFER):
+        Transfer(tgt_name, src_name, tgt_ranges, src_ranges, style, by_id)
+        return
+
+      pieces = 0
+      while (tgt_ranges.size() > MAX_BLOCKS_PER_DIFF_TRANSFER and
+             src_ranges.size() > MAX_BLOCKS_PER_DIFF_TRANSFER):
+        tgt_split_name = "%s-%d" % (tgt_name, pieces)
+        src_split_name = "%s-%d" % (src_name, pieces)
+        tgt_first = tgt_ranges.first(MAX_BLOCKS_PER_DIFF_TRANSFER)
+        src_first = src_ranges.first(MAX_BLOCKS_PER_DIFF_TRANSFER)
+        Transfer(tgt_split_name, src_split_name, tgt_first, src_first, style,
+                 by_id)
+
+        tgt_ranges = tgt_ranges.subtract(tgt_first)
+        src_ranges = src_ranges.subtract(src_first)
+        pieces += 1
+
+      # Handle remaining blocks.
+      if tgt_ranges.size() or src_ranges.size():
+        # Must be both non-empty.
+        assert tgt_ranges.size() and src_ranges.size()
+        tgt_split_name = "%s-%d" % (tgt_name, pieces)
+        src_split_name = "%s-%d" % (src_name, pieces)
+        Transfer(tgt_split_name, src_split_name, tgt_ranges, src_ranges, style,
+                 by_id)
+
     empty = RangeSet()
     for tgt_fn, tgt_ranges in self.tgt.file_map.items():
       if tgt_fn == "__ZERO":
@@ -854,28 +1012,28 @@
         # in any file and that are filled with zeros.  We have a
         # special transfer style for zero blocks.
         src_ranges = self.src.file_map.get("__ZERO", empty)
-        Transfer(tgt_fn, "__ZERO", tgt_ranges, src_ranges,
-                 "zero", self.transfers)
+        AddTransfer(tgt_fn, "__ZERO", tgt_ranges, src_ranges,
+                    "zero", self.transfers)
         continue
 
       elif tgt_fn == "__COPY":
         # "__COPY" domain includes all the blocks not contained in any
         # file and that need to be copied unconditionally to the target.
-        Transfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers)
+        AddTransfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers)
         continue
 
       elif tgt_fn in self.src.file_map:
         # Look for an exact pathname match in the source.
-        Transfer(tgt_fn, tgt_fn, tgt_ranges, self.src.file_map[tgt_fn],
-                 "diff", self.transfers)
+        AddTransfer(tgt_fn, tgt_fn, tgt_ranges, self.src.file_map[tgt_fn],
+                    "diff", self.transfers, self.version >= 3)
         continue
 
       b = os.path.basename(tgt_fn)
       if b in self.src_basenames:
         # Look for an exact basename match in the source.
         src_fn = self.src_basenames[b]
-        Transfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],
-                 "diff", self.transfers)
+        AddTransfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],
+                    "diff", self.transfers, self.version >= 3)
         continue
 
       b = re.sub("[0-9]+", "#", b)
@@ -885,11 +1043,11 @@
         # for .so files that contain version numbers in the filename
         # that get bumped.)
         src_fn = self.src_numpatterns[b]
-        Transfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],
-                 "diff", self.transfers)
+        AddTransfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],
+                    "diff", self.transfers, self.version >= 3)
         continue
 
-      Transfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers)
+      AddTransfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers)
 
   def AbbreviateSourceNames(self):
     for k in self.src.file_map.keys():
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 4b43c0c..357a666 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -399,6 +399,7 @@
     mount_point: such as "system", "data" etc.
   """
   d = {}
+
   if "build.prop" in glob_dict:
     bp = glob_dict["build.prop"]
     if "ro.build.date.utc" in bp:
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 99b319d..2965fa8 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -59,6 +59,8 @@
     self.device_specific = None
     self.extras = {}
     self.info_dict = None
+    self.source_info_dict = None
+    self.target_info_dict = None
     self.worker_threads = None
 
 
@@ -390,7 +392,9 @@
     img_keyblock = tempfile.NamedTemporaryFile()
     cmd = [info_dict["vboot_signer_cmd"], info_dict["futility"],
            img_unsigned.name, info_dict["vboot_key"] + ".vbpubk",
-           info_dict["vboot_key"] + ".vbprivk", img_keyblock.name,
+           info_dict["vboot_key"] + ".vbprivk",
+           info_dict["vboot_subkey"] + ".vbprivk",
+           img_keyblock.name,
            img.name]
     p = Run(cmd, stdout=subprocess.PIPE)
     p.communicate()
@@ -1193,7 +1197,11 @@
     self.path = os.path.join(tmpdir, partition)
     b.Compute(self.path)
 
-    _, self.device = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
+    if src is None:
+      _, self.device = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
+    else:
+      _, self.device = GetTypeAndDevice("/" + partition,
+                                        OPTIONS.source_info_dict)
 
   def WriteScript(self, script, output_zip, progress=None):
     if not self.src:
@@ -1391,6 +1399,8 @@
   output_sink("recovery-from-boot.p", patch)
 
   try:
+    # The following GetTypeAndDevice()s need to use the path in the target
+    # info_dict instead of source_info_dict.
     boot_type, boot_device = GetTypeAndDevice("/boot", info_dict)
     recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict)
   except KeyError:
@@ -1413,18 +1423,28 @@
        'bonus_args': bonus_args}
 
   # The install script location moved from /system/etc to /system/bin
-  # in the L release.  Parse the init.rc file to find out where the
+  # in the L release.  Parse init.*.rc files to find out where the
   # target-files expects it to be, and put it there.
   sh_location = "etc/install-recovery.sh"
-  try:
-    with open(os.path.join(input_dir, "BOOT", "RAMDISK", "init.rc")) as f:
+  found = False
+  init_rc_dir = os.path.join(input_dir, "BOOT", "RAMDISK")
+  init_rc_files = os.listdir(init_rc_dir)
+  for init_rc_file in init_rc_files:
+    if (not init_rc_file.startswith('init.') or
+        not init_rc_file.endswith('.rc')):
+      continue
+
+    with open(os.path.join(init_rc_dir, init_rc_file)) as f:
       for line in f:
         m = re.match(r"^service flash_recovery /system/(\S+)\s*$", line)
         if m:
           sh_location = m.group(1)
-          print "putting script in", sh_location
+          found = True
           break
-  except (OSError, IOError) as e:
-    print "failed to read init.rc: %s" % (e,)
+
+    if found:
+      break
+
+  print "putting script in", sh_location
 
   output_sink(sh_location, sh)
diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py
index 566e687..a52e328 100644
--- a/tools/releasetools/edify_generator.py
+++ b/tools/releasetools/edify_generator.py
@@ -243,6 +243,15 @@
     cmd = "delete(" + ",\0".join(['"%s"' % (i,) for i in file_list]) + ");"
     self.script.append(self.WordWrap(cmd))
 
+  def DeleteFilesIfNotMatching(self, file_list):
+    """Delete the file in file_list if not matching the checksum."""
+    if not file_list:
+      return
+    for name, sha1 in file_list:
+      cmd = ('sha1_check(read_file("{name}"), "{sha1}") || '
+             'delete("{name}");'.format(name=name, sha1=sha1))
+      self.script.append(self.WordWrap(cmd))
+
   def RenameFile(self, srcfile, tgtfile):
     """Moves a file from one location to another."""
     if self.info.get("update_rename_support", False):
@@ -254,7 +263,7 @@
     """Prepend an action with an apply_patch_check in order to
        skip the action if the file exists.  Used when a patch
        is later renamed."""
-    cmd = ('sha1_check(read_file("%s"), %s) || ' % (tgtfile, tgtsha1))
+    cmd = ('sha1_check(read_file("%s"), %s) ||' % (tgtfile, tgtsha1))
     self.script.append(self.WordWrap(cmd))
 
   def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs):
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 531a728..c5c16b4 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -42,6 +42,11 @@
       radio image. This option is only meaningful when -i is specified,
       because a full radio is always included in a full OTA if applicable.
 
+ --full_bootloader
+      When generating an incremental OTA, always include a full copy of
+      bootloader image. This option is only meaningful when -i is specified,
+      because a full bootloader is always included in a full OTA if applicable.
+
   -v  (--verify)
       Remount and verify the checksums of the files written to the
       system and vendor (if used) partitions.  Incremental builds only.
@@ -84,6 +89,9 @@
       Specifies the number of worker-threads that will be used when
       generating patches for incremental updates (defaults to 3).
 
+  --stash_threshold <float>
+      Specifies the threshold that will be used to compute the maximum
+      allowed stash size (defaults to 0.8).
 """
 
 import sys
@@ -122,6 +130,10 @@
 OPTIONS.oem_source = None
 OPTIONS.fallback_to_full = True
 OPTIONS.full_radio = False
+OPTIONS.full_bootloader = False
+# Stash size cannot exceed cache_size * threshold.
+OPTIONS.cache_size = None
+OPTIONS.stash_threshold = 0.8
 
 def MostPopularKey(d, default):
   """Given a dict, return the key corresponding to the largest
@@ -747,7 +759,7 @@
       output_zip=output_zip,
       script=script,
       metadata=metadata,
-      info_dict=OPTIONS.info_dict)
+      info_dict=OPTIONS.source_info_dict)
 
   # TODO: Currently this works differently from WriteIncrementalOTAPackage().
   # This function doesn't consider thumbprints when writing
@@ -1147,7 +1159,7 @@
       output_zip=output_zip,
       script=script,
       metadata=metadata,
-      info_dict=OPTIONS.info_dict)
+      info_dict=OPTIONS.source_info_dict)
 
   system_diff = FileDifference("system", source_zip, target_zip, output_zip)
   script.Mount("/system", recovery_mount_options)
@@ -1384,11 +1396,36 @@
   # Delete all the symlinks in source that aren't in target.  This
   # needs to happen before verbatim files are unpacked, in case a
   # symlink in the source is replaced by a real file in the target.
-  to_delete = []
+
+  # If a symlink in the source will be replaced by a regular file, we cannot
+  # delete the symlink/file in case the package gets applied again. For such
+  # a symlink, we prepend a sha1_check() to detect if it has been updated.
+  # (Bug: 23646151)
+  replaced_symlinks = dict()
+  if system_diff:
+    for i in system_diff.verbatim_targets:
+      replaced_symlinks["/%s" % (i[0],)] = i[2]
+  if vendor_diff:
+    for i in vendor_diff.verbatim_targets:
+      replaced_symlinks["/%s" % (i[0],)] = i[2]
+
+  if system_diff:
+    for tf in system_diff.renames.values():
+      replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
+  if vendor_diff:
+    for tf in vendor_diff.renames.values():
+      replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
+
+  always_delete = []
+  may_delete = []
   for dest, link in source_symlinks:
     if link not in target_symlinks_d:
-      to_delete.append(link)
-  script.DeleteFiles(to_delete)
+      if link in replaced_symlinks:
+        may_delete.append((link, replaced_symlinks[link]))
+      else:
+        always_delete.append(link)
+  script.DeleteFiles(always_delete)
+  script.DeleteFilesIfNotMatching(may_delete)
 
   if system_diff.verbatim_targets:
     script.Print("Unpacking new system files...")
@@ -1474,6 +1511,8 @@
       OPTIONS.incremental_source = a
     elif o == "--full_radio":
       OPTIONS.full_radio = True
+    elif o == "--full_bootloader":
+      OPTIONS.full_bootloader = True
     elif o in ("-w", "--wipe_user_data"):
       OPTIONS.wipe_user_data = True
     elif o in ("-n", "--no_prereq"):
@@ -1505,6 +1544,12 @@
       OPTIONS.updater_binary = a
     elif o in ("--no_fallback_to_full",):
       OPTIONS.fallback_to_full = False
+    elif o == "--stash_threshold":
+      try:
+        OPTIONS.stash_threshold = float(a)
+      except ValueError:
+        raise ValueError("Cannot parse value %r for option %r - expecting "
+                         "a float" % (a, o))
     else:
       return False
     return True
@@ -1516,6 +1561,7 @@
                                  "package_key=",
                                  "incremental_from=",
                                  "full_radio",
+                                 "full_bootloader",
                                  "wipe_user_data",
                                  "no_prereq",
                                  "extra_script=",
@@ -1528,6 +1574,7 @@
                                  "oem_settings=",
                                  "verify",
                                  "no_fallback_to_full",
+                                 "stash_threshold=",
                              ], extra_option_handler=option_handler)
 
   if len(args) != 2:
@@ -1585,6 +1632,11 @@
       output_zip = zipfile.ZipFile(temp_zip_file, "w",
                                    compression=zipfile.ZIP_DEFLATED)
 
+    cache_size = OPTIONS.info_dict.get("cache_size", None)
+    if cache_size is None:
+      raise RuntimeError("can't determine the cache partition size")
+    OPTIONS.cache_size = cache_size
+
     if OPTIONS.incremental_source is None:
       WriteFullOTAPackage(input_zip, output_zip)
       if OPTIONS.package_key is None:
diff --git a/tools/releasetools/rangelib.py b/tools/releasetools/rangelib.py
index 1506658..373bbed 100644
--- a/tools/releasetools/rangelib.py
+++ b/tools/releasetools/rangelib.py
@@ -24,6 +24,7 @@
   lots of runs."""
 
   def __init__(self, data=None):
+    # TODO(tbao): monotonic is broken when passing in a tuple.
     self.monotonic = False
     if isinstance(data, str):
       self._parse_internal(data)
@@ -260,6 +261,38 @@
       out = out.union(RangeSet(str(s1) + "-" + str(e1-1)))
     return out
 
+  def first(self, n):
+    """Return the RangeSet that contains at most the first 'n' integers.
+
+    >>> RangeSet("0-9").first(1)
+    <RangeSet("0")>
+    >>> RangeSet("10-19").first(5)
+    <RangeSet("10-14")>
+    >>> RangeSet("10-19").first(15)
+    <RangeSet("10-19")>
+    >>> RangeSet("10-19 30-39").first(3)
+    <RangeSet("10-12")>
+    >>> RangeSet("10-19 30-39").first(15)
+    <RangeSet("10-19 30-34")>
+    >>> RangeSet("10-19 30-39").first(30)
+    <RangeSet("10-19 30-39")>
+    >>> RangeSet("0-9").first(0)
+    <RangeSet("")>
+    """
+
+    if self.size() <= n:
+      return self
+
+    out = []
+    for s, e in self:
+      if e - s >= n:
+        out += (s, s+n)
+        break
+      else:
+        out += (s, e)
+        n -= e - s
+    return RangeSet(data=out)
+
 
 if __name__ == "__main__":
   import doctest
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index ec49112..60d62c2 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -203,11 +203,13 @@
         common.ZipWriteStr(output_tf_zip, out_info, data)
     elif info.filename in ("SYSTEM/build.prop",
                            "VENDOR/build.prop",
+                           "BOOT/RAMDISK/default.prop",
                            "RECOVERY/RAMDISK/default.prop"):
       print "rewriting %s:" % (info.filename,)
       new_data = RewriteProps(data, misc_info)
       common.ZipWriteStr(output_tf_zip, out_info, new_data)
-      if info.filename == "RECOVERY/RAMDISK/default.prop":
+      if info.filename in ("BOOT/RAMDISK/default.prop",
+                           "RECOVERY/RAMDISK/default.prop"):
         write_to_temp(info.filename, info.external_attr, new_data)
     elif info.filename.endswith("mac_permissions.xml"):
       print "rewriting %s with new keys." % (info.filename,)
@@ -310,6 +312,10 @@
         pieces = value.split("/")
         pieces[-1] = EditTags(pieces[-1])
         value = "/".join(pieces)
+      elif key == "ro.bootimage.build.fingerprint":
+        pieces = value.split("/")
+        pieces[-1] = EditTags(pieces[-1])
+        value = "/".join(pieces)
       elif key == "ro.build.description":
         pieces = value.split(" ")
         assert len(pieces) == 5
diff --git a/tools/releasetools/sparse_img.py b/tools/releasetools/sparse_img.py
index 07f3c1c..013044f 100644
--- a/tools/releasetools/sparse_img.py
+++ b/tools/releasetools/sparse_img.py
@@ -210,6 +210,16 @@
     nonzero_blocks = []
     reference = '\0' * self.blocksize
 
+    # Workaround for bug 23227672. For squashfs, we don't have a system.map. So
+    # the whole system image will be treated as a single file. But for some
+    # unknown bug, the updater will be killed due to OOM when writing back the
+    # patched image to flash (observed on lenok-userdebug MEA49). Prior to
+    # getting a real fix, we evenly divide the non-zero blocks into smaller
+    # groups (currently 1024 blocks or 4MB per group).
+    # Bug: 23227672
+    MAX_BLOCKS_PER_GROUP = 1024
+    nonzero_groups = []
+
     f = self.simg_f
     for s, e in remaining:
       for b in range(s, e):
@@ -232,12 +242,22 @@
           nonzero_blocks.append(b)
           nonzero_blocks.append(b+1)
 
-    assert zero_blocks or nonzero_blocks or clobbered_blocks
+          if len(nonzero_blocks) >= MAX_BLOCKS_PER_GROUP:
+            nonzero_groups.append(nonzero_blocks)
+            # Clear the list.
+            nonzero_blocks = []
+
+    if nonzero_blocks:
+      nonzero_groups.append(nonzero_blocks)
+      nonzero_blocks = []
+
+    assert zero_blocks or nonzero_groups or clobbered_blocks
 
     if zero_blocks:
       out["__ZERO"] = rangelib.RangeSet(data=zero_blocks)
-    if nonzero_blocks:
-      out["__NONZERO"] = rangelib.RangeSet(data=nonzero_blocks)
+    if nonzero_groups:
+      for i, blocks in enumerate(nonzero_groups):
+        out["__NONZERO-%d" % i] = rangelib.RangeSet(data=blocks)
     if clobbered_blocks:
       out["__COPY"] = clobbered_blocks