$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/STATIC_LIBRARIES/lib?camera_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libwebcore_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
-
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/Silence.ogg)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/notifications/Silence.ogg)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)
endif
-$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(INSTALLED_RECOVERYIMAGE_TARGET) | $(ACP)
+# The system partition needs room for the recovery image as well. We
+# now store the recovery image as a binary patch using the boot image
+# as the source (since they are very similar). Generate the patch so
+# we can see how big it's going to be, and include that in the system
+# image size check calculation.
+ifneq ($(TARGET_PRODUCT),sdk)
+ifneq ($(TARGET_PRODUCT),generic)
+intermediates := $(call intermediates-dir-for,PACKAGING,recovery_patch)
+RECOVERY_FROM_BOOT_PATCH := $(intermediates)/recovery_from_boot.p
+$(RECOVERY_FROM_BOOT_PATCH): $(INSTALLED_RECOVERYIMAGE_TARGET) \
+ $(INSTALLED_BOOTIMAGE_TARGET) \
+ $(HOST_OUT_EXECUTABLES)/imgdiff \
+ $(HOST_OUT_EXECUTABLES)/bsdiff
+ @echo "Construct recovery from boot"
+ mkdir -p $(dir $@)
+ PATH=$(HOST_OUT_EXECUTABLES):$$PATH $(HOST_OUT_EXECUTABLES)/imgdiff $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_RECOVERYIMAGE_TARGET) $@
+endif # TARGET_PRODUCT != generic
+endif # TARGET_PRODUCT != sdk
+
+$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP)
@echo "Install system fs image: $@"
$(copy-file-to-target)
- $(hide) $(call assert-max-file-size,$@ $(INSTALLED_RECOVERYIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_MAX_SIZE))
+ $(hide) $(call assert-max-file-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_MAX_SIZE))
systemimage: $(INSTALLED_SYSTEMIMAGE)
built_ota_tools := \
$(call intermediates-dir-for,EXECUTABLES,applypatch)/applypatch \
+ $(call intermediates-dir-for,EXECUTABLES,applypatch_static)/applypatch_static \
$(call intermediates-dir-for,EXECUTABLES,check_prereq)/check_prereq \
$(call intermediates-dir-for,EXECUTABLES,updater)/updater
$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_OTA_TOOLS := $(built_ota_tools)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := applypatch
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
+LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := main.c
+LOCAL_MODULE := applypatch_static
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := eng
-LOCAL_STATIC_LIBRARIES += libapplypatch
-LOCAL_STATIC_LIBRARIES += libmtdutils libmincrypt libbz libz
-LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
+LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
include $(BUILD_EXECUTABLE)
return 0;
}
-// Copy the contents of source_file to target_mtd partition, a string
-// of the form "MTD:<partition>[:...]". Return 0 on success.
-int CopyToMTDPartition(const char* source_file, const char* target_mtd) {
+// Write a memory buffer to target_mtd partition, a string of the form
+// "MTD:<partition>[:...]". Return 0 on success.
+int WriteToMTDPartition(unsigned char* data, size_t len,
+ const char* target_mtd) {
char* partition = strchr(target_mtd, ':');
if (partition == NULL) {
fprintf(stderr, "bad MTD target name \"%s\"\n", target_mtd);
if (end != NULL)
*end = '\0';
- FILE* f = fopen(source_file, "rb");
- if (f == NULL) {
- fprintf(stderr, "failed to open %s for reading: %s\n",
- source_file, strerror(errno));
- return -1;
- }
-
if (!mtd_partitions_scanned) {
mtd_scan_partitions();
mtd_partitions_scanned = 1;
return -1;
}
- const int buffer_size = 4096;
- char buffer[buffer_size];
- size_t read;
- while ((read = fread(buffer, 1, buffer_size, f)) > 0) {
- size_t written = mtd_write_data(ctx, buffer, read);
- if (written != read) {
- fprintf(stderr, "only wrote %d of %d bytes to MTD %s\n",
- written, read, partition);
- mtd_write_close(ctx);
- return -1;
- }
+ size_t written = mtd_write_data(ctx, (char*)data, len);
+ if (written != len) {
+ fprintf(stderr, "only wrote %d of %d bytes to MTD %s\n",
+ written, len, partition);
+ mtd_write_close(ctx);
+ return -1;
}
- fclose(f);
if (mtd_erase_blocks(ctx, -1) < 0) {
fprintf(stderr, "error finishing mtd write of %s\n", partition);
mtd_write_close(ctx);
return 0;
}
+size_t FileSink(unsigned char* data, size_t len, void* token) {
+ return fwrite(data, 1, len, (FILE*)token);
+}
+
+typedef struct {
+ unsigned char* buffer;
+ size_t size;
+ size_t pos;
+} MemorySinkInfo;
+
+size_t MemorySink(unsigned char* data, size_t len, void* token) {
+ MemorySinkInfo* msi = (MemorySinkInfo*)token;
+ if (msi->size - msi->pos < len) {
+ return -1;
+ }
+ memcpy(msi->buffer + msi->pos, data, len);
+ msi->pos += len;
+ return len;
+}
+
// Return the amount of free space (in bytes) on the filesystem
// containing filename. filename must exist. Return -1 on error.
size_t FreeSpaceForFile(const char* filename) {
}
char* outname = NULL;
+ FILE* output = NULL;
+ MemorySinkInfo msi;
+ SinkFn sink = NULL;
+ void* token = NULL;
if (strncmp(target_filename, "MTD:", 4) == 0) {
- outname = MTD_TARGET_TEMP_FILE;
+ // We store the decoded output in memory.
+ msi.buffer = malloc(target_size);
+ if (msi.buffer == NULL) {
+ fprintf(stderr, "failed to alloc %ld bytes for output\n",
+ (long)target_size);
+ return 1;
+ }
+ msi.pos = 0;
+ msi.size = target_size;
+ sink = MemorySink;
+ token = &msi;
} else {
// We write the decoded output to "<tgt-file>.patch".
outname = (char*)malloc(strlen(target_filename) + 10);
strcpy(outname, target_filename);
strcat(outname, ".patch");
- }
- FILE* output = fopen(outname, "wb");
- if (output == NULL) {
- fprintf(stderr, "failed to open output file %s: %s\n",
- outname, strerror(errno));
- return 1;
+
+ output = fopen(outname, "wb");
+ if (output == NULL) {
+ fprintf(stderr, "failed to open output file %s: %s\n",
+ outname, strerror(errno));
+ return 1;
+ }
+ sink = FileSink;
+ token = output;
}
#define MAX_HEADER_LENGTH 8
} else if (header_bytes_read >= 8 &&
memcmp(header, "BSDIFF40", 8) == 0) {
int result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
- patch_filename, 0, output, &ctx);
+ patch_filename, 0, sink, token, &ctx);
if (result != 0) {
fprintf(stderr, "ApplyBSDiffPatch failed\n");
return result;
memcmp(header, "IMGDIFF", 7) == 0 &&
(header[7] == '1' || header[7] == '2')) {
int result = ApplyImagePatch(source_to_use->data, source_to_use->size,
- patch_filename, output, &ctx);
+ patch_filename, sink, token, &ctx);
if (result != 0) {
fprintf(stderr, "ApplyImagePatch failed\n");
return result;
return 1;
}
- fflush(output);
- fsync(fileno(output));
- fclose(output);
+ if (output != NULL) {
+ fflush(output);
+ fsync(fileno(output));
+ fclose(output);
+ }
const uint8_t* current_target_sha1 = SHA_final(&ctx);
if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
return 1;
}
- if (strcmp(outname, MTD_TARGET_TEMP_FILE) == 0) {
+ if (output == NULL) {
// Copy the temp file to the MTD partition.
- if (CopyToMTDPartition(outname, target_filename) != 0) {
- fprintf(stderr, "copy of %s to %s failed\n", outname, target_filename);
+ if (WriteToMTDPartition(msi.buffer, msi.pos, target_filename) != 0) {
+ fprintf(stderr, "write of patched data to %s failed\n", target_filename);
return 1;
}
- unlink(outname);
+ free(msi.buffer);
} else {
// Give the .patch file the same owner, group, and mode of the
// original source file.
// and use it as the source instead.
#define CACHE_TEMP_SOURCE "/cache/saved.file"
-// When writing to an MTD partition, we first put the output in this
-// temp file, then copy it to the partition once the patching is
-// finished (and the target sha1 verified).
-#define MTD_TARGET_TEMP_FILE "/tmp/mtd-temp"
+typedef size_t (*SinkFn)(unsigned char*, size_t, void*);
// applypatch.c
size_t FreeSpaceForFile(const char* filename);
void ShowBSDiffLicense();
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
const char* patch_filename, ssize_t offset,
- FILE* output, SHA_CTX* ctx);
+ SinkFn sink, void* token, SHA_CTX* ctx);
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
const char* patch_filename, ssize_t patch_offset,
unsigned char** new_data, ssize_t* new_size);
// imgpatch.c
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
- const char* patch_filename,
- FILE* output, SHA_CTX* ctx);
+ const char* patch_filename,
+ SinkFn sink, void* token, SHA_CTX* ctx);
// freecache.c
int MakeFreeSpaceOnCache(size_t bytes_needed);
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
const char* patch_filename, ssize_t patch_offset,
- FILE* output, SHA_CTX* ctx) {
+ SinkFn sink, void* token, SHA_CTX* ctx) {
unsigned char* new_data;
ssize_t new_size;
return -1;
}
- if (fwrite(new_data, 1, new_size, output) < new_size) {
+ if (sink(new_data, new_size, token) < new_size) {
fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno));
return 1;
}
*/
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
const char* patch_filename,
- FILE* output, SHA_CTX* ctx) {
+ SinkFn sink, void* token, SHA_CTX* ctx) {
FILE* f;
if ((f = fopen(patch_filename, "rb")) == NULL) {
fprintf(stderr, "failed to open patch file\n");
ApplyBSDiffPatch(old_data + src_start, src_len,
patch_filename, patch_offset,
- output, ctx);
+ sink, token, ctx);
} else if (type == CHUNK_GZIP) {
// This branch is basically a duplicate of the CHUNK_DEFLATE
// branch, with a bit of extra processing for the gzip header
// Now compress the target data and append it to the output.
// start with the gzip header.
- fwrite(gzip+64, 1, gzip_header_len, output);
+ sink(gzip+64, gzip_header_len, token);
SHA_update(ctx, gzip+64, gzip_header_len);
// we're done with the expanded_source data buffer, so we'll
ret = deflate(&strm, Z_FINISH);
size_t have = temp_size - strm.avail_out;
- if (fwrite(temp_data, 1, have, output) != have) {
+ if (sink(temp_data, have, token) != have) {
fprintf(stderr, "failed to write %d compressed bytes to output\n",
have);
return -1;
deflateEnd(&strm);
// lastly, the gzip footer.
- fwrite(gzip+64+gzip_header_len, 1, 8, output);
+ sink(gzip+64+gzip_header_len, 8, token);
SHA_update(ctx, gzip+64+gzip_header_len, 8);
free(temp_data);
return -1;
}
SHA_update(ctx, temp, data_len);
- if (fwrite(temp, 1, data_len, output) != data_len) {
+ if (sink(temp, data_len, token) != data_len) {
fprintf(stderr, "failed to write chunk %d raw data\n", i);
return -1;
}
ret = deflate(&strm, Z_FINISH);
size_t have = temp_size - strm.avail_out;
- if (fwrite(temp_data, 1, have, output) != have) {
+ if (sink(temp_data, have, token) != have) {
fprintf(stderr, "failed to write %d compressed bytes to output\n",
have);
return -1;
Except as noted, this content is
licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
Creative Commons Attribution 2.5</a>. For details and
- restrictions, see the <a href="<?cs var:toroot ?>license.html">Content
+ restrictions, see the <a href="http://developer.android.com/license.html">Content
License</a>.<?cs
/def ?>
def:custom_copyright() ?>
Except as noted, this content is licensed under <a
href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>.
- For details and restrictions, see the <a href="<?cs var:toroot ?>license.html">
+ For details and restrictions, see the <a href="http://developer.android.com/license.html">
Content License</a>.<?cs
/def ?>
for i in sha1:
out.append(" " + i)
self.script.append("".join(out))
- self.included_files.add("applypatch")
+ self.included_files.add(("applypatch_static", "applypatch"))
def CacheFreeSpaceCheck(self, amount):
"""Check that there's at least 'amount' space that can be made
available on /cache."""
self.script.append("run_program PACKAGE:applypatch -s %d" % (amount,))
- self.included_files.add("applypatch")
+ self.included_files.add(("applypatch_static", "applypatch"))
def Mount(self, kind, what, path):
# no-op; amend uses it's 'roots' system to automatically mount
(srcfile, tgtfile, tgtsha1, tgtsize)) +
" ".join(["%s:%s" % patchpairs[i:i+2]
for i in range(0, len(patchpairs), 2)]))
- self.included_files.add("applypatch")
+ self.included_files.add(("applypatch_static", "applypatch"))
def WriteFirmwareImage(self, kind, fn):
"""Arrange to update the given firmware image (kind must be
common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-script",
"\n".join(self.script) + "\n")
for i in self.included_files:
+ if isinstance(i, tuple):
+ sourcefn, targetfn = i
+ else:
+ sourcefn = i
+ targetfn = i
try:
if input_path is None:
- data = input_zip.read(os.path.join("OTA/bin", i))
+ data = input_zip.read(os.path.join("OTA/bin", sourcefn))
else:
- data = open(os.path.join(input_path, i)).read()
- common.ZipWriteStr(output_zip, i, data, perms=0755)
+ data = open(os.path.join(input_path, sourcefn)).read()
+ common.ZipWriteStr(output_zip, targetfn, data, perms=0755)
except (IOError, KeyError), e:
raise ExternalError("unable to include binary %s: %s" % (i, e))
common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw)
-def FixPermissions(script):
- Item.GetMetadata()
- root = Item.Get("system")
- root.SetPermissions(script)
-
-
def AppendAssertions(script, input_zip):
device = GetBuildProp("ro.product.device", input_zip)
script.AssertDevice(device)
script.AssertSomeBootloader(*bootloaders)
+def MakeRecoveryPatch(output_zip, recovery_img, boot_img):
+ """Generate a binary patch that creates the recovery image starting
+ with the boot image. (Most of the space in these images is just the
+ kernel, which is identical for the two, so the resulting patch
+ should be efficient.) Add it to the output zip, along with a shell
+ script that is run from init.rc on first boot to actually do the
+ patching and install the new recovery image.
+
+ recovery_img and boot_img should be File objects for the
+ corresponding images.
+
+ Returns an Item for the shell script, which must be made
+ executable.
+ """
+
+ patch = Difference(recovery_img, boot_img, "imgdiff")
+ common.ZipWriteStr(output_zip, "system/recovery-from-boot.p", patch)
+ Item.Get("system/recovery-from-boot.p", dir=False)
+
+ # Images with different content will have a different first page, so
+ # we check to see if this recovery has already been installed by
+ # testing just the first 2k.
+ HEADER_SIZE = 2048
+ header_sha1 = sha.sha(recovery_img.data[:HEADER_SIZE]).hexdigest()
+ sh = """#!/system/bin/sh
+if ! applypatch -c MTD:recovery:%(header_size)d:%(header_sha1)s; then
+ log -t recovery "Installing new recovery image"
+ applypatch MTD:boot:%(boot_size)d:%(boot_sha1)s MTD:recovery %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p
+else
+ log -t recovery "Recovery image already installed"
+fi
+""" % { 'boot_size': boot_img.size,
+ 'boot_sha1': boot_img.sha1,
+ 'header_size': HEADER_SIZE,
+ 'header_sha1': header_sha1,
+ 'recovery_size': recovery_img.size,
+ 'recovery_sha1': recovery_img.sha1 }
+ common.ZipWriteStr(output_zip, "system/etc/install-recovery.sh", sh)
+ return Item.Get("system/etc/install-recovery.sh", dir=False)
+
+
def WriteFullOTAPackage(input_zip, output_zip):
if OPTIONS.script_mode == "auto":
script = both_generator.BothGenerator(2)
symlinks = CopySystemFiles(input_zip, output_zip)
script.MakeSymlinks(symlinks)
- if common.BuildAndAddBootableImage(
- os.path.join(OPTIONS.input_tmp, "RECOVERY"),
- "system/recovery.img", output_zip):
- Item.Get("system/recovery.img", dir=False)
+ boot_img = File("boot.img", common.BuildBootableImage(
+ os.path.join(OPTIONS.input_tmp, "BOOT")))
+ recovery_img = File("recovery.img", common.BuildBootableImage(
+ os.path.join(OPTIONS.input_tmp, "RECOVERY")))
+ i = MakeRecoveryPatch(output_zip, recovery_img, boot_img)
- FixPermissions(script)
+ Item.GetMetadata()
+
+ # GetMetadata uses the data in android_filesystem_config.h to assign
+ # the uid/gid/mode of all files. We want to override that for the
+ # recovery patching shell script to make it executable.
+ i.uid = 0
+ i.gid = 0
+ i.mode = 0544
+ Item.Get("system").SetPermissions(script)
- common.AddBoot(output_zip)
+ common.CheckSize(boot_img.data, "boot.img")
+ common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
script.ShowProgress(0.2, 0)
script.WriteRawImage("boot", "boot.img")
script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
- if updating_recovery:
- d = Difference(target_recovery, source_recovery, "imgdiff")
- print "recovery target: %d source: %d diff: %d" % (
- target_recovery.size, source_recovery.size, len(d))
-
- common.ZipWriteStr(output_zip, "patch/recovery.img.p", d)
-
- script.PatchCheck("MTD:recovery:%d:%s:%d:%s" %
- (source_recovery.size, source_recovery.sha1,
- target_recovery.size, target_recovery.sha1))
-
if updating_boot:
d = Difference(target_boot, source_boot, "imgdiff")
print "boot target: %d source: %d diff: %d" % (
print "boot image unchanged; skipping."
if updating_recovery:
- # Produce /system/recovery.img by applying a patch to the current
- # contents of the recovery partition.
- script.Print("Patching recovery image...")
- script.ApplyPatch("MTD:recovery:%d:%s:%d:%s"
- % (source_recovery.size, source_recovery.sha1,
- target_recovery.size, target_recovery.sha1),
- "/system/recovery.img",
- target_recovery.size, target_recovery.sha1,
- source_recovery.sha1, "/tmp/patchtmp/recovery.img.p")
- print "recovery image changed; including."
+ # Is it better to generate recovery as a patch from the current
+ # boot image, or from the previous recovery image? For large
+ # updates with significant kernel changes, probably the former.
+ # For small updates where the kernel hasn't changed, almost
+ # certainly the latter. We pick the first option. Future
+ # complicated schemes may let us effectively use both.
+ #
+ # A wacky possibility: as long as there is room in the boot
+ # partition, include the binaries and image files from recovery in
+ # the boot image (though not in the ramdisk) so they can be used
+ # as fodder for constructing the recovery image.
+ recovery_sh_item = MakeRecoveryPatch(output_zip,
+ target_recovery, target_boot)
+ print "recovery image changed; including as patch from boot."
else:
print "recovery image unchanged; skipping."
target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
temp_script = script.MakeTemporary()
- FixPermissions(temp_script)
+ Item.GetMetadata()
+ if updating_recovery:
+ recovery_sh_item.uid = 0
+ recovery_sh_item.gid = 0
+ recovery_sh_item.mode = 0544
+ Item.Get("system").SetPermissions(temp_script)
# Note that this call will mess up the tree of Items, so make sure
# we're done with it.