Merge "Move system image initialization to core."
diff --git a/android/avd/hardware-properties.ini b/android/avd/hardware-properties.ini
index 69abdaa..5ecfcc4 100644
--- a/android/avd/hardware-properties.ini
+++ b/android/avd/hardware-properties.ini
@@ -222,26 +222,33 @@
 abstract    = Path to the ramdisk image
 description = Path to the ramdisk image.
 
-# Path to the system partition.
+# System partition image(s).
+#
+# disk.systemPartition.path points to the read/write system partition image.
+#   if empty, a temporary file will be created, initialized with the content
+#   of .initPath
+#
+# disk.systemPartition.initPath is only used when .path is empty. It must
+# then point to a read-only initialization system image file.
+#
+# disk.systemPartition.size is the ideal size of the system partition. The
+# size is ignored if the actual system partition image is larger. Otherwise,
+# it indicates the maximum size the disk image file can grow to.
+#
 name        = disk.systemPartition.path
 type        = string
-default     = <init>
-abstract    = Path to system partition image
-description = Path to read/write system partition image during emulation. If special value '<init>' is used, a temporary file will be created, populated with the content of .initPath
+default     =
+abstract    = Path to runtime system partition image
 
-# Initial path to the system partition.
 name        = disk.systemPartition.initPath
 type        = string
 default     =
 abstract    = Initial system partition image
-description = Only used if .path is '<init>', path to an initial system image that will be copied into the temporary system image file before launch.
 
-# System partition size.
 name        = disk.systemPartition.size
 type        = diskSize
 default     = 0
 abstract    = Ideal size of system partition
-description = ideal size of system partition. Ignored if smaller than the size of .path (or .initPath). Otherwise, gives the maximum size the partition is allowed to grow dynamically.
 
 # Path to the data partition.
 name        = disk.dataPartition.path
diff --git a/android/avd/hw-config-defs.h b/android/avd/hw-config-defs.h
index 6c8ccae..e2def30 100644
--- a/android/avd/hw-config-defs.h
+++ b/android/avd/hw-config-defs.h
@@ -223,23 +223,23 @@
 HWCFG_STRING(
   disk_systemPartition_path,
   "disk.systemPartition.path",
-  "<init>",
-  "Path to system partition image",
-  "Path to read/write system partition image during emulation. If special value '<init>' is used, a temporary file will be created, populated with the content of .initPath")
+  "",
+  "Path to runtime system partition image",
+  "")
 
 HWCFG_STRING(
   disk_systemPartition_initPath,
   "disk.systemPartition.initPath",
   "",
   "Initial system partition image",
-  "Only used if .path is '<init>', path to an initial system image that will be copied into the temporary system image file before launch.")
+  "")
 
 HWCFG_DISKSIZE(
   disk_systemPartition_size,
   "disk.systemPartition.size",
   "0",
   "Ideal size of system partition",
-  "ideal size of system partition. Ignored if smaller than the size of .path (or .initPath). Otherwise, gives the maximum size the partition is allowed to grow dynamically.")
+  "")
 
 HWCFG_STRING(
   disk_dataPartition_path,
@@ -260,7 +260,7 @@
   "disk.dataPartition.size",
   "0",
   "Ideal size of data partition",
-  "ideal size of data partition. Ignored if smaller than the size of .path (or .initPath). Otherwise, gives the maximum size the partition is allowed to grow dynamically.")
+  "")
 
 HWCFG_STRING(
   disk_snapStorage_path,
diff --git a/android/avd/info.c b/android/avd/info.c
index ca4f0cf..a23cd63 100644
--- a/android/avd/info.c
+++ b/android/avd/info.c
@@ -470,6 +470,7 @@
     char         temp[MAX_PATH], *p = temp, *end = p + sizeof(temp);
 
     p = bufprint(temp, end, "%s/skins/%s", skinDirRoot, skinName);
+    DD("Probing skin directory: %s", temp);
     if (p >= end || !path_exists(temp)) {
         DD("    ignore bad skin directory %s", temp);
         return NULL;
@@ -1098,31 +1099,6 @@
     return l->pPath[0];
 }
 
-/* Attempts to load an AVD image, but does not kill the process if loading
- * fails.
- */
-static void
-imageLoader_loadOptional( ImageLoader *l, AvdImageType img_type,
-                           const char *forcedPath )
-{
-    imageLoader_set (l, img_type);
-    imageLoader_load(l, IMAGE_OPTIONAL |
-                        IMAGE_IGNORE_IF_LOCKED);
-
-    /* if the file was not found, ignore it */
-    if (l->pPath[0] && !path_exists(l->pPath[0]))
-    {
-        D("ignoring non-existing %s at %s: %s",
-          l->imageText, l->pPath[0], strerror(errno));
-
-        /* if the user provided the path by hand, warn him. */
-        if (forcedPath != NULL)
-            dwarning("ignoring non-existing %s image", l->imageText);
-
-        imageLoader_setPath(l, NULL);
-    }
-}
-
 /* find the correct path of all image files we're going to need
  * and lock the files that need it.
  */
@@ -1135,14 +1111,6 @@
 
     imageLoader_init(l, i, params);
 
-    /* the system image
-     *
-     * if there is one in the content directory just lock
-     * and use it.
-     */
-    imageLoader_set ( l, AVD_IMAGE_INITSYSTEM );
-    imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK );
-
     /* the data partition - this one is special because if it
      * is missing, we need to copy the initial image file into it.
      *
@@ -1335,15 +1303,6 @@
 
     AFREE(srcData);
 
-    /** load the system image. read-only. the caller must
-     ** take care of checking the state
-     **/
-    imageLoader_set ( l, AVD_IMAGE_INITSYSTEM );
-    imageLoader_load( l, IMAGE_REQUIRED | IMAGE_DONT_LOCK );
-
-    /* force the system image to read-only status */
-    l->pState[0] = IMAGE_STATE_READONLY;
-
     return 0;
 }
 
@@ -1522,6 +1481,13 @@
 }
 
 char*
+avdInfo_getSystemImagePath( AvdInfo*  i )
+{
+    const char* imageName = _imageFileNames[ AVD_IMAGE_USERSYSTEM ];
+    return _avdInfo_getContentFilePath(i, imageName);
+}
+
+char*
 avdInfo_getSystemInitImagePath( AvdInfo*  i )
 {
     const char* imageName = _imageFileNames[ AVD_IMAGE_INITSYSTEM ];
diff --git a/android/avd/info.h b/android/avd/info.h
index afc71fb..4191165 100644
--- a/android/avd/info.h
+++ b/android/avd/info.h
@@ -147,7 +147,16 @@
 char*  avdInfo_getDefaultCachePath( AvdInfo*  i );
 
 
+/* avdInfo_getSystemImagePath() will return NULL, except if the AVD content
+ * directory contains a file named "system-qemu.img".
+ */
+char*  avdInfo_getSystemImagePath( AvdInfo* i );
+
+/* avdInfo_getSystemInitImagePath() retrieves the path to the read-only
+ * initialization image for this disk image.
+ */
 char*  avdInfo_getSystemInitImagePath( AvdInfo*  i );
+
 char*  avdInfo_getDataInitImagePath( AvdInfo* i );
 
 /* Returns the path to a given AVD image file. This will return NULL if
diff --git a/android/main-common.c b/android/main-common.c
index 740f59a..9e5318d 100644
--- a/android/main-common.c
+++ b/android/main-common.c
@@ -1031,13 +1031,7 @@
 {
     unsigned  defaultPartitionSize = convertMBToBytes(66);
 
-    if (_update_hwconfig_path(&hwConfig->disk_systemPartition_initPath, avd, AVD_IMAGE_INITSYSTEM)) {
-        dprint("system path %s is invalid", hwConfig->disk_systemPartition_initPath);
-        exit(1);
-    }
-
     _update_hwconfig_path(&hwConfig->disk_dataPartition_path, avd, AVD_IMAGE_USERDATA);
-    _update_hwconfig_path(&hwConfig->disk_systemPartition_path, avd, AVD_IMAGE_USERSYSTEM);
     _update_hwconfig_path(&hwConfig->disk_dataPartition_path, avd, AVD_IMAGE_INITDATA);
 
     if (opts->partition_size) {
@@ -1057,25 +1051,6 @@
         }
         defaultPartitionSize = sizeMB * ONE_MB;
     }
-
-    /* Check the size of the system partition image.
-     * If we have an AVD, it must be smaller than
-     * the disk.systemPartition.size hardware property.
-     *
-     * Otherwise, we need to adjust the systemPartitionSize
-     * automatically, and print a warning.
-     *
-     */
-    {
-        uint64_t   systemBytes  = avdInfo_getImageFileSize(avd, AVD_IMAGE_INITSYSTEM);
-        uint64_t   defaultBytes = defaultPartitionSize;
-
-        if (defaultBytes == 0 || opts->partition_size)
-            defaultBytes = defaultPartitionSize;
-        hwConfig->disk_systemPartition_size =
-            _adjustPartitionSize("system", systemBytes, defaultBytes, inAndroidBuild);
-    }
-
     /* Check the size of the /data partition. The only interesting cases here are:
      * - when the USERDATA image already exists and is larger than the default
      * - when we're wiping data and the INITDATA is larger than the default.
diff --git a/android/main.c b/android/main.c
index 240574a..33a96ea 100644
--- a/android/main.c
+++ b/android/main.c
@@ -108,6 +108,52 @@
     exit(1);
 }
 
+/* TODO: Put in shared source file */
+static char*
+_getFullFilePath( const char* rootPath, const char* fileName )
+{
+    if (path_is_absolute(fileName)) {
+        return ASTRDUP(fileName);
+    } else {
+        char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+
+        p = bufprint(temp, end, "%s/%s", rootPath, fileName);
+        if (p >= end) {
+            return NULL;
+        }
+        return ASTRDUP(temp);
+    }
+}
+
+static uint64_t
+_adjustPartitionSize( const char*  description,
+                      uint64_t     imageBytes,
+                      uint64_t     defaultBytes,
+                      int          inAndroidBuild )
+{
+    char      temp[64];
+    unsigned  imageMB;
+    unsigned  defaultMB;
+
+    if (imageBytes <= defaultBytes)
+        return defaultBytes;
+
+    imageMB   = convertBytesToMB(imageBytes);
+    defaultMB = convertBytesToMB(defaultBytes);
+
+    if (imageMB > defaultMB) {
+        snprintf(temp, sizeof temp, "(%d MB > %d MB)", imageMB, defaultMB);
+    } else {
+        snprintf(temp, sizeof temp, "(%lld bytes > %lld bytes)", imageBytes, defaultBytes);
+    }
+
+    if (inAndroidBuild) {
+        dwarning("%s partition size adjusted to match image file %s\n", description, temp);
+    }
+
+    return convertMBToBytes(imageMB);
+}
+
 int main(int argc, char **argv)
 {
     char   tmp[MAX_PATH];
@@ -126,6 +172,7 @@
     AConfig*          skinConfig;
     char*             skinPath;
     int               inAndroidBuild;
+    uint64_t          defaultPartitionSize = convertMBToBytes(66);
 
     AndroidOptions  opts[1];
     /* net.shared_net_ip boot property value. */
@@ -426,21 +473,116 @@
     hw->disk_ramdisk_path = avdInfo_getRamdiskPath(avd);
     D("autoconfig: -ramdisk %s", hw->disk_ramdisk_path);
 
-    {
-        const char*  filetype = "file";
+    /* -partition-size is used to specify the max size of both the system
+     * and data partition sizes.
+     */
+    if (opts->partition_size) {
+        char*  end;
+        long   sizeMB = strtol(opts->partition_size, &end, 0);
+        long   minSizeMB = 10;
+        long   maxSizeMB = LONG_MAX / ONE_MB;
 
-        // TODO: This should go to core
-        if (avdInfo_isImageReadOnly(avd, AVD_IMAGE_INITSYSTEM))
-            filetype = "initfile";
-
-        bufprint(tmp, tmpend,
-             "system,size=0x%x,%s=%s", (uint32_t)hw->disk_systemPartition_size,
-             filetype, avdInfo_getImageFile(avd, AVD_IMAGE_INITSYSTEM));
-
-        args[n++] = "-nand";
-        args[n++] = strdup(tmp);
+        if (sizeMB < 0 || *end != 0) {
+            derror( "-partition-size must be followed by a positive integer" );
+            exit(1);
+        }
+        if (sizeMB < minSizeMB || sizeMB > maxSizeMB) {
+            derror( "partition-size (%d) must be between %dMB and %dMB",
+                    sizeMB, minSizeMB, maxSizeMB );
+            exit(1);
+        }
+        defaultPartitionSize = (uint64_t) sizeMB * ONE_MB;
     }
 
+
+    /** SYSTEM PARTITION **/
+
+    if (opts->sysdir == NULL) {
+        if (avdInfo_inAndroidBuild(avd)) {
+            opts->sysdir = ASTRDUP(avdInfo_getContentPath(avd));
+            D("autoconfig: -sysdir %s", opts->sysdir);
+        }
+    }
+
+    if (opts->sysdir != NULL) {
+        if (!path_exists(opts->sysdir)) {
+            derror("Directory does not exist: %s", opts->sysdir);
+            exit(1);
+        }
+    }
+
+    {
+        char*  rwImage   = NULL;
+        char*  initImage = NULL;
+
+        do {
+            if (opts->system == NULL) {
+                /* If -system is not used, try to find a runtime system image
+                * (i.e. system-qemu.img) in the content directory.
+                */
+                rwImage = avdInfo_getSystemImagePath(avd);
+                if (rwImage != NULL) {
+                    break;
+                }
+                /* Otherwise, try to find the initial system image */
+                initImage = avdInfo_getSystemInitImagePath(avd);
+                if (initImage == NULL) {
+                    derror("No initial system image for this configuration!");
+                    exit(1);
+                }
+                break;
+            }
+
+            /* If -system <name> is used, use it to find the initial image */
+            if (opts->sysdir != NULL) {
+                initImage = _getFullFilePath(opts->sysdir, opts->system);
+            } else {
+                initImage = ASTRDUP(opts->system);
+            }
+            if (!path_exists(initImage)) {
+                derror("System image file doesn't exist: %s", initImage);
+                exit(1);
+            }
+
+        } while (0);
+
+        if (rwImage != NULL) {
+            /* Use the read/write image file directly */
+            hw->disk_systemPartition_path     = rwImage;
+            hw->disk_systemPartition_initPath = NULL;
+            D("Using direct system image: %s", rwImage);
+        } else if (initImage != NULL) {
+            hw->disk_systemPartition_path = NULL;
+            hw->disk_systemPartition_initPath = initImage;
+            D("Using initial system image: %s", initImage);
+        }
+
+        /* Check the size of the system partition image.
+        * If we have an AVD, it must be smaller than
+        * the disk.systemPartition.size hardware property.
+        *
+        * Otherwise, we need to adjust the systemPartitionSize
+        * automatically, and print a warning.
+        *
+        */
+        const char* systemImage = hw->disk_systemPartition_path;
+        uint64_t    systemBytes;
+
+        if (systemImage == NULL)
+            systemImage = hw->disk_systemPartition_initPath;
+
+        if (path_get_size(systemImage, &systemBytes) < 0) {
+            derror("Missing system image: %s", systemImage);
+            exit(1);
+        }
+
+        hw->disk_systemPartition_size =
+            _adjustPartitionSize("system", systemBytes, defaultPartitionSize,
+                                 avdInfo_inAndroidBuild(avd));
+    }
+
+    /** DATA PARTITION **/
+
     bufprint(tmp, tmpend,
              "userdata,size=0x%x,file=%s",
              (uint32_t)hw->disk_dataPartition_size,
diff --git a/vl-android.c b/vl-android.c
index 04fed59..36289e4 100644
--- a/vl-android.c
+++ b/vl-android.c
@@ -60,6 +60,7 @@
 #include "android/utils/filelock.h"
 #include "android/utils/path.h"
 #include "android/utils/stralloc.h"
+#include "android/utils/tempfile.h"
 #include "android/display-core.h"
 #include "android/utils/timezone.h"
 #include "android/snapshot.h"
@@ -4841,6 +4842,41 @@
     }
 #endif  // CONFIG_NAND_LIMITS
 
+    /* Initialize system partition image */
+    {
+        char        tmp[PATH_MAX+32];
+        const char* sysImage = android_hw->disk_systemPartition_path;
+        const char* initImage = android_hw->disk_systemPartition_initPath;
+        uint64_t    sysBytes = android_hw->disk_systemPartition_size;
+
+        if (sysBytes == 0) {
+            PANIC("Invalid system partition size: %" PRUd64, sysBytes);
+        }
+
+        snprintf(tmp,sizeof(tmp),"system,size=0x%" PRUx64, sysBytes);
+
+        if (sysImage && *sysImage) {
+            if (filelock_create(sysImage) == NULL) {
+                fprintf(stderr,"WARNING: System image already in use, changes will not persist!\n");
+                /* If there is no file= parameters, nand_add_dev will create
+                 * a temporary file to back the partition image. */
+            } else {
+                pstrcat(tmp,sizeof(tmp),",file=");
+                pstrcat(tmp,sizeof(tmp),sysImage);
+            }
+        }
+        if (initImage && *initImage) {
+            if (!path_exists(initImage)) {
+                PANIC("Invalid initial system image path: %s", initImage);
+            }
+            pstrcat(tmp,sizeof(tmp),",initfile=");
+            pstrcat(tmp,sizeof(tmp),initImage);
+        } else {
+            PANIC("Missing initial system image path!");
+        }
+        nand_add_dev(tmp);
+    }
+
     /* Init SD-Card stuff. For Android, it is always hda */
     /* If the -hda option was used, ignore the Android-provided one */
     if (hda_opts == NULL) {