OSDN Git Service

AI 143754: SdkManager: list unknown AVDs and why they didn't load.
authorRaphael Moll <>
Tue, 31 Mar 2009 19:39:09 +0000 (12:39 -0700)
committerThe Android Open Source Project <initial-contribution@android.com>
Tue, 31 Mar 2009 19:39:09 +0000 (12:39 -0700)
  BUG=1703143

Automated import of CL 143754

sdkmanager/app/src/com/android/sdkmanager/Main.java
sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java

index adf37ed..191aa9e 100644 (file)
@@ -459,11 +459,30 @@ class Main {
                     mSdkLog.printf("  Sdcard: %s\n", sdcard);
                 }
             }
+
+            // Are there some unused AVDs?
+            List<AvdInfo> badAvds = avdManager.getUnavailableAvdList();
+
+            if (badAvds == null || badAvds.size() == 0) {
+                return;
+            }
+
+            mSdkLog.printf("\nThe following Android Virtual Devices are no longer available:\n");
+            boolean needSeparator = false;
+            for (AvdInfo info : badAvds) {
+                if (needSeparator) {
+                    mSdkLog.printf("---------\n");
+                }
+                mSdkLog.printf("    Name: %s\n", info.getName()  == null ? "--" : info.getName());
+                mSdkLog.printf("    Path: %s\n", info.getPath()  == null ? "--" : info.getPath());
+                mSdkLog.printf("   Error: %s\n", info.getError() == null ? "--" : info.getError());
+                needSeparator = true;
+            }
         } catch (AndroidLocationException e) {
             errorAndExit(e.getMessage());
         }
     }
-    
+
     /**
      * Creates a new AVD. This is a text based creation with command line prompt.
      */
index 93577e4..4342551 100644 (file)
@@ -32,8 +32,11 @@ import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.TreeSet;
 import java.util.Map.Entry;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -118,15 +121,43 @@ public final class AvdManager {
         private final String mPath;
         private final IAndroidTarget mTarget;
         private final Map<String, String> mProperties;
-        
-        /** Creates a new AVD info. Values are immutable. 
-         * @param properties */
+        private final String mError;
+
+        /**
+         * Creates a new valid AVD info. Values are immutable. 
+         * <p/>
+         * Such an AVD is available and can be used.
+         * The error string is set to null.
+         * 
+         * @param name The name of the AVD (for display or reference)
+         * @param path The path to the config.ini file
+         * @param target The target. Cannot be null.
+         * @param properties The property map. Cannot be null.
+         */
         public AvdInfo(String name, String path, IAndroidTarget target,
                 Map<String, String> properties) {
+            this(name, path, target, properties, null /*error*/);
+        }
+
+        /**
+         * Creates a new <em>invalid</em> AVD info. Values are immutable. 
+         * <p/>
+         * Such an AVD is not complete and cannot be used.
+         * The error string must be non-null.
+         * 
+         * @param name The name of the AVD (for display or reference)
+         * @param path The path to the config.ini file
+         * @param target The target. Can be null.
+         * @param properties The property map. Can be null.
+         * @param error The error describing why this AVD is invalid. Cannot be null.
+         */
+        public AvdInfo(String name, String path, IAndroidTarget target,
+                Map<String, String> properties, String error) {
             mName = name;
             mPath = path;
             mTarget = target;
             mProperties = properties;
+            mError = error;
         }
 
         /** Returns the name of the AVD. */
@@ -144,6 +175,11 @@ public final class AvdManager {
             return mTarget;
         }
 
+        /** Returns the error describing why an AVD failed to load. Always null for valid AVDs. */
+        public String getError() {
+            return mError;
+        }
+
         /** 
          * Helper method that returns the .ini {@link File} for a given AVD name. 
          * @throws AndroidLocationException if there's a problem getting android root directory.
@@ -634,29 +670,27 @@ public final class AvdManager {
         }
     }
 
-    private void buildAvdList(ArrayList<AvdInfo> list) throws AndroidLocationException {
+    /**
+     * Returns a list of files that are potential AVD ini files.
+     * <p/>
+     * This lists the $HOME/.android/avd/<name>.ini files.
+     * Such files are properties file than then indicate where the AVD folder is located.
+     * 
+     * @return A new {@link File} array or null. The array might be empty.
+     * @throws AndroidLocationException if there's a problem getting android root directory.
+     */
+    private File[] buildAvdFilesList() throws AndroidLocationException {
         // get the Android prefs location.
         String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
 
-        final boolean avdListDebug = System.getenv("AVD_LIST_DEBUG") != null;
-        if (avdListDebug) {
-            mSdkLog.printf("[AVD LIST DEBUG] AVD root: '%s'\n", avdRoot);
-        }
-        
         // ensure folder validity.
         File folder = new File(avdRoot);
         if (folder.isFile()) {
-            if (avdListDebug) {
-                mSdkLog.printf("[AVD LIST DEBUG] AVD root is a file.\n");
-            }
             throw new AndroidLocationException(String.format("%s is not a valid folder.", avdRoot));
         } else if (folder.exists() == false) {
-            if (avdListDebug) {
-                mSdkLog.printf("[AVD LIST DEBUG] AVD root folder doesn't exist, creating.\n");
-            }
             // folder is not there, we create it and return
             folder.mkdirs();
-            return;
+            return null;
         }
         
         File[] avds = folder.listFiles(new FilenameFilter() {
@@ -664,10 +698,6 @@ public final class AvdManager {
                 if (INI_NAME_PATTERN.matcher(name).matches()) {
                     // check it's a file and not a folder
                     boolean isFile = new File(parent, name).isFile();
-                    if (avdListDebug) {
-                        mSdkLog.printf("[AVD LIST DEBUG] Item '%s': %s\n",
-                                name, isFile ? "accepted file" : "rejected");
-                    }
                     return isFile;
                 }
 
@@ -675,52 +705,130 @@ public final class AvdManager {
             }
         });
         
+        return avds;
+    }
+
+    /**
+     * Computes the internal list of available AVDs.
+     * This only contains AVDs that reference the target currently available.
+     * 
+     * @param list An array list that will contain the list of AVDs.
+     * @throws AndroidLocationException if there's a problem getting android root directory.
+     */
+    private void buildAvdList(ArrayList<AvdInfo> list) throws AndroidLocationException {
+        
+        File[] avds = buildAvdFilesList();
+
         for (File avd : avds) {
-            AvdInfo info = parseAvdInfo(avd);
+            AvdInfo info = parseAvdInfo(avd, false /*acceptError*/);
             if (info != null) {
                 list.add(info);
-                if (avdListDebug) {
-                    mSdkLog.printf("[AVD LIST DEBUG] Added AVD '%s'\n", info.getPath());
-                }
-            } else if (avdListDebug) {
-                mSdkLog.printf("[AVD LIST DEBUG] Failed to parse AVD '%s'\n", avd.getPath());
             }
         }
     }
-    
-    private AvdInfo parseAvdInfo(File path) {
-        Map<String, String> map = SdkManager.parsePropertyFile(path, mSdkLog);
-        
-        String avdPath = map.get(AVD_INFO_PATH);
-        if (avdPath == null) {
+
+    public List<AvdInfo> getUnavailableAvdList() throws AndroidLocationException {
+        AvdInfo[] avds = getAvds();
+        File[] allAvds = buildAvdFilesList();
+        if (allAvds == null || allAvds.length == 0) {
             return null;
         }
+
+        TreeSet<File> list = new TreeSet<File>(Arrays.asList(allAvds));
+
+        for (AvdInfo info : avds) {
+            if (list.remove(info.getIniFile())) {
+                if (list.size() == 0) {
+                    return null;
+                }
+            }
+        }
+        
+        ArrayList<AvdInfo> errorAvds = new ArrayList<AvdInfo>(list.size());
+        for (File file : list) {
+            errorAvds.add(parseAvdInfo(file, true /*acceptError*/));
+        }
         
+        return errorAvds;
+    }
+
+    /**
+     * Parses an AVD config.ini to create an {@link AvdInfo}.
+     * 
+     * @param path The path to the AVD config.ini
+     * @param acceptError When false, an AVD that fails to load will be discarded and null will be
+     *        returned. When true, such an AVD will be returned with an error description.
+     * @return A new {@link AvdInfo} or null if the file is not valid or null if the AVD is not
+     *         valid and acceptError is false.
+     */
+    private AvdInfo parseAvdInfo(File path, boolean acceptError) {
+        String error = null;
+        Map<String, String> map = SdkManager.parsePropertyFile(path, mSdkLog);        
+
+        String avdPath = map.get(AVD_INFO_PATH);
         String targetHash = map.get(AVD_INFO_TARGET);
-        if (targetHash == null) {
-            return null;
+
+        IAndroidTarget target = null;
+        File configIniFile = null;
+        Map<String, String> properties = null;
+        
+        if (targetHash != null) {
+            target = mSdk.getTargetFromHashString(targetHash);
         }
 
-        IAndroidTarget target = mSdk.getTargetFromHashString(targetHash);
-        if (target == null) {
-            return null;
+        // load the avd properties.
+        if (avdPath != null) {
+            configIniFile = new File(avdPath, CONFIG_INI);
         }
         
-        // load the avd properties.
-        File configIniFile = new File(avdPath, CONFIG_INI);
-        Map<String, String> properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog);
+        if (configIniFile != null) {
+            properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog);
+        }
 
+        // get name
+        String name = path.getName();
         Matcher matcher = INI_NAME_PATTERN.matcher(path.getName());
-
+        if (matcher.matches()) {
+            name = matcher.group(1);
+        }
+        
+        if (!acceptError) {
+            if (avdPath == null ||
+                    targetHash == null ||
+                    target == null ||
+                    configIniFile == null ||
+                    properties == null) {
+                return null;
+            }
+        } else {
+            if (avdPath == null || configIniFile == null) {
+                error = String.format("Missing AVD 'path' property in %1$s", name);
+            } else if (targetHash == null) {
+                error = String.format("Missing 'target' property in %1$s", name);
+            } else if (target == null) {
+                error = String.format("Unknown 'target=%2$s' property in %1$s", name, targetHash);
+            } else if (properties == null) {
+                error = String.format("Failed to parse properties from %1$s", avdPath);
+            }
+        }
+        
         AvdInfo info = new AvdInfo(
-                matcher.matches() ? matcher.group(1) : path.getName(), // should not happen
+                name,
                 avdPath,
                 target,
-                properties);
+                properties,
+                error);
         
         return info;
     }
 
+    /**
+     * Writes a new AVD config.ini file from a set of properties.
+     * 
+     * @param iniFile The file to generate.
+     * @param values THe properties to place in the ini file.
+     * @throws IOException if {@link FileWriter} fails to open, write or close the file.
+     */
     private static void createConfigIni(File iniFile, Map<String, String> values)
             throws IOException {
         FileWriter writer = new FileWriter(iniFile);
@@ -732,6 +840,15 @@ public final class AvdManager {
 
     }
     
+    /**
+     * Invokes the tool to create a new SD card image file.
+     * 
+     * @param toolLocation The path to the mksdcard tool.
+     * @param size The size of the new SD Card, compatible with {@link #SDCARD_SIZE_PATTERN}.
+     * @param location The path of the new sdcard image file to generate.
+     * @param log The logger object, to report errors.
+     * @return True if the sdcard could be created.
+     */
     private boolean createSdCard(String toolLocation, String size, String location, ISdkLog log) {
         try {
             String[] command = new String[3];