common: android: avb support load android image separate
Skip fdt and ramdisk relocation to save boot time. Change-Id: I56fd2fca97fa7795024aa542f0a45d0512be01d4 Signed-off-by: Joseph Chen <chenjh@rock-chips.com>
This commit is contained in:
parent
89151b4aad
commit
503a892f5a
|
|
@ -659,9 +659,14 @@ static AvbSlotVerifyResult android_slot_verify(char *boot_partname,
|
||||||
load_address -= hdr->page_size;
|
load_address -= hdr->page_size;
|
||||||
*android_load_address = load_address;
|
*android_load_address = load_address;
|
||||||
|
|
||||||
|
#ifdef CONFIG_ANDROID_BOOT_IMAGE_SEPARATE
|
||||||
|
android_image_load_separate(hdr, NULL,
|
||||||
|
(void *)load_address, hdr);
|
||||||
|
#else
|
||||||
memcpy((uint8_t *)load_address,
|
memcpy((uint8_t *)load_address,
|
||||||
slot_data[0]->loaded_partitions->data,
|
slot_data[0]->loaded_partitions->data,
|
||||||
slot_data[0]->loaded_partitions->data_size);
|
slot_data[0]->loaded_partitions->data_size);
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
slot_set_unbootable(&ab_data.slots[slot_index_to_boot]);
|
slot_set_unbootable(&ab_data.slots[slot_index_to_boot]);
|
||||||
}
|
}
|
||||||
|
|
@ -951,17 +956,6 @@ static int load_android_image(struct blk_desc *dev_desc,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool avb_enabled;
|
|
||||||
void android_avb_set_enabled(bool enable)
|
|
||||||
{
|
|
||||||
avb_enabled = enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool android_avb_is_enabled(void)
|
|
||||||
{
|
|
||||||
return avb_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
int android_bootloader_boot_flow(struct blk_desc *dev_desc,
|
int android_bootloader_boot_flow(struct blk_desc *dev_desc,
|
||||||
unsigned long load_address)
|
unsigned long load_address)
|
||||||
{
|
{
|
||||||
|
|
@ -1100,7 +1094,6 @@ int android_bootloader_boot_flow(struct blk_desc *dev_desc,
|
||||||
|
|
||||||
if (vboot_flag) {
|
if (vboot_flag) {
|
||||||
printf("SecureBoot enabled, AVB verify\n");
|
printf("SecureBoot enabled, AVB verify\n");
|
||||||
android_avb_set_enabled(true);
|
|
||||||
if (android_slot_verify(boot_partname, &load_address,
|
if (android_slot_verify(boot_partname, &load_address,
|
||||||
slot_suffix))
|
slot_suffix))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1113,13 +1106,11 @@ int android_bootloader_boot_flow(struct blk_desc *dev_desc,
|
||||||
printf("SecureBoot disabled, AVB skip\n");
|
printf("SecureBoot disabled, AVB skip\n");
|
||||||
env_update("bootargs",
|
env_update("bootargs",
|
||||||
"androidboot.verifiedbootstate=orange");
|
"androidboot.verifiedbootstate=orange");
|
||||||
android_avb_set_enabled(false);
|
|
||||||
if (load_android_image(dev_desc, boot_partname,
|
if (load_android_image(dev_desc, boot_partname,
|
||||||
slot_suffix, &load_address))
|
slot_suffix, &load_address))
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
printf("SecureBoot enabled, AVB verify\n");
|
printf("SecureBoot enabled, AVB verify\n");
|
||||||
android_avb_set_enabled(true);
|
|
||||||
if (android_slot_verify(boot_partname, &load_address,
|
if (android_slot_verify(boot_partname, &load_address,
|
||||||
slot_suffix))
|
slot_suffix))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
||||||
|
|
@ -194,37 +194,28 @@ ulong android_image_get_kload(const struct andr_img_hdr *hdr)
|
||||||
int android_image_get_ramdisk(const struct andr_img_hdr *hdr,
|
int android_image_get_ramdisk(const struct andr_img_hdr *hdr,
|
||||||
ulong *rd_data, ulong *rd_len)
|
ulong *rd_data, ulong *rd_len)
|
||||||
{
|
{
|
||||||
bool avb_enabled = false;
|
|
||||||
|
|
||||||
#ifdef CONFIG_ANDROID_BOOTLOADER
|
|
||||||
avb_enabled = android_avb_is_enabled();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!hdr->ramdisk_size) {
|
if (!hdr->ramdisk_size) {
|
||||||
*rd_data = *rd_len = 0;
|
*rd_data = *rd_len = 0;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* We have load ramdisk at "ramdisk_addr_r" */
|
||||||
* We have load ramdisk at "ramdisk_addr_r" when android avb is
|
#ifdef CONFIG_ANDROID_BOOT_IMAGE_SEPARATE
|
||||||
* disabled and CONFIG_ANDROID_BOOT_IMAGE_SEPARATE enabled.
|
ulong ramdisk_addr_r;
|
||||||
*/
|
|
||||||
if (!avb_enabled && IS_ENABLED(CONFIG_ANDROID_BOOT_IMAGE_SEPARATE)) {
|
|
||||||
ulong ramdisk_addr_r;
|
|
||||||
|
|
||||||
ramdisk_addr_r = env_get_ulong("ramdisk_addr_r", 16, 0);
|
ramdisk_addr_r = env_get_ulong("ramdisk_addr_r", 16, 0);
|
||||||
if (!ramdisk_addr_r) {
|
if (!ramdisk_addr_r) {
|
||||||
printf("No Found Ramdisk Load Address.\n");
|
printf("No Found Ramdisk Load Address.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
*rd_data = ramdisk_addr_r;
|
|
||||||
} else {
|
|
||||||
*rd_data = (unsigned long)hdr;
|
|
||||||
*rd_data += hdr->page_size;
|
|
||||||
*rd_data += ALIGN(hdr->kernel_size, hdr->page_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*rd_data = ramdisk_addr_r;
|
||||||
|
#else
|
||||||
|
*rd_data = (unsigned long)hdr;
|
||||||
|
*rd_data += hdr->page_size;
|
||||||
|
*rd_data += ALIGN(hdr->kernel_size, hdr->page_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
*rd_len = hdr->ramdisk_size;
|
*rd_len = hdr->ramdisk_size;
|
||||||
|
|
||||||
printf("RAM disk load addr 0x%08lx size %u KiB\n",
|
printf("RAM disk load addr 0x%08lx size %u KiB\n",
|
||||||
|
|
@ -236,40 +227,30 @@ int android_image_get_ramdisk(const struct andr_img_hdr *hdr,
|
||||||
int android_image_get_fdt(const struct andr_img_hdr *hdr,
|
int android_image_get_fdt(const struct andr_img_hdr *hdr,
|
||||||
ulong *rd_data)
|
ulong *rd_data)
|
||||||
{
|
{
|
||||||
bool avb_enabled = false;
|
|
||||||
|
|
||||||
#ifdef CONFIG_ANDROID_BOOTLOADER
|
|
||||||
avb_enabled = android_avb_is_enabled();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!hdr->second_size) {
|
if (!hdr->second_size) {
|
||||||
*rd_data = 0;
|
*rd_data = 0;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* We have load fdt at "fdt_addr_r" */
|
||||||
* We have load fdt at "fdt_addr_r" when android avb is
|
#if defined(CONFIG_USING_KERNEL_DTB) || \
|
||||||
* disabled and CONFIG_ANDROID_BOOT_IMAGE_SEPARATE enabled;
|
defined(CONFIG_ANDROID_BOOT_IMAGE_SEPARATE)
|
||||||
* or CONFIG_USING_KERNEL_DTB is enabled.
|
ulong fdt_addr_r;
|
||||||
*/
|
|
||||||
if (IS_ENABLED(CONFIG_USING_KERNEL_DTB) ||
|
|
||||||
(!avb_enabled && IS_ENABLED(CONFIG_ANDROID_BOOT_IMAGE_SEPARATE))) {
|
|
||||||
ulong fdt_addr_r;
|
|
||||||
|
|
||||||
fdt_addr_r = env_get_ulong("fdt_addr_r", 16, 0);
|
fdt_addr_r = env_get_ulong("fdt_addr_r", 16, 0);
|
||||||
if (!fdt_addr_r) {
|
if (!fdt_addr_r) {
|
||||||
printf("No Found FDT Load Address.\n");
|
printf("No Found FDT Load Address.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
*rd_data = fdt_addr_r;
|
|
||||||
} else {
|
|
||||||
*rd_data = (unsigned long)hdr;
|
|
||||||
*rd_data += hdr->page_size;
|
|
||||||
*rd_data += ALIGN(hdr->kernel_size, hdr->page_size);
|
|
||||||
*rd_data += ALIGN(hdr->ramdisk_size, hdr->page_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*rd_data = fdt_addr_r;
|
||||||
|
#else
|
||||||
|
*rd_data = (unsigned long)hdr;
|
||||||
|
*rd_data += hdr->page_size;
|
||||||
|
*rd_data += ALIGN(hdr->kernel_size, hdr->page_size);
|
||||||
|
*rd_data += ALIGN(hdr->ramdisk_size, hdr->page_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
printf("FDT load addr 0x%08x size %u KiB\n",
|
printf("FDT load addr 0x%08x size %u KiB\n",
|
||||||
hdr->second_addr, DIV_ROUND_UP(hdr->second_size, 1024));
|
hdr->second_addr, DIV_ROUND_UP(hdr->second_size, 1024));
|
||||||
|
|
||||||
|
|
@ -277,53 +258,70 @@ int android_image_get_fdt(const struct andr_img_hdr *hdr,
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_ANDROID_BOOT_IMAGE_SEPARATE
|
#ifdef CONFIG_ANDROID_BOOT_IMAGE_SEPARATE
|
||||||
static int android_image_load_separate(struct blk_desc *dev_desc,
|
int android_image_load_separate(struct andr_img_hdr *hdr,
|
||||||
struct andr_img_hdr *hdr,
|
const disk_partition_t *part,
|
||||||
const disk_partition_t *part,
|
void *load_address, void *ram_src)
|
||||||
void *android_load_address)
|
|
||||||
{
|
{
|
||||||
|
struct blk_desc *dev_desc = rockchip_get_bootdev();
|
||||||
ulong fdt_addr_r = env_get_ulong("fdt_addr_r", 16, 0);
|
ulong fdt_addr_r = env_get_ulong("fdt_addr_r", 16, 0);
|
||||||
|
char *fdt_high = env_get("fdt_high");
|
||||||
|
char *ramdisk_high = env_get("initrd_high");
|
||||||
ulong blk_start, blk_cnt, size;
|
ulong blk_start, blk_cnt, size;
|
||||||
int ret, blk_read = 0;
|
int ret, blk_read = 0;
|
||||||
|
ulong start;
|
||||||
|
|
||||||
if (hdr->kernel_size) {
|
if (hdr->kernel_size) {
|
||||||
size = hdr->kernel_size + hdr->page_size;
|
size = hdr->kernel_size + hdr->page_size;
|
||||||
blk_start = part->start;
|
|
||||||
blk_cnt = DIV_ROUND_UP(size, dev_desc->blksz);
|
blk_cnt = DIV_ROUND_UP(size, dev_desc->blksz);
|
||||||
if (!sysmem_alloc_base(MEMBLK_ID_KERNEL,
|
if (!sysmem_alloc_base(MEMBLK_ID_KERNEL,
|
||||||
(phys_addr_t)android_load_address,
|
(phys_addr_t)load_address,
|
||||||
blk_cnt * dev_desc->blksz))
|
blk_cnt * dev_desc->blksz))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
ret = blk_dread(dev_desc, blk_start,
|
if (ram_src) {
|
||||||
blk_cnt, android_load_address);
|
start = (ulong)ram_src;
|
||||||
if (ret != blk_cnt) {
|
memcpy((char *)load_address,
|
||||||
debug("%s: read kernel failed, ret=%d\n",
|
(char *)start, hdr->kernel_size);
|
||||||
__func__, ret);
|
} else {
|
||||||
return -1;
|
blk_start = part->start;
|
||||||
|
ret = blk_dread(dev_desc, blk_start,
|
||||||
|
blk_cnt, load_address);
|
||||||
|
if (ret != blk_cnt) {
|
||||||
|
debug("%s: read kernel failed, ret=%d\n",
|
||||||
|
__func__, ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
blk_read += ret;
|
||||||
}
|
}
|
||||||
blk_read += ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdr->ramdisk_size) {
|
if (hdr->ramdisk_size) {
|
||||||
ulong ramdisk_addr_r = env_get_ulong("ramdisk_addr_r", 16, 0);
|
ulong ramdisk_addr_r = env_get_ulong("ramdisk_addr_r", 16, 0);
|
||||||
|
|
||||||
size = hdr->page_size + ALIGN(hdr->kernel_size, hdr->page_size);
|
size = hdr->page_size + ALIGN(hdr->kernel_size, hdr->page_size);
|
||||||
blk_start = part->start + DIV_ROUND_UP(size, dev_desc->blksz);
|
|
||||||
blk_cnt = DIV_ROUND_UP(hdr->ramdisk_size, dev_desc->blksz);
|
blk_cnt = DIV_ROUND_UP(hdr->ramdisk_size, dev_desc->blksz);
|
||||||
if (!sysmem_alloc_base(MEMBLK_ID_RAMDISK,
|
if (!sysmem_alloc_base(MEMBLK_ID_RAMDISK,
|
||||||
ramdisk_addr_r,
|
ramdisk_addr_r,
|
||||||
blk_cnt * dev_desc->blksz))
|
blk_cnt * dev_desc->blksz))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
if (ram_src) {
|
||||||
ret = blk_dread(dev_desc, blk_start,
|
start = (unsigned long)ram_src;
|
||||||
blk_cnt, (void *)ramdisk_addr_r);
|
start += hdr->page_size;
|
||||||
if (ret != blk_cnt) {
|
start += ALIGN(hdr->kernel_size, hdr->page_size);
|
||||||
debug("%s: read ramdisk failed, ret=%d\n",
|
memcpy((char *)ramdisk_addr_r,
|
||||||
__func__, ret);
|
(char *)start, hdr->ramdisk_size);
|
||||||
return -1;
|
} else {
|
||||||
|
blk_start = part->start +
|
||||||
|
DIV_ROUND_UP(size, dev_desc->blksz);
|
||||||
|
ret = blk_dread(dev_desc, blk_start,
|
||||||
|
blk_cnt, (void *)ramdisk_addr_r);
|
||||||
|
if (ret != blk_cnt) {
|
||||||
|
debug("%s: read ramdisk failed, ret=%d\n",
|
||||||
|
__func__, ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
blk_read += ret;
|
||||||
}
|
}
|
||||||
blk_read += ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((gd->fdt_blob != (void *)fdt_addr_r) && hdr->second_size) {
|
if ((gd->fdt_blob != (void *)fdt_addr_r) && hdr->second_size) {
|
||||||
|
|
@ -345,7 +343,6 @@ static int android_image_load_separate(struct blk_desc *dev_desc,
|
||||||
size = hdr->page_size +
|
size = hdr->page_size +
|
||||||
ALIGN(hdr->kernel_size, hdr->page_size) +
|
ALIGN(hdr->kernel_size, hdr->page_size) +
|
||||||
ALIGN(hdr->ramdisk_size, hdr->page_size);
|
ALIGN(hdr->ramdisk_size, hdr->page_size);
|
||||||
blk_start = part->start + DIV_ROUND_UP(size, dev_desc->blksz);
|
|
||||||
blk_cnt = DIV_ROUND_UP(hdr->second_size, dev_desc->blksz);
|
blk_cnt = DIV_ROUND_UP(hdr->second_size, dev_desc->blksz);
|
||||||
if (!sysmem_alloc_base(MEMBLK_ID_FDT_AOSP,
|
if (!sysmem_alloc_base(MEMBLK_ID_FDT_AOSP,
|
||||||
fdt_addr_r,
|
fdt_addr_r,
|
||||||
|
|
@ -353,16 +350,42 @@ static int android_image_load_separate(struct blk_desc *dev_desc,
|
||||||
CONFIG_SYS_FDT_PAD))
|
CONFIG_SYS_FDT_PAD))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
ret = blk_dread(dev_desc, blk_start, blk_cnt, (void *)fdt_addr_r);
|
if (ram_src) {
|
||||||
if (ret != blk_cnt) {
|
start = (unsigned long)ram_src;
|
||||||
debug("%s: read dtb failed, ret=%d\n", __func__, ret);
|
start += hdr->page_size;
|
||||||
return -1;
|
start += ALIGN(hdr->kernel_size, hdr->page_size);
|
||||||
}
|
start += ALIGN(hdr->ramdisk_size, hdr->page_size);
|
||||||
|
memcpy((char *)fdt_addr_r,
|
||||||
|
(char *)start, hdr->second_size);
|
||||||
|
} else {
|
||||||
|
blk_start = part->start +
|
||||||
|
DIV_ROUND_UP(size, dev_desc->blksz);
|
||||||
|
ret = blk_dread(dev_desc, blk_start, blk_cnt,
|
||||||
|
(void *)fdt_addr_r);
|
||||||
|
if (ret != blk_cnt) {
|
||||||
|
debug("%s: read dtb failed, ret=%d\n",
|
||||||
|
__func__, ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
blk_read += blk_cnt;
|
blk_read += blk_cnt;
|
||||||
|
}
|
||||||
#endif /* CONFIG_RKIMG_BOOTLOADER */
|
#endif /* CONFIG_RKIMG_BOOTLOADER */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (blk_read > 0 || ram_src) {
|
||||||
|
if (!fdt_high) {
|
||||||
|
env_set_hex("fdt_high", -1UL);
|
||||||
|
printf("Fdt ");
|
||||||
|
}
|
||||||
|
if (!ramdisk_high) {
|
||||||
|
env_set_hex("initrd_high", -1UL);
|
||||||
|
printf("Ramdisk ");
|
||||||
|
}
|
||||||
|
if (!fdt_high || !ramdisk_high)
|
||||||
|
printf("skip relocation\n");
|
||||||
|
}
|
||||||
|
|
||||||
return blk_read;
|
return blk_read;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_ANDROID_BOOT_IMAGE_SEPARATE */
|
#endif /* CONFIG_ANDROID_BOOT_IMAGE_SEPARATE */
|
||||||
|
|
@ -451,36 +474,17 @@ long android_image_load(struct blk_desc *dev_desc,
|
||||||
blk_cnt, load_address);
|
blk_cnt, load_address);
|
||||||
|
|
||||||
#ifdef CONFIG_ANDROID_BOOT_IMAGE_SEPARATE
|
#ifdef CONFIG_ANDROID_BOOT_IMAGE_SEPARATE
|
||||||
if (!android_avb_is_enabled()) {
|
blk_read =
|
||||||
char *fdt_high = env_get("fdt_high");
|
android_image_load_separate(hdr, part_info, buf, NULL);
|
||||||
char *ramdisk_high = env_get("initrd_high");
|
#else
|
||||||
|
if (!sysmem_alloc_base(MEMBLK_ID_ANDROID,
|
||||||
|
(phys_addr_t)buf,
|
||||||
|
blk_cnt * part_info->blksz))
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
blk_read =
|
blk_read = blk_dread(dev_desc, part_info->start,
|
||||||
android_image_load_separate(dev_desc, hdr,
|
blk_cnt, buf);
|
||||||
part_info, buf);
|
|
||||||
if (blk_read > 0) {
|
|
||||||
if (!fdt_high) {
|
|
||||||
env_set_hex("fdt_high", -1UL);
|
|
||||||
printf("Fdt ");
|
|
||||||
}
|
|
||||||
if (!ramdisk_high) {
|
|
||||||
env_set_hex("initrd_high", -1UL);
|
|
||||||
printf("Ramdisk ");
|
|
||||||
}
|
|
||||||
if (!fdt_high || !ramdisk_high)
|
|
||||||
printf("skip relocation\n");
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
#endif
|
#endif
|
||||||
{
|
|
||||||
if (!sysmem_alloc_base(MEMBLK_ID_ANDROID,
|
|
||||||
(phys_addr_t)buf,
|
|
||||||
blk_cnt * part_info->blksz))
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
blk_read = blk_dread(dev_desc, part_info->start,
|
|
||||||
blk_cnt, buf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -94,10 +94,4 @@ char *android_str_append(char *base_name, char *slot_suffix);
|
||||||
*/
|
*/
|
||||||
int android_fdt_overlay_apply(void *fdt_addr);
|
int android_fdt_overlay_apply(void *fdt_addr);
|
||||||
|
|
||||||
/** android_avb_is_enabled- get avb enable state.
|
|
||||||
* *
|
|
||||||
* @return true on enabled, otherwise disabled;
|
|
||||||
*/
|
|
||||||
bool android_avb_is_enabled(void);
|
|
||||||
|
|
||||||
#endif /* __ANDROID_BOOTLOADER_H */
|
#endif /* __ANDROID_BOOTLOADER_H */
|
||||||
|
|
|
||||||
|
|
@ -1274,6 +1274,20 @@ ulong android_image_get_end(const struct andr_img_hdr *hdr);
|
||||||
ulong android_image_get_kload(const struct andr_img_hdr *hdr);
|
ulong android_image_get_kload(const struct andr_img_hdr *hdr);
|
||||||
void android_print_contents(const struct andr_img_hdr *hdr);
|
void android_print_contents(const struct andr_img_hdr *hdr);
|
||||||
|
|
||||||
|
/** android_image_load_separate - Load an Android Image separate from storage or ram
|
||||||
|
*
|
||||||
|
* Load an Android Image based on the header size in the storage or ram.
|
||||||
|
*
|
||||||
|
* @hdr: The android image header
|
||||||
|
* @part: The partition where to read the image from
|
||||||
|
* @load_address: The address where the image will be loaded
|
||||||
|
* @ram_src: The ram source to load, if NULL load from partition
|
||||||
|
* @return the blk count.
|
||||||
|
*/
|
||||||
|
int android_image_load_separate(struct andr_img_hdr *hdr,
|
||||||
|
const disk_partition_t *part,
|
||||||
|
void *load_address, void *ram_src);
|
||||||
|
|
||||||
/** android_image_load - Load an Android Image from storage.
|
/** android_image_load - Load an Android Image from storage.
|
||||||
*
|
*
|
||||||
* Load an Android Image based on the header size in the storage.
|
* Load an Android Image based on the header size in the storage.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue