FileDocCategorySizeDatePackage
AvdManager.javaAPI DocAndroid 1.5 API48004Wed May 06 22:41:10 BST 2009com.android.sdklib.avd

AvdManager

public final class AvdManager extends Object
Android Virtual Device Manager to manage AVDs.

Fields Summary
public static final String
AVD_FOLDER_EXTENSION
public static final String
AVD_INFO_PATH
public static final String
AVD_INFO_TARGET
public static final String
AVD_INI_SKIN_PATH
AVD/config.ini key name representing the SDK-relative path of the skin folder, if any, or a 320x480 like constant for a numeric skin size.
public static final String
AVD_INI_SKIN_NAME
AVD/config.ini key name representing an UI name for the skin. This config key is ignored by the emulator. It is only used by the SDK manager or tools to give a friendlier name to the skin. If missing, use the {@link #AVD_INI_SKIN_PATH} key instead.
public static final String
AVD_INI_SDCARD_PATH
AVD/config.ini key name representing the path to the sdcard file. If missing, the default name "sdcard.img" will be used for the sdcard, if there's such a file.
public static final String
AVD_INI_SDCARD_SIZE
AVD/config.ini key name representing the size of the SD card. This property is for UI purposes only. It is not used by the emulator.
public static final String
AVD_INI_IMAGES_1
AVD/config.ini key name representing the first path where the emulator looks for system images. Typically this is the path to the add-on system image or the path to the platform system image if there's no add-on.

The emulator looks at {@link #AVD_INI_IMAGES_1} before {@link #AVD_INI_IMAGES_2}.

public static final String
AVD_INI_IMAGES_2
AVD/config.ini key name representing the second path where the emulator looks for system images. Typically this is the path to the platform system image.
public static final Pattern
NUMERIC_SKIN_SIZE
Pattern to match pixel-sized skin "names", e.g. "320x480".
private static final String
USERDATA_IMG
private static final String
CONFIG_INI
private static final String
SDCARD_IMG
private static final String
INI_EXTENSION
private static final Pattern
INI_NAME_PATTERN
private static final Pattern
IMAGE_NAME_PATTERN
private static final Pattern
SDCARD_SIZE_PATTERN
Pattern for matching SD Card sizes, e.g. "4K" or "16M".
private final ArrayList
mAllAvdList
private AvdInfo[]
mValidAvdList
private AvdInfo[]
mBrokenAvdList
private com.android.sdklib.ISdkLog
mSdkLog
private final com.android.sdklib.SdkManager
mSdk
Constructors Summary
public AvdManager(com.android.sdklib.SdkManager sdk, com.android.sdklib.ISdkLog sdkLog)


           
        mSdk = sdk;
        mSdkLog = sdkLog;
        buildAvdList(mAllAvdList);
    
Methods Summary
private java.io.File[]buildAvdFilesList()
Returns a list of files that are potential AVD ini files.

This lists the $HOME/.android/avd/.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.

        // get the Android prefs location.
        String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;

        // ensure folder validity.
        File folder = new File(avdRoot);
        if (folder.isFile()) {
            throw new AndroidLocationException(
                    String.format("%1$s is not a valid folder.", avdRoot));
        } else if (folder.exists() == false) {
            // folder is not there, we create it and return
            folder.mkdirs();
            return null;
        }
        
        File[] avds = folder.listFiles(new FilenameFilter() {
            public boolean accept(File parent, String name) {
                if (INI_NAME_PATTERN.matcher(name).matches()) {
                    // check it's a file and not a folder
                    boolean isFile = new File(parent, name).isFile();
                    return isFile;
                }

                return false;
            }
        });
        
        return avds;
    
private voidbuildAvdList(java.util.ArrayList allList)
Computes the internal list of available AVDs

param
allList the list to contain all the AVDs
throws
AndroidLocationException if there's a problem getting android root directory.

        File[] avds = buildAvdFilesList();
        if (avds != null) {
            for (File avd : avds) {
                AvdInfo info = parseAvdInfo(avd);
                if (info != null) {
                    allList.add(info);
                }
            }
        }
    
public com.android.sdklib.avd.AvdManager$AvdInfocreateAvd(java.io.File avdFolder, java.lang.String name, com.android.sdklib.IAndroidTarget target, java.lang.String skinName, java.lang.String sdcard, java.util.Map hardwareConfig, boolean removePrevious)
Creates a new AVD. It is expected that there is no existing AVD with this name already.

param
avdFolder the data folder for the AVD. It will be created as needed.
param
name the name of the AVD
param
target the target of the AVD
param
skinName the name of the skin. Can be null. Must have been verified by caller.
param
sdcard the parameter value for the sdCard. Can be null. This is either a path to an existing sdcard image or a sdcard size (\d+, \d+K, \dM).
param
hardwareConfig the hardware setup for the AVD
param
removePrevious If true remove any previous files.

        
        File iniFile = null;
        boolean needCleanup = false;
        try {
            if (avdFolder.exists()) {
                if (removePrevious) {
                    // AVD already exists and removePrevious is set, try to remove the
                    // directory's content first (but not the directory itself).
                    recursiveDelete(avdFolder);
                } else {
                    // AVD shouldn't already exist if removePrevious is false.
                    if (mSdkLog != null) {
                        mSdkLog.error(null,
                                "Folder %1$s is in the way. Use --force if you want to overwrite.",
                                avdFolder.getAbsolutePath());
                    }
                    return null;
                }
            } else {
                // create the AVD folder.
                avdFolder.mkdir();
            }

            // actually write the ini file
            iniFile = createAvdIniFile(name, avdFolder, target);

            // writes the userdata.img in it.
            String imagePath = target.getPath(IAndroidTarget.IMAGES);
            File userdataSrc = new File(imagePath, USERDATA_IMG);
            
            if (userdataSrc.exists() == false && target.isPlatform() == false) {
                imagePath = target.getParent().getPath(IAndroidTarget.IMAGES);
                userdataSrc = new File(imagePath, USERDATA_IMG);
            }
            
            if (userdataSrc.exists() == false) {
                mSdkLog.error(null, "Unable to find a '%1$s' file to copy into the AVD folder.",
                        USERDATA_IMG);
                needCleanup = true;
                return null;
            }
            
            FileInputStream fis = new FileInputStream(userdataSrc);
            
            File userdataDest = new File(avdFolder, USERDATA_IMG);
            FileOutputStream fos = new FileOutputStream(userdataDest);
            
            byte[] buffer = new byte[4096];
            int count;
            while ((count = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, count);
            }
            
            fos.close();
            fis.close();
            
            // Config file.
            HashMap<String, String> values = new HashMap<String, String>();

            if (setImagePathProperties(target, values) == false) {
                needCleanup = true;
                return null;
            }
            
            // Now the skin.
            if (skinName == null) {
                skinName = target.getDefaultSkin();
            }

            if (NUMERIC_SKIN_SIZE.matcher(skinName).matches()) {
                // Skin name is an actual screen resolution.
                // Set skin.name for display purposes in the AVD manager and
                // set skin.path for use by the emulator.
                values.put(AVD_INI_SKIN_NAME, skinName);
                values.put(AVD_INI_SKIN_PATH, skinName);
            } else {
                // get the path of the skin (relative to the SDK)
                // assume skin name is valid
                String skinPath = getSkinRelativePath(skinName, target);
                if (skinPath == null) {
                    needCleanup = true;
                    return null;
                }

                values.put(AVD_INI_SKIN_PATH, skinPath);
                values.put(AVD_INI_SKIN_NAME, skinName);
            }

            if (sdcard != null) {
                File sdcardFile = new File(sdcard);
                if (sdcardFile.isFile()) {
                    // sdcard value is an external sdcard, so we put its path into the config.ini
                    values.put(AVD_INI_SDCARD_PATH, sdcard);
                } else {
                    // Sdcard is possibly a size. In that case we create a file called 'sdcard.img'
                    // in the AVD folder, and do not put any value in config.ini.
                    
                    // First, check that it matches the pattern for sdcard size
                    Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard);
                    if (m.matches()) {
                        // create the sdcard.
                        sdcardFile = new File(avdFolder, SDCARD_IMG);
                        String path = sdcardFile.getAbsolutePath();
                        
                        // execute mksdcard with the proper parameters.
                        File toolsFolder = new File(mSdk.getLocation(), SdkConstants.FD_TOOLS);
                        File mkSdCard = new File(toolsFolder, SdkConstants.mkSdCardCmdName());
                        
                        if (mkSdCard.isFile() == false) {
                            mSdkLog.error(null, "'%1$s' is missing from the SDK tools folder.",
                                    mkSdCard.getName());
                            needCleanup = true;
                            return null;
                        }
                        
                        if (createSdCard(mkSdCard.getAbsolutePath(), sdcard, path) == false) {
                            needCleanup = true;
                            return null; // mksdcard output has already been displayed, no need to
                                         // output anything else.
                        }
                        
                        // add a property containing the size of the sdcard for display purpose
                        // only when the dev does 'android list avd'
                        values.put(AVD_INI_SDCARD_SIZE, sdcard);
                    } else {
                        mSdkLog.error(null,
                                "'%1$s' is not recognized as a valid sdcard value.\n" +
                                "Value should be:\n" +
                                "1. path to an sdcard.\n" +
                                "2. size of the sdcard to create: <size>[K|M]",
                                sdcard);
                        needCleanup = true;
                        return null;
                    }
                }
            }

            if (hardwareConfig != null) {
                values.putAll(hardwareConfig);
            }

            File configIniFile = new File(avdFolder, CONFIG_INI);
            writeIniFile(configIniFile, values);
            
            if (mSdkLog != null) {
                if (target.isPlatform()) {
                    mSdkLog.printf("Created AVD '%1$s' based on %2$s\n", name, target.getName());
                } else {
                    mSdkLog.printf("Created AVD '%1$s' based on %2$s (%3$s)\n", name, target.getName(),
                               target.getVendor());
                }
            }
            
            // create the AvdInfo object, and add it to the list
            AvdInfo avdInfo = new AvdInfo(name, avdFolder.getAbsolutePath(), target.hashString(),
                    target, values);
            
            synchronized (mAllAvdList) {
                mAllAvdList.add(avdInfo);
                mValidAvdList = mBrokenAvdList = null;
            }
            
            return avdInfo;
        } catch (AndroidLocationException e) {
            if (mSdkLog != null) {
                mSdkLog.error(e, null);
            }
        } catch (IOException e) {
            if (mSdkLog != null) {
                mSdkLog.error(e, null);
            }
        } finally {
            if (needCleanup) {
                if (iniFile != null && iniFile.exists()) {
                    iniFile.delete();
                }
                
                recursiveDelete(avdFolder);
                avdFolder.delete();
            }
        }
        
        return null;
    
private java.io.FilecreateAvdIniFile(java.lang.String name, java.io.File avdFolder, com.android.sdklib.IAndroidTarget target)
Creates the ini file for an AVD.

param
name of the AVD.
param
avdFolder path for the data folder of the AVD.
param
target of the AVD.
throws
AndroidLocationException if there's a problem getting android root directory.
throws
IOException if {@link File#getAbsolutePath()} fails.

        HashMap<String, String> values = new HashMap<String, String>();
        File iniFile = AvdInfo.getIniFile(name);
        values.put(AVD_INFO_PATH, avdFolder.getAbsolutePath());
        values.put(AVD_INFO_TARGET, target.hashString());
        writeIniFile(iniFile, values);

        return iniFile;
    
private java.io.FilecreateAvdIniFile(com.android.sdklib.avd.AvdManager$AvdInfo info)
Creates the ini file for an AVD.

param
info of the AVD.
throws
AndroidLocationException if there's a problem getting android root directory.
throws
IOException if {@link File#getAbsolutePath()} fails.

        return createAvdIniFile(info.getName(), new File(info.getPath()), info.getTarget());
    
private booleancreateSdCard(java.lang.String toolLocation, java.lang.String size, java.lang.String location)
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.

        try {
            String[] command = new String[3];
            command[0] = toolLocation;
            command[1] = size;
            command[2] = location;
            Process process = Runtime.getRuntime().exec(command);
    
            ArrayList<String> errorOutput = new ArrayList<String>();
            ArrayList<String> stdOutput = new ArrayList<String>();
            int status = grabProcessOutput(process, errorOutput, stdOutput,
                    true /* waitForReaders */);

            if (status == 0) {
                return true;
            } else {
                for (String error : errorOutput) {
                    mSdkLog.error(null, error);
                }
            }

        } catch (InterruptedException e) {
            // pass, print error below
        } catch (IOException e) {
            // pass, print error below
        }
        
        mSdkLog.error(null, "Failed to create the SD card.");
        return false;
    
public voiddeleteAvd(com.android.sdklib.avd.AvdManager$AvdInfo avdInfo, com.android.sdklib.ISdkLog log)
Actually deletes the files of an existing AVD.

This also remove it from the manager's list, The caller does not need to call {@link #removeAvd(AvdInfo)} afterwards.

This method is designed to somehow work with an unavailable AVD, that is an AVD that could not be loaded due to some error. That means this method still tries to remove the AVD ini file or its folder if it can be found. An error will be output if any of these operations fail.

param
avdInfo the information on the AVD to delete

        try {
            boolean error = false;
            
            File f = avdInfo.getIniFile();
            if (f != null && f.exists()) {
                log.warning("Deleting file %1$s", f.getCanonicalPath());
                if (!f.delete()) {
                    log.error(null, "Failed to delete %1$s", f.getCanonicalPath());
                    error = true;
                }
            }

            String path = avdInfo.getPath();
            if (path != null) {
                f = new File(path);
                if (f.exists()) {
                    log.warning("Deleting folder %1$s", f.getCanonicalPath());
                    recursiveDelete(f);
                    if (!f.delete()) {
                        log.error(null, "Failed to delete %1$s", f.getCanonicalPath());
                        error = true;
                    }
                }
            }

            removeAvd(avdInfo);

            if (error) {
                log.printf("AVD '%1$s' deleted with errors. See warnings above.\n",
                        avdInfo.getName());
            } else {
                log.printf("AVD '%1$s' deleted.\n", avdInfo.getName());
            }

        } catch (AndroidLocationException e) {
            log.error(e, null);
        } catch (IOException e) {
            log.error(e, null);
        }
    
public com.android.sdklib.avd.AvdManager$AvdInfo[]getAllAvds()
Returns all the existing AVDs.

return
a newly allocated array containing all the AVDs.

        synchronized (mAllAvdList) {
            return mAllAvdList.toArray(new AvdInfo[mAllAvdList.size()]);
        }
    
public com.android.sdklib.avd.AvdManager$AvdInfogetAvd(java.lang.String name, boolean validAvdOnly)
Returns the {@link AvdInfo} matching the given name.

param
name the name of the AVD to return
param
validAvdOnly if true, only look through the list of valid AVDs.
return
the matching AvdInfo or null if none were found.

        if (validAvdOnly) {
            for (AvdInfo info : getValidAvds()) {
                if (info.getName().equals(name)) {
                    return info;
                }
            }
        } else {
            synchronized (mAllAvdList) {
                for (AvdInfo info : getValidAvds()) {
                    if (info.getName().equals(name)) {
                        return info;
                    }
                }
            }
        }

        return null;
    
public com.android.sdklib.avd.AvdManager$AvdInfo[]getBrokenAvds()
Returns all the broken AVDs.

return
a newly allocated array containing all the broken AVDs.

        synchronized (mAllAvdList) {
            if (mBrokenAvdList == null) {
                ArrayList<AvdInfo> list = new ArrayList<AvdInfo>();
                for (AvdInfo avd : mAllAvdList) {
                    if (avd.getStatus() != AvdStatus.OK) {
                        list.add(avd);
                    }
                }
                mBrokenAvdList = list.toArray(new AvdInfo[list.size()]);
            }
            return mBrokenAvdList;
        }
    
private java.lang.StringgetImageRelativePath(com.android.sdklib.IAndroidTarget target)
Returns the path to the target images folder as a relative path to the SDK, if the folder is not empty. If the image folder is empty or does not exist, null is returned.

throws
InvalidTargetPathException if the target image folder is not in the current SDK.

        String imageFullPath = target.getPath(IAndroidTarget.IMAGES);

        // make this path relative to the SDK location
        String sdkLocation = mSdk.getLocation();
        if (imageFullPath.startsWith(sdkLocation) == false) {
            // this really really should not happen.
            assert false;
            throw new InvalidTargetPathException("Target location is not inside the SDK.");
        }
        
        File folder = new File(imageFullPath);
        if (folder.isDirectory()) {
            String[] list = folder.list(new FilenameFilter() {
                public boolean accept(File dir, String name) {
                    return IMAGE_NAME_PATTERN.matcher(name).matches();
                }
            });

            if (list.length > 0) {
                imageFullPath = imageFullPath.substring(sdkLocation.length());
                if (imageFullPath.charAt(0) == File.separatorChar) {
                    imageFullPath = imageFullPath.substring(1);
                }
        
                return imageFullPath;
            }
        }
        
        return null;
    
private java.lang.StringgetSkinRelativePath(java.lang.String skinName, com.android.sdklib.IAndroidTarget target)
Returns the path to the skin, as a relative path to the SDK.

        // first look to see if the skin is in the target
        
        String path = target.getPath(IAndroidTarget.SKINS);
        File skin = new File(path, skinName);
        
        if (skin.exists() == false && target.isPlatform() == false) {
            target = target.getParent();

            path = target.getPath(IAndroidTarget.SKINS);
            skin = new File(path, skinName);
        }
        
        // skin really does not exist!
        if (skin.exists() == false) {
            mSdkLog.error(null, "Skin '%1$s' does not exist.", skinName);
            return null;
        }
        
        // get the skin path
        path = skin.getAbsolutePath();

        // make this path relative to the SDK location
        String sdkLocation = mSdk.getLocation();
        if (path.startsWith(sdkLocation) == false) {
            // this really really should not happen.
            mSdkLog.error(null, "Target location is not inside the SDK.");
            assert false;
            return null;
        }

        path = path.substring(sdkLocation.length());
        if (path.charAt(0) == File.separatorChar) {
            path = path.substring(1);
        }
        return path;
    
public com.android.sdklib.avd.AvdManager$AvdInfo[]getValidAvds()
Returns all the valid AVDs.

return
a newly allocated array containing all valid the AVDs.

        synchronized (mAllAvdList) {
            if (mValidAvdList == null) {
                ArrayList<AvdInfo> list = new ArrayList<AvdInfo>();
                for (AvdInfo avd : mAllAvdList) {
                    if (avd.getStatus() == AvdStatus.OK) {
                        list.add(avd);
                    }
                }
                
                mValidAvdList = list.toArray(new AvdInfo[list.size()]);
            }
            return mValidAvdList;
        }
    
private intgrabProcessOutput(java.lang.Process process, java.util.ArrayList errorOutput, java.util.ArrayList stdOutput, boolean waitforReaders)
Gets the stderr/stdout outputs of a process and returns when the process is done. Both must be read or the process will block on windows.

param
process The process to get the ouput from
param
errorOutput The array to store the stderr output. cannot be null.
param
stdOutput The array to store the stdout output. cannot be null.
param
waitforReaders if true, this will wait for the reader threads.
return
the process return code.
throws
InterruptedException

        assert errorOutput != null;
        assert stdOutput != null;
        // read the lines as they come. if null is returned, it's
        // because the process finished
        Thread t1 = new Thread("") { //$NON-NLS-1$
            @Override
            public void run() {
                // create a buffer to read the stderr output
                InputStreamReader is = new InputStreamReader(process.getErrorStream());
                BufferedReader errReader = new BufferedReader(is);

                try {
                    while (true) {
                        String line = errReader.readLine();
                        if (line != null) {
                            errorOutput.add(line);
                        } else {
                            break;
                        }
                    }
                } catch (IOException e) {
                    // do nothing.
                }
            }
        };

        Thread t2 = new Thread("") { //$NON-NLS-1$
            @Override
            public void run() {
                InputStreamReader is = new InputStreamReader(process.getInputStream());
                BufferedReader outReader = new BufferedReader(is);

                try {
                    while (true) {
                        String line = outReader.readLine();
                        if (line != null) {
                            stdOutput.add(line);
                        } else {
                            break;
                        }
                    }
                } catch (IOException e) {
                    // do nothing.
                }
            }
        };

        t1.start();
        t2.start();

        // it looks like on windows process#waitFor() can return
        // before the thread have filled the arrays, so we wait for both threads and the
        // process itself.
        if (waitforReaders) {
            try {
                t1.join();
            } catch (InterruptedException e) {
            }
            try {
                t2.join();
            } catch (InterruptedException e) {
            }
        }

        // get the return code from the process
        return process.waitFor();
    
public booleanmoveAvd(com.android.sdklib.avd.AvdManager$AvdInfo avdInfo, java.lang.String newName, java.lang.String paramFolderPath, com.android.sdklib.ISdkLog log)
Moves and/or rename an existing AVD and its files. This also change it in the manager's list.

The caller should make sure the name or path given are valid, do not exist and are actually different than current values.

param
avdInfo the information on the AVD to move.
param
newName the new name of the AVD if non null.
param
paramFolderPath the new data folder if non null.
return
True if the move succeeded or there was nothing to do. If false, this method will have had already output error in the log.

        
        try {
            if (paramFolderPath != null) {
                File f = new File(avdInfo.getPath());
                log.warning("Moving '%1$s' to '%2$s'.", avdInfo.getPath(), paramFolderPath);
                if (!f.renameTo(new File(paramFolderPath))) {
                    log.error(null, "Failed to move '%1$s' to '%2$s'.",
                            avdInfo.getPath(), paramFolderPath);
                    return false;
                }
    
                // update AVD info
                AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath,
                        avdInfo.getTargetHash(), avdInfo.getTarget(), avdInfo.getProperties());
                replaceAvd(avdInfo, info);

                // update the ini file
                createAvdIniFile(avdInfo);
            }

            if (newName != null) {
                File oldIniFile = avdInfo.getIniFile();
                File newIniFile = AvdInfo.getIniFile(newName);
                
                log.warning("Moving '%1$s' to '%2$s'.", oldIniFile.getPath(), newIniFile.getPath());
                if (!oldIniFile.renameTo(newIniFile)) {
                    log.error(null, "Failed to move '%1$s' to '%2$s'.", 
                            oldIniFile.getPath(), newIniFile.getPath());
                    return false;
                }

                // update AVD info
                AvdInfo info = new AvdInfo(newName, avdInfo.getPath(),
                        avdInfo.getTargetHash(), avdInfo.getTarget(), avdInfo.getProperties());
                replaceAvd(avdInfo, info);
            }

            log.printf("AVD '%1$s' moved.\n", avdInfo.getName());

        } catch (AndroidLocationException e) {
            log.error(e, null);
        } catch (IOException e) {
            log.error(e, null);
        }

        // nothing to do or succeeded
        return true;
    
private com.android.sdklib.avd.AvdManager$AvdInfoparseAvdInfo(java.io.File path)
Parses an AVD .ini file to create an {@link AvdInfo}.

param
path The path to the AVD .ini file
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.

        Map<String, String> map = SdkManager.parsePropertyFile(path, mSdkLog);

        String avdPath = map.get(AVD_INFO_PATH);
        String targetHash = map.get(AVD_INFO_TARGET);

        IAndroidTarget target = null;
        File configIniFile = null;
        Map<String, String> properties = null;
        
        if (targetHash != null) {
            target = mSdk.getTargetFromHashString(targetHash);
        }

        // load the AVD properties.
        if (avdPath != null) {
            configIniFile = new File(avdPath, CONFIG_INI);
        }
        
        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);
        }
        
        // check the image.sysdir are valid
        boolean validImageSysdir = true;
        if (properties != null) {
            String imageSysDir = properties.get(AVD_INI_IMAGES_1);
            if (imageSysDir != null) {
                File f = new File(mSdk.getLocation() + File.separator + imageSysDir);
                if (f.isDirectory() == false) {
                    validImageSysdir = false;
                } else {
                    imageSysDir = properties.get(AVD_INI_IMAGES_2);
                    if (imageSysDir != null) {
                        f = new File(mSdk.getLocation() + File.separator + imageSysDir);
                        if (f.isDirectory() == false) {
                            validImageSysdir = false;
                        }
                    }
                }
            }
        }

        AvdStatus status;
        
        if (avdPath == null) {
            status = AvdStatus.ERROR_PATH;
        } else if (configIniFile == null) {
            status = AvdStatus.ERROR_CONFIG;
        } else if (targetHash == null) {
            status = AvdStatus.ERROR_TARGET_HASH;
        } else if (target == null) {
            status = AvdStatus.ERROR_TARGET;
        } else if (properties == null) {
            status = AvdStatus.ERROR_PROPERTIES;
        } else if (validImageSysdir == false) {
            status = AvdStatus.ERROR_IMAGE_DIR;
        } else {
            status = AvdStatus.OK;
        }
        
        AvdInfo info = new AvdInfo(
                name,
                avdPath,
                targetHash,
                target,
                properties,
                status);
        
        return info;
    
public voidrecursiveDelete(java.io.File folder)
Helper method to recursively delete a folder's content (but not the folder itself).

throws
SecurityException like {@link File#delete()} does if file/folder is not writable.

        for (File f : folder.listFiles()) {
            if (f.isDirectory()) {
                recursiveDelete(folder);
            }
            f.delete();
        }
    
public voidreloadAvds()
Reloads the AVD list.

throws
AndroidLocationException if there was an error finding the location of the AVD folder.

        // build the list in a temp list first, in case the method throws an exception.
        // It's better than deleting the whole list before reading the new one.
        ArrayList<AvdInfo> allList = new ArrayList<AvdInfo>();
        buildAvdList(allList);

        synchronized (mAllAvdList) {
            mAllAvdList.clear();
            mAllAvdList.addAll(allList);
            mValidAvdList = mBrokenAvdList = null;
        }
    
public booleanremoveAvd(com.android.sdklib.avd.AvdManager$AvdInfo avdInfo)
Removes an {@link AvdInfo} from the internal list.

param
avdInfo The {@link AvdInfo} to remove.
return
true if this {@link AvdInfo} was present and has been removed.

        synchronized (mAllAvdList) {
            if (mAllAvdList.remove(avdInfo)) {
                mValidAvdList = mBrokenAvdList = null;
                return true;
            }
        }
        
        return false;
    
private voidreplaceAvd(com.android.sdklib.avd.AvdManager$AvdInfo oldAvd, com.android.sdklib.avd.AvdManager$AvdInfo newAvd)
Replaces an old {@link AvdInfo} with a new one in the lists storing them.

param
oldAvd the {@link AvdInfo} to remove.
param
newAvd the {@link AvdInfo} to add.

        synchronized (mAllAvdList) {
            mAllAvdList.remove(oldAvd);
            mAllAvdList.add(newAvd);
            mValidAvdList = mBrokenAvdList = null;
        }
    
private booleansetImagePathProperties(com.android.sdklib.IAndroidTarget target, java.util.Map properties)
Sets the paths to the system images in a properties map.

param
target the target in which to find the system images.
param
properties the properties in which to set the paths.
return
true if success, false if some path are missing.

        properties.remove(AVD_INI_IMAGES_1);
        properties.remove(AVD_INI_IMAGES_2);
        
        try {
            String property = AVD_INI_IMAGES_1;
            
            // First the image folders of the target itself
            String imagePath = getImageRelativePath(target);
            if (imagePath != null) {
                properties.put(property, imagePath);
                property = AVD_INI_IMAGES_2;
            }
    
    
            // If the target is an add-on we need to add the Platform image as a backup.
            IAndroidTarget parent = target.getParent();
            if (parent != null) {
                imagePath = getImageRelativePath(parent);
                if (imagePath != null) {
                    properties.put(property, imagePath);
                }
            }
            
            // we need at least one path!
            return properties.containsKey(AVD_INI_IMAGES_1);
        } catch (InvalidTargetPathException e) {
            mSdkLog.error(e, e.getMessage());
        }
        
        return false;
    
public voidupdateAvd(java.lang.String name)
Updates an AVD with new path to the system image folders.

param
name the name of the AVD to update.
throws
IOException
throws
AndroidLocationException

        // find the AVD to update. It should be be in the broken list.
        AvdInfo avd = null;
        synchronized (mAllAvdList) {
            for (AvdInfo info : mAllAvdList) {
                if (info.getName().equals(name)) {
                    avd = info;
                    break;
                }
            }
        }
        
        if (avd == null) {
            // not in the broken list, just return.
            mSdkLog.error(null, "There is no Android Virtual Device named '%s'.", name);
            return;
        }

        // get the properties. This is a unmodifiable Map.
        Map<String, String> oldProperties = avd.getProperties();

        // create a new map
        Map<String, String> properties = new HashMap<String, String>();
        properties.putAll(oldProperties);
        
        AvdStatus status;
        
        // create the path to the new system images.
        if (setImagePathProperties(avd.getTarget(), properties)) {
            if (properties.containsKey(AVD_INI_IMAGES_1)) {
                mSdkLog.printf("Updated '%1$s' with value '%2$s'\n", AVD_INI_IMAGES_1,
                        properties.get(AVD_INI_IMAGES_1));
            }
    
            if (properties.containsKey(AVD_INI_IMAGES_2)) {
                mSdkLog.printf("Updated '%1$s' with value '%2$s'\n", AVD_INI_IMAGES_2,
                        properties.get(AVD_INI_IMAGES_2));
            }
            
            status = AvdStatus.OK;
        } else {
            mSdkLog.error(null, "Unable to find non empty system images folders for %1$s", name);
            //FIXME: display paths to empty image folders?
            status = AvdStatus.ERROR_IMAGE_DIR;
        }

        // now write the config file
        File configIniFile = new File(avd.getPath(), CONFIG_INI);
        writeIniFile(configIniFile, properties);

        // finally create a new AvdInfo for this unbroken avd and add it to the list.
        // instead of creating the AvdInfo object directly we reparse it, to detect other possible
        // errors
        // FIXME: We may want to create this AvdInfo by reparsing the AVD instead. This could detect other errors.
        AvdInfo newAvd = new AvdInfo(
                name,
                avd.getPath(),
                avd.getTargetHash(),
                avd.getTarget(),
                properties,
                status);
        
        replaceAvd(avd, newAvd);
    
private static voidwriteIniFile(java.io.File iniFile, java.util.Map values)
Writes a .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.

        FileWriter writer = new FileWriter(iniFile);
        
        for (Entry<String, String> entry : values.entrySet()) {
            writer.write(String.format("%1$s=%2$s\n", entry.getKey(), entry.getValue()));
        }
        writer.close();