Merge "Add suppress-this to Lint View, and preserve tree expansion state"
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsPage.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsPage.java
index 1fb64b4..88e7b05 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/DetailsPage.java
@@ -50,6 +50,7 @@
 
     private List<IStateDetailsProvider> mStateDetailProviders = Arrays.asList(
             new ShaderSourceDetailsProvider(),
+            new ShaderUniformDetailsProvider(),
             new TextureImageDetailsProvider());
 
     public DetailsPage(GLTrace trace) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/ShaderUniformDetailsProvider.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/ShaderUniformDetailsProvider.java
new file mode 100644
index 0000000..4111b77
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/views/ShaderUniformDetailsProvider.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.gltrace.views;
+
+import com.android.ide.eclipse.gldebugger.GLEnum;
+import com.android.ide.eclipse.gltrace.state.GLCompositeProperty;
+import com.android.ide.eclipse.gltrace.state.GLStateType;
+import com.android.ide.eclipse.gltrace.state.IGLProperty;
+import com.google.common.base.Joiner;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ShaderUniformDetailsProvider implements IStateDetailsProvider {
+    private Text mTextControl;
+    private static final Joiner JOINER = Joiner.on(", ");
+
+    @Override
+    public boolean isApplicable(IGLProperty state) {
+        return getShaderUniformProperty(state) != null;
+    }
+
+    @Override
+    public void createControl(Composite parent) {
+        mTextControl = new Text(parent, SWT.BORDER | SWT.READ_ONLY | SWT.MULTI | SWT.WRAP);
+        mTextControl.setEditable(false);
+    }
+
+    @Override
+    public void disposeControl() {
+    }
+
+    @Override
+    public Control getControl() {
+        return mTextControl;
+    }
+
+    @Override
+    public void updateControl(IGLProperty state) {
+        IGLProperty uniform = getShaderUniformProperty(state);
+        if (uniform instanceof GLCompositeProperty) {
+            GLCompositeProperty uniformProperty = (GLCompositeProperty) uniform;
+            IGLProperty nameProperty = uniformProperty.getProperty(GLStateType.UNIFORM_NAME);
+            IGLProperty typeProperty = uniformProperty.getProperty(GLStateType.UNIFORM_TYPE);
+            IGLProperty valueProperty = uniformProperty.getProperty(GLStateType.UNIFORM_VALUE);
+
+            String name = (String) nameProperty.getValue();
+            GLEnum type = (GLEnum) typeProperty.getValue();
+            Object value = valueProperty.getValue();
+            mTextControl.setText(formatUniform(name, type, value));
+            mTextControl.setEnabled(true);
+        } else {
+            mTextControl.setText(""); //$NON-NLS-1$
+            mTextControl.setEnabled(false);
+        }
+    }
+
+    private String formatUniform(String name, GLEnum type, Object value) {
+        String valueText;
+
+        switch (type) {
+            case GL_FLOAT:
+            case GL_FLOAT_VEC2:
+            case GL_FLOAT_VEC3:
+            case GL_FLOAT_VEC4:
+            case GL_INT:
+            case GL_INT_VEC2:
+            case GL_INT_VEC3:
+            case GL_INT_VEC4:
+            case GL_BOOL:
+            case GL_BOOL_VEC2:
+            case GL_BOOL_VEC3:
+            case GL_BOOL_VEC4:
+                valueText = formatVector(value);
+                break;
+            case GL_FLOAT_MAT2:
+                valueText = formatMatrix(2, value);
+                break;
+            case GL_FLOAT_MAT3:
+                valueText = formatMatrix(3, value);
+                break;
+            case GL_FLOAT_MAT4:
+                valueText = formatMatrix(4, value);
+                break;
+            case GL_SAMPLER_2D:
+            case GL_SAMPLER_CUBE:
+            default:
+                valueText = value.toString();
+                break;
+        }
+
+        return String.format("%s %s = %s", type, name, valueText); //$NON-NLS-1$
+    }
+
+    private String formatVector(Object value) {
+        if (value instanceof List<?>) {
+            List<?> list = (List<?>) value;
+            StringBuilder sb = new StringBuilder(list.size() * 4);
+            sb.append('[');
+            JOINER.appendTo(sb, list);
+            sb.append(']');
+            return sb.toString();
+        }
+
+        return value.toString();
+    }
+
+    private String formatMatrix(int dimension, Object value) {
+        if (value instanceof List<?>) {
+            List<?> list = (List<?>) value;
+            if (list.size() != dimension * dimension) {
+                // Uniforms can only be square matrices, so this scenario should
+                // not occur.
+                return formatVector(value);
+            }
+
+            StringBuilder sb = new StringBuilder(list.size() * 4);
+            sb.append('[');
+            sb.append('\n');
+            for (int i = 0; i < dimension; i++) {
+                sb.append("    "); //$NON-NLS-1$
+                JOINER.appendTo(sb, list.subList(i * dimension, (i + 1) * dimension));
+                sb.append('\n');
+            }
+            sb.append(']');
+            return sb.toString();
+        }
+
+        return value.toString();
+    }
+
+    /**
+     * Get the {@link GLStateType#PER_UNIFORM_STATE} property given a node in
+     * the state hierarchy.
+     */
+    private IGLProperty getShaderUniformProperty(IGLProperty state) {
+        if (state.getType() == GLStateType.PER_UNIFORM_STATE) {
+            return state;
+        }
+
+        state = state.getParent();
+        if (state != null && state.getType() == GLStateType.PER_UNIFORM_STATE) {
+            return state;
+        }
+
+        return null;
+    }
+
+    @Override
+    public List<IContributionItem> getToolBarItems() {
+        return Collections.emptyList();
+    }
+}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java
index 88b3c28..75aaa0b 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Category.java
@@ -163,5 +163,5 @@
     public static final Category TYPOGRAPHY = Category.create(USABILITY, "Typography", null, 76);
 
     /** Issues related to messages/strings */
-    public static final Category MESSAGES = Category.create(CORRECTNESS, "Messages", null, 40);
+    public static final Category MESSAGES = Category.create(CORRECTNESS, "Messages", null, 95);
 }
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
index d13108a..4f3ed9f 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
@@ -114,41 +114,45 @@
     /** dx.jar file */
     public static final String FN_DX_JAR = "dx.jar";                                  //$NON-NLS-1$
 
-    /** dx executable (with extension for the current OS)  */
-    public final static String FN_DX = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
-            "dx.bat" : "dx";                                            //$NON-NLS-1$ //$NON-NLS-2$
+    /** dx executable (with extension for the current OS) */
+    public final static String FN_DX =
+        "dx" + ext(".bat", "");                           //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
-    /** aapt executable (with extension for the current OS)  */
-    public final static String FN_AAPT = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
-            "aapt.exe" : "aapt";                                        //$NON-NLS-1$ //$NON-NLS-2$
+    /** aapt executable (with extension for the current OS) */
+    public final static String FN_AAPT =
+        "aapt" + ext(".exe", "");                         //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
-    /** aidl executable (with extension for the current OS)  */
-    public final static String FN_AIDL = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
-            "aidl.exe" : "aidl";                                        //$NON-NLS-1$ //$NON-NLS-2$
+    /** aidl executable (with extension for the current OS) */
+    public final static String FN_AIDL =
+        "aidl" + ext(".exe", "");                         //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
-    /** renderscript executable (with extension for the current OS)  */
-    public final static String FN_RENDERSCRIPT = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
-            "llvm-rs-cc.exe" : "llvm-rs-cc";                            //$NON-NLS-1$ //$NON-NLS-2$
+    /** renderscript executable (with extension for the current OS) */
+    public final static String FN_RENDERSCRIPT =
+        "llvm-rs-cc" + ext(".exe", "");                   //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
-    /** adb executable (with extension for the current OS)  */
-    public final static String FN_ADB = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
-            "adb.exe" : "adb";                                          //$NON-NLS-1$ //$NON-NLS-2$
+    /** adb executable (with extension for the current OS) */
+    public final static String FN_ADB =
+        "adb" + ext(".exe", "");                          //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
     /** emulator executable for the current OS */
-    public final static String FN_EMULATOR = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
-            "emulator.exe" : "emulator";                                //$NON-NLS-1$ //$NON-NLS-2$
+    public final static String FN_EMULATOR =
+        "emulator" + ext(".exe", "");                     //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
-    /** zipalign executable (with extension for the current OS)  */
-    public final static String FN_ZIPALIGN = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
-            "zipalign.exe" : "zipalign";                                //$NON-NLS-1$ //$NON-NLS-2$
+    /** zipalign executable (with extension for the current OS) */
+    public final static String FN_ZIPALIGN =
+        "zipalign" + ext(".exe", "");                     //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
-    /** dexdump executable (with extension for the current OS)  */
-    public final static String FN_DEXDUMP = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
-            "dexdump.exe" : "dexdump";                                  //$NON-NLS-1$ //$NON-NLS-2$
+    /** dexdump executable (with extension for the current OS) */
+    public final static String FN_DEXDUMP =
+        "dexdump" + ext(".exe", "");                      //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
-    /** zipalign executable (with extension for the current OS)  */
-    public final static String FN_PROGUARD = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
-            "proguard.bat" : "proguard.sh";                             //$NON-NLS-1$ //$NON-NLS-2$
+    /** proguard executable (with extension for the current OS) */
+    public final static String FN_PROGUARD =
+        "proguard" + ext(".bat", ".sh");                  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+    /** find_lock for Windows (with extension for the current OS) */
+    public final static String FN_FIND_LOCK =
+        "find_lock" + ext(".exe", "");                    //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
     /** properties file for SDK Updater packages */
     public final static String FN_SOURCE_PROP = "source.properties";                  //$NON-NLS-1$
@@ -476,4 +480,12 @@
 
         return "Other";
     }
+
+    private static String ext(String windowsExtension, String nonWindowsExtension) {
+        if (CURRENT_PLATFORM == PLATFORM_WINDOWS) {
+            return windowsExtension;
+        } else {
+            return nonWindowsExtension;
+        }
+    }
 }
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
index 6ce1118..a784b34 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
@@ -26,6 +26,7 @@
 import com.android.sdklib.internal.repository.Archive.Arch;

 import com.android.sdklib.internal.repository.Archive.Os;

 import com.android.sdklib.repository.PkgProps;

+import com.android.sdklib.repository.SdkAddonConstants;

 import com.android.sdklib.repository.SdkRepoConstants;

 import com.android.util.Pair;

 

@@ -44,8 +45,10 @@
 public class AddonPackage extends Package

     implements IPackageVersion, IPlatformDependency, IExactApiLevelDependency, ILayoutlibVersion {

 

-    private final String mVendor;

-    private final String mName;

+    private final String mVendorId;

+    private final String mVendorDisplay;

+    private final String mNameId;

+    private final String mDisplayName;

     private final AndroidVersion mVersion;

 

     /**

@@ -122,14 +125,74 @@
      *          parameters that vary according to the originating XML schema.

      * @param licenses The licenses loaded from the XML originating document.

      */

-    AddonPackage(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) {

+    AddonPackage(

+            SdkSource source,

+            Node packageNode,

+            String nsUri,

+            Map<String,String> licenses) {

         super(source, packageNode, nsUri, licenses);

-        mVendor   = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_VENDOR);

-        mName     = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_NAME);

-        int apiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);

+

+        // --- name id/display ---

+        // addon-4.xsd introduces the name-id, name-display, vendor-id and vendor-display.

+        // These are not optional but we still need to support a fallback for older addons

+        // that only provide name and vendor. If the addon provides neither set of fields,

+        // it will simply not work as expected.

+

+        String nameId   = XmlParserUtils.getXmlString(packageNode,

+                                                      SdkRepoConstants.NODE_NAME_ID);

+        String nameDisp = XmlParserUtils.getXmlString(packageNode,

+                                                      SdkRepoConstants.NODE_NAME_DISPLAY);

+        String name     = XmlParserUtils.getXmlString(packageNode,

+                                                      SdkRepoConstants.NODE_NAME);

+

+        // The old <name> is equivalent to the new <name-display>

+        if (nameDisp.length() == 0) {

+            nameDisp = name;

+        }

+

+        // For a missing id, we simply use a sanitized version of the display name

+        if (nameId.length() == 0) {

+            nameId = sanitizeDisplayToNameId(name.length() > 0 ? name : nameDisp);

+        }

+

+        assert nameId.length() > 0;

+        assert nameDisp.length() > 0;

+

+        mNameId = nameId.trim();

+        mDisplayName = nameDisp.trim();

+

+        // --- vendor id/display ---

+        // Same processing for vendor id vs display

+

+        String vendorId   = XmlParserUtils.getXmlString(packageNode,

+                                                        SdkAddonConstants.NODE_VENDOR_ID);

+        String vendorDisp = XmlParserUtils.getXmlString(packageNode,

+                                                        SdkAddonConstants.NODE_VENDOR_DISPLAY);

+        String vendor     = XmlParserUtils.getXmlString(packageNode,

+                                                        SdkAddonConstants.NODE_VENDOR);

+

+        // The old <vendor> is equivalent to the new <vendor-display>

+        if (vendorDisp.length() == 0) {

+            vendorDisp = vendor;

+        }

+

+        // For a missing id, we simply use a sanitized version of the display vendor

+        if (vendorId.length() == 0) {

+            vendorId = sanitizeDisplayToNameId(vendor.length() > 0 ? vendor : vendorDisp);

+        }

+

+        assert vendorId.length() > 0;

+        assert vendorDisp.length() > 0;

+

+        mVendorId      = vendorId.trim();

+        mVendorDisplay = vendorDisp.trim();

+

+        // --- other attributes

+

+        int apiLevel = XmlParserUtils.getXmlInt(packageNode, SdkAddonConstants.NODE_API_LEVEL, 0);

         mVersion = new AndroidVersion(apiLevel, null /*codeName*/);

 

-        mLibs = parseLibs(XmlParserUtils.getFirstChild(packageNode, SdkRepoConstants.NODE_LIBS));

+        mLibs = parseLibs(XmlParserUtils.getFirstChild(packageNode, SdkAddonConstants.NODE_LIBS));

 

         mLayoutlibVersion = new LayoutlibVersionMixin(packageNode);

     }

@@ -164,9 +227,58 @@
                 target.getLocation()        //archiveOsPath

                 );

 

+        // --- name id/display ---

+        // addon-4.xsd introduces the name-id, name-display, vendor-id and vendor-display.

+        // These are not optional but we still need to support a fallback for older addons

+        // that only provide name and vendor. If the addon provides neither set of fields,

+        // it will simply not work as expected.

+

+        String nameId   = getProperty(props, PkgProps.ADDON_NAME_ID, "");           //$NON-NLS-1$

+        String nameDisp = getProperty(props, PkgProps.ADDON_NAME_DISPLAY, "");      //$NON-NLS-1$

+        String name     = getProperty(props, PkgProps.ADDON_NAME, target.getName());

+

+        // The old <name> is equivalent to the new <name-display>

+        if (nameDisp.length() == 0) {

+            nameDisp = name;

+        }

+

+        // For a missing id, we simply use a sanitized version of the display name

+        if (nameId.length() == 0) {

+            nameId = sanitizeDisplayToNameId(name.length() > 0 ? name : nameDisp);

+        }

+

+        assert nameId.length() > 0;

+        assert nameDisp.length() > 0;

+

+        mNameId = nameId.trim();

+        mDisplayName = nameDisp.trim();

+

+        // --- vendor id/display ---

+        // Same processing for vendor id vs display

+

+        String vendorId   = getProperty(props, PkgProps.ADDON_VENDOR_ID, "");       //$NON-NLS-1$

+        String vendorDisp = getProperty(props, PkgProps.ADDON_VENDOR_DISPLAY, "");  //$NON-NLS-1$

+        String vendor     = getProperty(props, PkgProps.ADDON_VENDOR, target.getVendor());

+

+        // The old <vendor> is equivalent to the new <vendor-display>

+        if (vendorDisp.length() == 0) {

+            vendorDisp = vendor;

+        }

+

+        // For a missing id, we simply use a sanitized version of the display vendor

+        if (vendorId.length() == 0) {

+            vendorId = sanitizeDisplayToNameId(vendor.length() > 0 ? vendor : vendorDisp);

+        }

+

+        assert vendorId.length() > 0;

+        assert vendorDisp.length() > 0;

+

+        mVendorId = vendorId.trim();

+        mVendorDisplay = vendorDisp.trim();

+

+        // --- other attributes

+

         mVersion = target.getVersion();

-        mName     = target.getName();

-        mVendor   = target.getVendor();

         mLayoutlibVersion = new LayoutlibVersionMixin(props);

 

         IOptionalLibrary[] optLibs = target.getOptionalLibraries();

@@ -184,14 +296,27 @@
      * Creates a broken addon which we know failed to load properly.

      *

      * @param archiveOsPath The absolute OS path of the addon folder.

-     * @param props The properties parsed from the addon manifest (not the source.properties).

+     * @param sourceProps The properties parsed from the addon's source.properties. Can be null.

+     * @param addonProps The properties parsed from the addon manifest (NOT the source.properties).

      * @param error The error indicating why this addon failed to be loaded.

      */

-    static Package createBroken(String archiveOsPath, Map<String, String> props, String error) {

-        String name     = props.get(SdkManager.ADDON_NAME);

-        String vendor   = props.get(SdkManager.ADDON_VENDOR);

-        String api      = props.get(SdkManager.ADDON_API);

-        String revision = props.get(SdkManager.ADDON_REVISION);

+    static Package createBroken(

+            String archiveOsPath,

+            Properties sourceProps,

+            Map<String, String> addonProps,

+            String error) {

+        String name     = getProperty(sourceProps,

+                                      PkgProps.ADDON_NAME_DISPLAY,

+                                      getProperty(sourceProps,

+                                                  PkgProps.ADDON_NAME,

+                                                  addonProps.get(SdkManager.ADDON_NAME)));

+        String vendor   = getProperty(sourceProps,

+                                      PkgProps.ADDON_VENDOR_DISPLAY,

+                                      getProperty(sourceProps,

+                                                  PkgProps.ADDON_VENDOR,

+                                                  addonProps.get(SdkManager.ADDON_VENDOR)));

+        String api      = addonProps.get(SdkManager.ADDON_API);

+        String revision = addonProps.get(SdkManager.ADDON_REVISION);

 

         String shortDesc = String.format("%1$s by %2$s, Android API %3$s, revision %4$s [*]",

                 name,

@@ -235,12 +360,10 @@
         mVersion.saveProperties(props);

         mLayoutlibVersion.saveProperties(props);

 

-        if (mName != null) {

-            props.setProperty(PkgProps.ADDON_NAME, mName);

-        }

-        if (mVendor != null) {

-            props.setProperty(PkgProps.ADDON_VENDOR, mVendor);

-        }

+        props.setProperty(PkgProps.ADDON_NAME_ID,        mNameId);

+        props.setProperty(PkgProps.ADDON_NAME_DISPLAY,   mDisplayName);

+        props.setProperty(PkgProps.ADDON_VENDOR_ID,      mVendorId);

+        props.setProperty(PkgProps.ADDON_VENDOR_DISPLAY, mVendorDisplay);

     }

 

     /**

@@ -274,14 +397,24 @@
                        XmlParserUtils.getXmlString(libNode, SdkRepoConstants.NODE_DESCRIPTION));

     }

 

-    /** Returns the vendor, a string, for add-on packages. */

-    public String getVendor() {

-        return mVendor;

+    /** Returns the vendor id, a string, for add-on packages. */

+    public String getVendorId() {

+        return mVendorId;

     }

 

-    /** Returns the name, a string, for add-on packages or for libraries. */

-    public String getName() {

-        return mName;

+    /** Returns the vendor, a string for display purposes. */

+    public String getDisplayVendor() {

+        return mVendorDisplay;

+    }

+

+    /** Returns the name id, a string, for add-on packages or for libraries. */

+    public String getNameId() {

+        return mNameId;

+    }

+

+    /** Returns the name, a string for display purposes. */

+    public String getDisplayName() {

+        return mDisplayName;

     }

 

     /**

@@ -334,9 +467,8 @@
      */

     @Override

     public String getListDescription() {

-        return String.format("%1$s by %2$s%3$s",

-                getName(),

-                getVendor(),

+        return String.format("%1$s%2$s",

+                getDisplayName(),

                 isObsolete() ? " (Obsolete)" : "");

     }

 

@@ -345,9 +477,8 @@
      */

     @Override

     public String getShortDescription() {

-        return String.format("%1$s by %2$s, Android API %3$s, revision %4$s%5$s",

-                getName(),

-                getVendor(),

+        return String.format("%1$s, Android API %2$s, revision %3$s%4$s",

+                getDisplayName(),

                 mVersion.getApiString(),

                 getRevision(),

                 isObsolete() ? " (Obsolete)" : "");

@@ -361,15 +492,16 @@
      */

     @Override

     public String getLongDescription() {

-        String s = getDescription();

-        if (s == null || s.length() == 0) {

-            s = getShortDescription();

-        }

+        String s = String.format("%1$s, Android API %2$s, revision %3$s%4$s\nBy %5$s",

+                getDisplayName(),

+                mVersion.getApiString(),

+                getRevision(),

+                isObsolete() ? " (Obsolete)" : "",  //$NON-NLS-2$

+                getDisplayVendor());

 

-        if (s.indexOf("revision") == -1) {

-            s += String.format("\nRevision %1$d%2$s",

-                    getRevision(),

-                    isObsolete() ? " (Obsolete)" : "");

+        String d = getDescription();

+        if (d != null && d.length() > 0) {

+            s += '\n' + d;

         }

 

         s += String.format("\nRequires SDK Platform Android API %1$s",

@@ -396,11 +528,17 @@
 

         // First find if this add-on is already installed. If so, reuse the same directory.

         for (IAndroidTarget target : sdkManager.getTargets()) {

-            if (!target.isPlatform() &&

-                    target.getVersion().equals(mVersion) &&

-                    target.getName().equals(getName()) &&

-                    target.getVendor().equals(getVendor())) {

-                return new File(target.getLocation());

+            if (!target.isPlatform() && target.getVersion().equals(mVersion)) {

+                // Starting with addon-4.xsd, the addon source.properties differentiate

+                // between ids and display strings. However the addon target which relies

+                // on the manifest.ini does not so we need to cover both cases.

+                // TODO fix when we get rid of manifest.ini for addons

+                if ((target.getName().equals(getNameId()) &&

+                     target.getVendor().equals(getVendorId())) ||

+                    (target.getName().equals(getDisplayName()) &&

+                     target.getVendor().equals(getDisplayVendor()))) {

+                    return new File(target.getLocation());

+                }

             }

         }

 

@@ -421,21 +559,35 @@
 

     private String encodeAddonName() {

         String name = String.format("addon-%s-%s-%s",     //$NON-NLS-1$

-                                    getName(), getVendor(), mVersion.getApiString());

+                                    getNameId(), getVendorId(), mVersion.getApiString());

         name = name.toLowerCase(Locale.US);

         name = name.replaceAll("[^a-z0-9_-]+", "_");      //$NON-NLS-1$ //$NON-NLS-2$

         name = name.replaceAll("_+", "_");                //$NON-NLS-1$ //$NON-NLS-2$

         return name;

     }

 

+    /**

+     * Computes a sanitized name-id based on an addon name-display.

+     * This is used to provide compatibility with older addons that lacks the new fields.

+     *

+     * @param displayName A name-display field or a old-style name field.

+     * @return A non-null sanitized name-id that fits in the {@code [a-zA-Z0-9_-]+} pattern.

+     */

+    private String sanitizeDisplayToNameId(String displayName) {

+        String name = displayName.toLowerCase(Locale.US);

+        name = name.replaceAll("[^a-z0-9_-]+", "_");      //$NON-NLS-1$ //$NON-NLS-2$

+        name = name.replaceAll("_+", "_");                //$NON-NLS-1$ //$NON-NLS-2$

+        return name;

+    }

+

     @Override

     public boolean sameItemAs(Package pkg) {

         if (pkg instanceof AddonPackage) {

             AddonPackage newPkg = (AddonPackage)pkg;

 

             // check they are the same add-on.

-            return getName().equals(newPkg.getName()) &&

-                    getVendor().equals(newPkg.getVendor()) &&

+            return getNameId().equals(newPkg.getNameId()) &&

+                    getVendorId().equals(newPkg.getVendorId()) &&

                     getVersion().equals(newPkg.getVersion());

         }

 

@@ -448,8 +600,8 @@
         int result = super.hashCode();

         result = prime * result + ((mLayoutlibVersion == null) ? 0 : mLayoutlibVersion.hashCode());

         result = prime * result + Arrays.hashCode(mLibs);

-        result = prime * result + ((mName == null) ? 0 : mName.hashCode());

-        result = prime * result + ((mVendor == null) ? 0 : mVendor.hashCode());

+        result = prime * result + ((mDisplayName == null) ? 0 : mDisplayName.hashCode());

+        result = prime * result + ((mVendorDisplay == null) ? 0 : mVendorDisplay.hashCode());

         result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode());

         return result;

     }

@@ -476,18 +628,18 @@
         if (!Arrays.equals(mLibs, other.mLibs)) {

             return false;

         }

-        if (mName == null) {

-            if (other.mName != null) {

+        if (mNameId == null) {

+            if (other.mNameId != null) {

                 return false;

             }

-        } else if (!mName.equals(other.mName)) {

+        } else if (!mNameId.equals(other.mNameId)) {

             return false;

         }

-        if (mVendor == null) {

-            if (other.mVendor != null) {

+        if (mVendorId == null) {

+            if (other.mVendorId != null) {

                 return false;

             }

-        } else if (!mVendor.equals(other.mVendor)) {

+        } else if (!mVendorId.equals(other.mVendorId)) {

             return false;

         }

         if (mVersion == null) {

@@ -512,8 +664,8 @@
         int pos = s.indexOf("|r:");         //$NON-NLS-1$

         assert pos > 0;

         s = s.substring(0, pos) +

-            "|ve:" + getVendor() +          //$NON-NLS-1$

-            "|na:" + getName() +            //$NON-NLS-1$

+            "|vid:" + getVendorId() +          //$NON-NLS-1$

+            "|nid:" + getNameId() +            //$NON-NLS-1$

             s.substring(pos);

         return s;

     }

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
index 4fa7c38..617ba42 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
@@ -354,6 +354,13 @@
      */

     @Override

     public String getLongDescription() {

+        return String.format("%1$s\n%2$s\n%3$s",

+                getShortDescription(),

+                getSizeDescription(),

+                getSha1Description());

+    }

+

+    public String getSizeDescription() {

         long size = getSize();

         String sizeStr;

         if (size < 1024) {

@@ -368,8 +375,11 @@
                     Math.round(10.0 * size / (1024 * 1024 * 1024.0))/ 10.0);

         }

 

-        return String.format("%1$s\nSize: %2$s\nSHA1: %3$s",

-                getShortDescription(), sizeStr, getChecksum());

+        return String.format("Size: %1$s", sizeStr);

+    }

+

+    public String getSha1Description() {

+        return String.format("SHA1: %1$s", getChecksum());

     }

 

     /**

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
index 9046e63..9e50430 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
@@ -23,6 +23,8 @@
 import com.android.sdklib.io.FileOp;

 import com.android.sdklib.io.IFileOp;

 import com.android.sdklib.repository.RepoConstants;

+import com.android.sdklib.util.GrabProcessOutput;

+import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;

 

 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;

 import org.apache.commons.compress.archivers.zip.ZipFile;

@@ -37,10 +39,13 @@
 import java.io.OutputStream;

 import java.security.MessageDigest;

 import java.security.NoSuchAlgorithmException;

+import java.util.Arrays;

 import java.util.Enumeration;

 import java.util.HashSet;

 import java.util.Properties;

 import java.util.Set;

+import java.util.TreeSet;

+import java.util.regex.Pattern;

 

 /**

  * Performs the work of installing a given {@link Archive}.

@@ -98,13 +103,6 @@
         File archiveFile = null;

         String name = pkg.getShortDescription();

 

-        if (pkg instanceof ExtraPackage && !((ExtraPackage) pkg).isPathValid()) {

-            monitor.log("Skipping %1$s: %2$s is not a valid install path.",

-                    name,

-                    ((ExtraPackage) pkg).getPath());

-            return false;

-        }

-

         if (newArchive.isLocal()) {

             // This should never happen.

             monitor.log("Skipping already installed archive: %1$s for %2$s",

@@ -503,18 +501,11 @@
                                 destFolder.getPath(), oldDestFolder.getPath());

 

                         if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) {

-                            String msg = String.format(

-                                    "-= Warning ! =-\n" +

-                                    "A folder failed to be moved. On Windows this " +

-                                    "typically means that a program is using that folder (for " +

-                                    "example Windows Explorer or your anti-virus software.)\n" +

-                                    "Please momentarily deactivate your anti-virus software or " +

-                                    "close any running programs that may be accessing the " +

-                                    "directory '%1$s'.\n" +

-                                    "When ready, press YES to try again.",

-                                    destFolder.getPath());

+                            boolean tryAgain = true;

 

-                            if (monitor.displayPrompt("SDK Manager: failed to install", msg)) {

+                            tryAgain = windowsDestDirLocked(osSdkRoot, destFolder, monitor);

+

+                            if (tryAgain) {

                                 // loop, trying to rename the temp dir into the destination

                                 continue;

                             } else {

@@ -586,6 +577,93 @@
         }

     }

 

+    private boolean windowsDestDirLocked(

+            String osSdkRoot,

+            File destFolder,

+            final ITaskMonitor monitor) {

+        String msg = null;

+

+        assert SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS;

+

+        File findLockExe = FileOp.append(

+                osSdkRoot, SdkConstants.FD_TOOLS, SdkConstants.FD_LIB, SdkConstants.FN_FIND_LOCK);

+

+        if (mFileOp.exists(findLockExe)) {

+            try {

+                final StringBuilder result = new StringBuilder();

+                String command[] = new String[] {

+                        findLockExe.getAbsolutePath(),

+                        destFolder.getAbsolutePath()

+                };

+                Process process = Runtime.getRuntime().exec(command);

+                int retCode = GrabProcessOutput.grabProcessOutput(

+                        process,

+                        true /*waitForReaders*/,

+                        new IProcessOutput() {

+                            @Override

+                            public void out(String line) {

+                                if (line != null) {

+                                    result.append(line).append("\n");

+                                }

+                            }

+

+                            @Override

+                            public void err(String line) {

+                                if (line != null) {

+                                    monitor.logError("[find_lock] Error: %1$s", line);

+                                }

+                            }

+                        });

+

+                if (retCode == 0 && result.length() > 0) {

+                    // TODO create a better dialog

+

+                    String found = result.toString().trim();

+                    monitor.logError("[find_lock] Directory locked by %1$s", found);

+

+                    TreeSet<String> apps = new TreeSet<String>(Arrays.asList(

+                            found.split(Pattern.quote(";"))));  //$NON-NLS-1$

+                    StringBuilder appStr = new StringBuilder();

+                    for (String app : apps) {

+                        appStr.append("\n  - ").append(app.trim());                //$NON-NLS-1$

+                    }

+

+                    msg = String.format(

+                            "-= Warning ! =-\n" +

+                            "The following processes: %1$s\n" +

+                            "are locking the following directory: \n" +

+                            "  %2$s\n" +

+                            "Please close these applications so that the installation can continue.\n" +

+                            "When ready, press YES to try again.",

+                            appStr.toString(),

+                            destFolder.getPath());

+                }

+

+            } catch (Exception e) {

+                monitor.error(e, "[find_lock failed]");

+            }

+

+

+        }

+

+        if (msg == null) {

+            // Old way: simply display a generic text and let user figure it out.

+            msg = String.format(

+                "-= Warning ! =-\n" +

+                "A folder failed to be moved. On Windows this " +

+                "typically means that a program is using that folder (for " +

+                "example Windows Explorer or your anti-virus software.)\n" +

+                "Please momentarily deactivate your anti-virus software or " +

+                "close any running programs that may be accessing the " +

+                "directory '%1$s'.\n" +

+                "When ready, press YES to try again.",

+                destFolder.getPath());

+        }

+

+        boolean tryAgain = monitor.displayPrompt("SDK Manager: failed to install", msg);

+        return tryAgain;

+    }

+

     /**

      * Tries to rename/move a folder.

      * <p/>

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
index 4bcaa6d..a242c26 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
@@ -16,6 +16,7 @@
 

 package com.android.sdklib.internal.repository;

 

+import com.android.annotations.Nullable;

 import com.android.annotations.VisibleForTesting;

 import com.android.annotations.VisibleForTesting.Visibility;

 import com.android.sdklib.NullSdkLog;

@@ -42,18 +43,22 @@
     implements IMinApiLevelDependency {

 

     /**

-     * The vendor folder name. It must be a non-empty single-segment path.

-     * <p/>

-     * The paths "add-ons", "platforms", "platform-tools", "tools" and "docs" are reserved and

-     * cannot be used.

-     * This limitation cannot be written in the XML Schema and must be enforced here by using

-     * the method {@link #isPathValid()} *before* installing the package.

+     * The extra display name. Used in the UI to represent the package. It can be anything.

      */

-    private final String mVendor;

+    private final String mDisplayName;

 

     /**

-     * The sub-folder name. It must be a non-empty single-segment path and has the same

-     * rules as {@link #mVendor}.

+     * The vendor id name. It is a simple alphanumeric string [a-zA-Z0-9_-].

+     */

+    private final String mVendorId;

+

+    /**

+     * The vendor display name. Used in the UI to represent the vendor. It can be anything.

+     */

+    private final String mVendorDisplay;

+

+    /**

+     * The sub-folder name. It must be a non-empty single-segment path.

      */

     private final String mPath;

 

@@ -85,14 +90,49 @@
      *          parameters that vary according to the originating XML schema.

      * @param licenses The licenses loaded from the XML originating document.

      */

-    ExtraPackage(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) {

+    ExtraPackage(

+            SdkSource source,

+            Node packageNode,

+            String nsUri,

+            Map<String,String> licenses) {

         super(source, packageNode, nsUri, licenses);

 

         mPath   = XmlParserUtils.getXmlString(packageNode, RepoConstants.NODE_PATH);

-        mVendor = XmlParserUtils.getXmlString(packageNode, RepoConstants.NODE_VENDOR);

 

-        mMinApiLevel = XmlParserUtils.getXmlInt(packageNode, RepoConstants.NODE_MIN_API_LEVEL,

-                MIN_API_LEVEL_NOT_SPECIFIED);

+        // Read name-display, vendor-display and vendor-id, introduced in addon-4.xsd.

+        // These are not optional, they are mandatory in addon-4 but we still treat them

+        // as optional so that we can fallback on using <vendor> which was the only one

+        // defined in addon-3.xsd.

+        String name  = XmlParserUtils.getXmlString(packageNode, RepoConstants.NODE_NAME_DISPLAY);

+        String vname = XmlParserUtils.getXmlString(packageNode, RepoConstants.NODE_VENDOR_DISPLAY);

+        String vid   = XmlParserUtils.getXmlString(packageNode, RepoConstants.NODE_VENDOR_ID);

+

+        if (vid.length() == 0) {

+            // If vid is missing, use the old <vendor> attribute.

+            // Note that in a valid XML, vendor-id cannot be an empty string.

+            // The only reason vid can be empty is when <vendor-id> is missing, which

+            // happens in an addon-3 schema, in which case the old <vendor> needs to be used.

+            String vendor = XmlParserUtils.getXmlString(packageNode, RepoConstants.NODE_VENDOR);

+            vid = sanitizeLegacyVendor(vendor);

+            if (vname.length() == 0) {

+                vname = vendor;

+            }

+        }

+        if (vname.length() == 0) {

+            // The vendor-display name can be empty, in which case we use the vendor-id.

+            vname = vid;

+        }

+        mVendorDisplay = vname.trim();

+        mVendorId      = vid.trim();

+

+        if (name.length() == 0) {

+            // If name is missing, use the <path> attribute as done in an addon-3 schema.

+            name = getPrettyName();

+        }

+        mDisplayName   = name.trim();

+

+        mMinApiLevel = XmlParserUtils.getXmlInt(

+                packageNode, RepoConstants.NODE_MIN_API_LEVEL, MIN_API_LEVEL_NOT_SPECIFIED);

 

         mProjectFiles = parseProjectFiles(

                 XmlParserUtils.getFirstChild(packageNode, RepoConstants.NODE_PROJECT_FILES));

@@ -146,30 +186,18 @@
             String archiveOsPath) {

         ExtraPackage ep = new ExtraPackage(source, props, vendor, path, revision, license,

                 description, descUrl, archiveOs, archiveArch, archiveOsPath);

-

-        if (ep.isPathValid()) {

-            return ep;

-        } else {

-            String shortDesc = ep.getShortDescription() + " [*]";  //$NON-NLS-1$

-

-            String longDesc = String.format(

-                    "Broken Extra Package: %1$s\n" +

-                    "[*] Package cannot be used due to error: Invalid install path %2$s",

-                    description,

-                    ep.getPath());

-

-            BrokenPackage ba = new BrokenPackage(props, shortDesc, longDesc,

-                    ep.getMinApiLevel(),

-                    IExactApiLevelDependency.API_LEVEL_INVALID,

-                    archiveOsPath);

-            return ba;

-        }

+        return ep;

     }

 

+    /**

+     * Constructor used to create a mock {@link ExtraPackage}.

+     * Most of the attributes here are optional.

+     * When not defined, they will be extracted from the {@code props} properties.

+     */

     @VisibleForTesting(visibility=Visibility.PRIVATE)

     protected ExtraPackage(SdkSource source,

             Properties props,

-            String vendor,

+            String vendorId,

             String path,

             int revision,

             String license,

@@ -188,15 +216,36 @@
                 archiveArch,

                 archiveOsPath);

 

-        // The vendor argument is not supposed to be empty. However this attribute did not

-        // exist prior to schema repo-v3 and tools r8, which means we need to cope with a

-        // lack of it when reading back old local repositories. In this case we allow an

-        // empty string.

-        mVendor = vendor != null ? vendor : getProperty(props, PkgProps.EXTRA_VENDOR, "");

-

         // The path argument comes before whatever could be in the properties

         mPath   = path != null ? path : getProperty(props, PkgProps.EXTRA_PATH, path);

 

+        String name  = getProperty(props, PkgProps.EXTRA_NAME_DISPLAY, "");     //$NON-NLS-1$

+        String vname = getProperty(props, PkgProps.EXTRA_VENDOR_DISPLAY, "");   //$NON-NLS-1$

+        String vid   = vendorId != null ? vendorId :

+                              getProperty(props, PkgProps.EXTRA_VENDOR_ID, ""); //$NON-NLS-1$

+

+        if (vid.length() == 0) {

+            // If vid is missing, use the old <vendor> attribute.

+            // <vendor> did not exist prior to schema repo-v3 and tools r8.

+            String vendor = getProperty(props, PkgProps.EXTRA_VENDOR, "");      //$NON-NLS-1$

+            vid = sanitizeLegacyVendor(vendor);

+            if (vname.length() == 0) {

+                vname = vendor;

+            }

+        }

+        if (vname.length() == 0) {

+            // The vendor-display name can be empty, in which case we use the vendor-id.

+            vname = vid;

+        }

+        mVendorDisplay = vname.trim();

+        mVendorId      = vid.trim();

+

+        if (name.length() == 0) {

+            // If name is missing, use the <path> attribute as done in an addon-3 schema.

+            name = getPrettyName();

+        }

+        mDisplayName   = name.trim();

+

         mOldPaths = getProperty(props, PkgProps.EXTRA_OLD_PATHS, null);

 

         mMinApiLevel = Integer.parseInt(

@@ -226,9 +275,9 @@
         super.saveProperties(props);

 

         props.setProperty(PkgProps.EXTRA_PATH, mPath);

-        if (mVendor != null) {

-            props.setProperty(PkgProps.EXTRA_VENDOR, mVendor);

-        }

+        props.setProperty(PkgProps.EXTRA_NAME_DISPLAY, mDisplayName);

+        props.setProperty(PkgProps.EXTRA_VENDOR_DISPLAY, mVendorDisplay);

+        props.setProperty(PkgProps.EXTRA_VENDOR_ID, mVendorId);

 

         if (getMinApiLevel() != MIN_API_LEVEL_NOT_SPECIFIED) {

             props.setProperty(PkgProps.EXTRA_MIN_API_LEVEL, Integer.toString(getMinApiLevel()));

@@ -296,32 +345,9 @@
     }

 

     /**

-     * Static helper to check if a given vendor and path is acceptable for an "extra" package.

-     */

-    public boolean isPathValid() {

-        return isSegmentValid(mVendor) && isSegmentValid(mPath);

-    }

-

-    private boolean isSegmentValid(String segment) {

-        if (SdkConstants.FD_ADDONS.equals(segment) ||

-                SdkConstants.FD_PLATFORMS.equals(segment) ||

-                SdkConstants.FD_PLATFORM_TOOLS.equals(segment) ||

-                SdkConstants.FD_TOOLS.equals(segment) ||

-                SdkConstants.FD_DOCS.equals(segment) ||

-                RepoConstants.FD_TEMP.equals(segment)) {

-            return false;

-        }

-        return segment != null && segment.indexOf('/') == -1 && segment.indexOf('\\') == -1;

-    }

-

-    /**

      * Returns the sanitized path folder name. It is a single-segment path.

      * <p/>

      * The package is installed in SDK/extras/vendor_name/path_name.

-     * <p/>

-     * The paths "add-ons", "platforms", "tools" and "docs" are reserved and cannot be used.

-     * This limitation cannot be written in the XML Schema and must be enforced here by using

-     * the method {@link #isPathValid()} *before* installing the package.

      */

     public String getPath() {

         // The XSD specifies the XML vendor and path should only contain [a-zA-Z0-9]+

@@ -339,20 +365,28 @@
     }

 

     /**

-     * Returns the sanitized vendor folder name. It is a single-segment path.

-     * <p/>

-     * The package is installed in SDK/extras/vendor_name/path_name.

-     * <p/>

-     * An empty string is returned in case of error.

+     * Returns the vendor id.

      */

-    public String getVendor() {

+    public String getVendorId() {

+        return mVendorId;

+    }

 

+    public String getVendorDisplay() {

+        return mVendorDisplay;

+    }

+

+    public String getDisplayName() {

+        return mDisplayName;

+    }

+

+    /** Transforms the legacy vendor name into a usable vendor id. */

+    private String sanitizeLegacyVendor(String vendorDisplay) {

         // The XSD specifies the XML vendor and path should only contain [a-zA-Z0-9]+

         // and cannot be empty. Let's be defensive and enforce that anyway since things

         // like "____" are still valid values that we don't want to allow.

 

-        if (mVendor != null && mVendor.length() > 0) {

-            String vendor = mVendor;

+        if (vendorDisplay != null && vendorDisplay.length() > 0) {

+            String vendor = vendorDisplay.trim();

             // Sanitize the vendor

             vendor = vendor.replaceAll("[^a-zA-Z0-9-]+", "_");      //$NON-NLS-1$

             if (vendor.equals("_")) {                               //$NON-NLS-1$

@@ -364,17 +398,22 @@
         }

 

         return ""; //$NON-NLS-1$

+

     }

 

+    /**

+     * Used to produce a suitable name-display based on the current {@link #mPath}

+     * and {@link #mVendorDisplay} in addon-3 schemas.

+     */

     private String getPrettyName() {

         String name = mPath;

 

         // In the past, we used to save the extras in a folder vendor-path,

         // and that "vendor" would end up in the path when we reload the extra from

         // disk. Detect this and compensate.

-        if (mVendor != null && mVendor.length() > 0) {

-            if (name.startsWith(mVendor + "-")) {  //$NON-NLS-1$

-                name = name.substring(mVendor.length() + 1);

+        if (mVendorDisplay != null && mVendorDisplay.length() > 0) {

+            if (name.startsWith(mVendorDisplay + "-")) {  //$NON-NLS-1$

+                name = name.substring(mVendorDisplay.length() + 1);

             }

         }

 

@@ -383,11 +422,11 @@
             name = name.replaceAll("[ _\t\f-]+", " ").trim();   //$NON-NLS-1$ //$NON-NLS-2$

         }

         if (name == null || name.length() == 0) {

-            name = "Unkown Extra";

+            name = "Unknown Extra";

         }

 

-        if (mVendor != null && mVendor.length() > 0) {

-            name = mVendor + " " + name;  //$NON-NLS-1$

+        if (mVendorDisplay != null && mVendorDisplay.length() > 0) {

+            name = mVendorDisplay + " " + name;  //$NON-NLS-1$

             name = name.replaceAll("[ _\t\f-]+", " ").trim();   //$NON-NLS-1$ //$NON-NLS-2$

         }

 

@@ -422,7 +461,7 @@
     @Override

     public String installId() {

         return String.format("extra-%1$s-%2$s",     //$NON-NLS-1$

-                getVendor(),

+                getVendorId(),

                 getPath());

     }

 

@@ -433,8 +472,8 @@
      */

     @Override

     public String getListDescription() {

-        String s = String.format("%1$s package%2$s",

-                getPrettyName(),

+        String s = String.format("%1$s%2$s",

+                getDisplayName(),

                 isObsolete() ? " (Obsolete)" : "");  //$NON-NLS-2$

 

         return s;

@@ -445,9 +484,8 @@
      */

     @Override

     public String getShortDescription() {

-

-        String s = String.format("%1$s package, revision %2$d%3$s",

-                getPrettyName(),

+        String s = String.format("%1$s, revision %2$d%3$s",

+                getDisplayName(),

                 getRevision(),

                 isObsolete() ? " (Obsolete)" : "");  //$NON-NLS-2$

 

@@ -462,15 +500,15 @@
      */

     @Override

     public String getLongDescription() {

-        String s = getDescription();

-        if (s == null || s.length() == 0) {

-            s = String.format("Extra %1$s package by %2$s", getPath(), getVendor());

-        }

+        String s = String.format("%1$s, revision %2$d%3$s\nBy %4$s",

+                getDisplayName(),

+                getRevision(),

+                isObsolete() ? " (Obsolete)" : "",  //$NON-NLS-2$

+                getVendorDisplay());

 

-        if (s.indexOf("revision") == -1) {

-            s += String.format("\nRevision %1$d%2$s",

-                    getRevision(),

-                    isObsolete() ? " (Obsolete)" : "");  //$NON-NLS-2$

+        String d = getDescription();

+        if (d != null && d.length() > 0) {

+            s += '\n' + d;

         }

 

         if (getMinToolsRevision() != MIN_TOOLS_REV_NOT_SPECIFIED) {

@@ -481,11 +519,15 @@
             s += String.format("\nRequires SDK Platform Android API %1$s", getMinApiLevel());

         }

 

-        // For a local archive, also put the install path in the long description.

-        // This should help users locate the extra on their drive.

         File localPath = getLocalArchivePath();

         if (localPath != null) {

+            // For a local archive, also put the install path in the long description.

+            // This should help users locate the extra on their drive.

             s += String.format("\nLocation: %1$s", localPath.getAbsolutePath());

+        } else {

+            // For a non-installed archive, indicate where it would be installed.

+            s += String.format("\nInstall path: %1$s",

+                    getInstallSubFolder(null/*sdk root*/).getPath());

         }

 

         return s;

@@ -497,7 +539,7 @@
      * <p/>

      * A "tool" package should always be located in SDK/tools.

      *

-     * @param osSdkRoot The OS path of the SDK root folder.

+     * @param osSdkRoot The OS path of the SDK root folder. Must NOT be null.

      * @param sdkManager An existing SDK manager to list current platforms and addons.

      *                   Not used in this implementation.

      * @return A new {@link File} corresponding to the directory to use to install this package.

@@ -521,10 +563,22 @@
             }

         }

 

+        return getInstallSubFolder(osSdkRoot);

+    }

+

+    /**

+     * Computes the "sub-folder" install path, relative to the given SDK root.

+     * For an extra package, this is generally ".../extra/vendor-id/path".

+     *

+     * @param osSdkRoot The OS path of the SDK root folder if known.

+     *   This CAN be null, in which case the path will start at /extra.

+     * @return Either /extra/vendor/path or sdk-root/extra/vendor-id/path.

+     */

+    private File getInstallSubFolder(@Nullable String osSdkRoot) {

         // The /extras dir at the root of the SDK

         File path = new File(osSdkRoot, SdkConstants.FD_EXTRAS);

 

-        String vendor = getVendor();

+        String vendor = getVendorId();

         if (vendor != null && vendor.length() > 0) {

             path = new File(path, vendor);

         }

@@ -547,8 +601,8 @@
             int lenEpOldPaths = epOldPaths.length;

             for (int indexEp = -1; indexEp < lenEpOldPaths; indexEp++) {

                 if (sameVendorAndPath(

-                        mVendor,    mPath,

-                        ep.mVendor, indexEp   < 0 ? ep.mPath : epOldPaths[indexEp])) {

+                        mVendorId,    mPath,

+                        ep.mVendorId, indexEp   < 0 ? ep.mPath : epOldPaths[indexEp])) {

                     return true;

                 }

             }

@@ -557,8 +611,8 @@
             int lenThisOldPaths = thisOldPaths.length;

             for (int indexThis = -1; indexThis < lenThisOldPaths; indexThis++) {

                 if (sameVendorAndPath(

-                        mVendor,    indexThis < 0 ? mPath    : thisOldPaths[indexThis],

-                        ep.mVendor, ep.mPath)) {

+                        mVendorId,    indexThis < 0 ? mPath    : thisOldPaths[indexThis],

+                        ep.mVendorId, ep.mPath)) {

                     return true;

                 }

             }

@@ -617,7 +671,7 @@
         int pos = s.indexOf("|r:");         //$NON-NLS-1$

         assert pos > 0;

         s = s.substring(0, pos) +

-            "|ve:" + getVendor() +          //$NON-NLS-1$

+            "|ve:" + getVendorId() +          //$NON-NLS-1$

             "|pa:" + getPath() +            //$NON-NLS-1$

             s.substring(pos);

         return s;

@@ -648,7 +702,7 @@
         result = prime * result + mMinApiLevel;

         result = prime * result + ((mPath == null) ? 0 : mPath.hashCode());

         result = prime * result + Arrays.hashCode(mProjectFiles);

-        result = prime * result + ((mVendor == null) ? 0 : mVendor.hashCode());

+        result = prime * result + ((mVendorDisplay == null) ? 0 : mVendorDisplay.hashCode());

         return result;

     }

 

@@ -677,11 +731,11 @@
         if (!Arrays.equals(mProjectFiles, other.mProjectFiles)) {

             return false;

         }

-        if (mVendor == null) {

-            if (other.mVendor != null) {

+        if (mVendorDisplay == null) {

+            if (other.mVendorDisplay != null) {

                 return false;

             }

-        } else if (!mVendor.equals(other.mVendor)) {

+        } else if (!mVendorDisplay.equals(other.mVendorDisplay)) {

             return false;

         }

         return true;

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
index 2e65388..13be219 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
@@ -235,7 +235,7 @@
                                 dir.getName(),              //path

                                 0,                          //revision

                                 null,                       //license

-                                "Tools",                    //description

+                                null,                       //description

                                 null,                       //descUrl

                                 Os.getCurrentOs(),          //archiveOs

                                 Arch.getCurrentArch(),      //archiveArch

@@ -308,11 +308,16 @@
             if (dir.isDirectory() && !visited.contains(dir)) {

                 Pair<Map<String, String>, String> infos =

                     SdkManager.parseAddonProperties(dir, sdkManager.getTargets(), log);

+                Properties sourceProps =

+                    parseProperties(new File(dir, SdkConstants.FN_SOURCE_PROP));

 

-                Map<String, String> props = infos.getFirst();

+                Map<String, String> addonProps = infos.getFirst();

                 String error = infos.getSecond();

                 try {

-                    Package pkg = AddonPackage.createBroken(dir.getAbsolutePath(), props, error);

+                    Package pkg = AddonPackage.createBroken(dir.getAbsolutePath(),

+                                                            sourceProps,

+                                                            addonProps,

+                                                            error);

                     packages.add(pkg);

                     visited.add(dir);

                 } catch (Exception e) {

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
index b09e2ee..a86171c 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
@@ -142,8 +142,8 @@
         mLicense     = getProperty(props, PkgProps.PKG_LICENSE,      license);

         mDescription = getProperty(props, PkgProps.PKG_DESC,         description);

         mDescUrl     = getProperty(props, PkgProps.PKG_DESC_URL,     descUrl);

-        mReleaseNote = getProperty(props, PkgProps.PKG_RELEASE_NOTE, "");

-        mReleaseUrl  = getProperty(props, PkgProps.PKG_RELEASE_URL,  "");

+        mReleaseNote = getProperty(props, PkgProps.PKG_RELEASE_NOTE, "");       //$NON-NLS-1$

+        mReleaseUrl  = getProperty(props, PkgProps.PKG_RELEASE_URL,  "");       //$NON-NLS-1$

         mObsolete    = getProperty(props, PkgProps.PKG_OBSOLETE,     null);

 

         // If source is null and we can find a source URL in the properties, generate

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkRepoSource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkRepoSource.java
index d35e365..00392de 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkRepoSource.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkRepoSource.java
@@ -63,12 +63,25 @@
         return false;

     }

 

+    private static String[] sDefaults = null; // lazily allocated in getDefaultXmlFileUrls

+

     @Override

     protected String[] getDefaultXmlFileUrls() {

-        return new String[] {

-                SdkRepoConstants.URL_DEFAULT_FILENAME2,

-                SdkRepoConstants.URL_DEFAULT_FILENAME

-        };

+        if (sDefaults == null) {

+            sDefaults = new String[SdkRepoConstants.NS_LATEST_VERSION

+                                   - SdkRepoConstants.NS_SERVER_MIN_VERSION

+                                   + 2];

+            int k = 0;

+            for (int i  = SdkRepoConstants.NS_LATEST_VERSION;

+                     i >= SdkRepoConstants.NS_SERVER_MIN_VERSION;

+                     i--) {

+                sDefaults[k++] = String.format(SdkRepoConstants.URL_FILENAME_PATTERN, i);

+            }

+            sDefaults[k++] = SdkRepoConstants.URL_DEFAULT_FILENAME;

+            assert k == sDefaults.length;

+        }

+

+        return sDefaults;

     }

 

     @Override

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java
index f757bfb..7933602 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java
@@ -826,7 +826,7 @@
                         if (SdkAddonConstants.NODE_ADD_ON.equals(name)) {

                             p = new AddonPackage(this, child, nsUri, licenses);

 

-                        } else if (RepoConstants.NODE_EXTRA.equals(name)) {

+                        } else if (SdkAddonConstants.NODE_EXTRA.equals(name)) {

                             p = new ExtraPackage(this, child, nsUri, licenses);

 

                         } else if (!isAddonSource()) {

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/-sdk-repository-6.xsd b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/-sdk-repository-6.xsd
new file mode 100755
index 0000000..149f8c5
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/-sdk-repository-6.xsd
@@ -0,0 +1,557 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<xsd:schema
+    targetNamespace="http://schemas.android.com/sdk/android/repository/6"
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+    xmlns:sdk="http://schemas.android.com/sdk/android/repository/6"
+    elementFormDefault="qualified"
+    attributeFormDefault="unqualified"
+    version="1">
+
+    <!-- The repository contains a collection of downloadable items known as
+         "packages". Each package has a type and various attributes and contains
+         a list of file "archives" that can be downloaded for specific OSes.
+
+         An Android SDK repository is a web site that contains a "repository.xml"
+         file that conforms to this XML Schema.
+
+         History:
+         - v1 is used by the SDK Updater in Tools r3 and r4.
+
+         - v2 is used by the SDK Updater in Tools r5:
+            - It introduces a new <sample> repository type. Previously samples
+              were included in the <platform> packages. Instead this package is used
+              and and the samples are installed in $SDK/samples.
+            - All repository types have a new <obsolete> node. It works as a marker
+              to indicate the package is obsolete and should not be selected by default.
+              The UI also hides these out by default.
+
+         - v3 is used by the SDK Updater in Tools r8:
+            - It introduces a new <platform-tool> repository type. Previously platform-specific
+              tools were included in the <platform> packages. Instead this package is used
+              and platform-specific tools are installed in $SDK/platform-tools
+            - There's a new element <min-platform-tools-rev> in <tool>. The tool package now
+              requires that at least some minimal version of <platform-tool> be installed.
+            - It removes the <addon> repository type, which is now in its own XML Schema.
+
+         - v4 is used by the SDK Updater in Tools r12:
+            - <extra> element now has a <project-files> element that contains 1 or
+              or more <path>, each indicating the relative path of a file that this package
+              can contribute to installed projects.
+            - <platform> element now has a mandatory <layoutlib> that indicates the API
+              and revision of that layout library for this particular platform.
+
+         - v5 is used by the SDK Updater in Tools R14:
+            - <extra> now has an <old-paths> element, a ;-separated list of old paths that
+              should be detected and migrated to the new <path> for that package.
+            - <platform> has a new optional <abi-included> that describes the ABI of the
+              system image included in the platform, if any.
+            - New <system-image> package type, to store system images outside of <platform>s.
+            - New <source> package type.
+
+         - v6 removed 'extra' packages. They are served only by the addon XML.
+    -->
+
+    <xsd:element name="sdk-repository" type="sdk:repositoryType" />
+
+    <xsd:complexType name="repositoryType">
+        <xsd:annotation>
+            <xsd:documentation>
+                The repository contains a collection of downloadable packages.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:choice minOccurs="0" maxOccurs="unbounded">
+            <xsd:element name="platform"        type="sdk:platformType"     />
+            <xsd:element name="system-image"    type="sdk:systemImageType"  />
+            <xsd:element name="source"          type="sdk:sourceType"       />
+            <xsd:element name="tool"            type="sdk:toolType"         />
+            <xsd:element name="platform-tool"   type="sdk:platformToolType" />
+            <xsd:element name="doc"             type="sdk:docType"          />
+            <xsd:element name="sample"          type="sdk:sampleType"       />
+            <xsd:element name="license"         type="sdk:licenseType"      />
+        </xsd:choice>
+    </xsd:complexType>
+
+    <!-- The definition of an SDK platform package. -->
+
+    <xsd:complexType name="platformType">
+        <xsd:annotation>
+            <xsd:documentation>An SDK platform package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The Android platform version. It is string such as "1.0". -->
+            <xsd:element name="version"   type="xsd:normalizedString" />
+            <!-- The Android API Level for the platform. An int > 0. -->
+            <xsd:element name="api-level" type="xsd:positiveInteger"  />
+            <!-- The optional codename for this platform, if it's a preview. -->
+            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"  type="xsd:positiveInteger" />
+
+            <!-- Information on the layoutlib packaged in this platform. -->
+            <xsd:element name="layoutlib" type="sdk:layoutlibType" />
+
+            <!-- optional elements -->
+
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license"  type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"   type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"      type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"   type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"      type="sdk:archivesType" />
+            <!-- The minimal revision of tools required by this package.
+                 Optional. If present, must be an int > 0. -->
+            <xsd:element name="min-tools-rev" type="xsd:positiveInteger" minOccurs="0" />
+
+            <!-- The ABI of the system image *included* in this platform, if any.
+                 When the field is present, it means the platform already embeds one
+                 system image. A platform can also have any number of external
+                 &lt;system-image&gt; associated with it.  -->
+            <xsd:element name="included-abi"  type="sdk:abiType" minOccurs="0" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"      type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of a layout library used by a platform. -->
+
+    <xsd:complexType name="layoutlibType" >
+        <xsd:annotation>
+            <xsd:documentation>
+                Version information for a layoutlib included in a platform.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The layoutlib API level, an int > 0,
+                 incremented with each new incompatible lib. -->
+            <xsd:element name="api"          type="xsd:positiveInteger" />
+            <!-- The incremental minor revision for that API, e.g. in case of bug fixes.
+                 Optional. An int >= 0, assumed to be 0 if the element is missing. -->
+            <xsd:element name="revision"     type="xsd:nonNegativeInteger" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of a system image used by a platform. -->
+
+    <xsd:complexType name="systemImageType" >
+        <xsd:annotation>
+            <xsd:documentation>
+                System Image for a platform.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- api-level + codename identifies the platform to which this system image belongs. -->
+
+            <!-- The Android API Level for the platform. An int > 0. -->
+            <xsd:element name="api-level" type="xsd:positiveInteger"  />
+            <!-- The optional codename for this platform, if it's a preview. -->
+            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
+
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"  type="xsd:positiveInteger" />
+
+            <!-- The ABI of the system emulated by this image. -->
+            <xsd:element name="abi"       type="sdk:abiType" />
+
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license"  type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"   type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"      type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"   type="xsd:token"       minOccurs="0" />
+
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"  type="sdk:archivesType" />
+        </xsd:all>
+    </xsd:complexType>
+
+    <!-- The definition of the ABI supported by a platform's system image. -->
+
+    <xsd:simpleType name="abiType">
+        <xsd:annotation>
+            <xsd:documentation>The ABI of a platform's system image.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:token">
+            <xsd:enumeration value="armeabi"     />
+            <xsd:enumeration value="armeabi-v7a" />
+            <xsd:enumeration value="x86"         />
+        </xsd:restriction>
+    </xsd:simpleType>
+
+
+    <!-- The definition of a source package. -->
+
+    <xsd:complexType name="sourceType" >
+        <xsd:annotation>
+            <xsd:documentation>
+                Sources for a platform.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- api-level + codename identifies the platform to which this source belongs. -->
+
+            <!-- The Android API Level for the platform. An int > 0. -->
+            <xsd:element name="api-level" type="xsd:positiveInteger"  />
+            <!-- The optional codename for this platform, if it's a preview. -->
+            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
+
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"  type="xsd:positiveInteger" />
+
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license"  type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"   type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"      type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"   type="xsd:token"       minOccurs="0" />
+
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"  type="sdk:archivesType" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of an SDK tool package. -->
+
+    <xsd:complexType name="toolType" >
+        <xsd:annotation>
+            <xsd:documentation>An SDK tool package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"     type="xsd:positiveInteger" />
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sdk:archivesType" />
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
+
+            <!-- The minimal revision of platform-tools required by this package.
+                 Mandatory. Must be an int > 0. -->
+            <xsd:element name="min-platform-tools-rev" type="xsd:positiveInteger" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of an SDK platform-tool package. -->
+
+    <xsd:complexType name="platformToolType" >
+        <xsd:annotation>
+            <xsd:documentation>An SDK platform-tool package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"     type="xsd:positiveInteger" />
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sdk:archivesType" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of an SDK doc package. -->
+
+    <xsd:complexType name="docType" >
+        <xsd:annotation>
+            <xsd:documentation>An SDK doc package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The Android API Level for the documentation. An int > 0. -->
+            <xsd:element name="api-level" type="xsd:positiveInteger"  />
+            <!-- The optional codename for this doc, if it's a preview. -->
+            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
+
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"     type="xsd:positiveInteger" />
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sdk:archivesType" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of an SDK sample package. -->
+
+    <xsd:complexType name="sampleType" >
+        <xsd:annotation>
+            <xsd:documentation>An SDK sample package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The Android API Level for the documentation. An int > 0. -->
+            <xsd:element name="api-level" type="xsd:positiveInteger"  />
+            <!-- The optional codename for this doc, if it's a preview. -->
+            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
+
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"     type="xsd:positiveInteger" />
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sdk:archivesType" />
+            <!-- The minimal revision of tools required by this package.
+                 Optional. If present, must be an int > 0. -->
+            <xsd:element name="min-tools-rev" type="xsd:positiveInteger" minOccurs="0" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of a path segment used by the extra element. -->
+
+    <xsd:simpleType name="segmentType">
+        <xsd:annotation>
+            <xsd:documentation>
+                One path segment for the install path of an extra element.
+                It must be a single-segment path.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:token">
+            <xsd:pattern value="[a-zA-Z0-9_]+"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:simpleType name="segmentListType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A semi-colon separated list of a segmentTypes.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:token">
+            <xsd:pattern value="[a-zA-Z0-9_;]+"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+
+    <!-- The definition of a license to be referenced by the uses-license element. -->
+
+    <xsd:complexType name="licenseType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A license definition. Such a license must be used later as a reference
+                using a uses-license element in one of the package elements.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:simpleContent>
+            <xsd:extension base="xsd:string">
+                <xsd:attribute name="id"   type="xsd:ID" />
+                <xsd:attribute name="type" type="xsd:token" fixed="text" />
+            </xsd:extension>
+        </xsd:simpleContent>
+    </xsd:complexType>
+
+
+    <!-- Type describing the license used by a package.
+         The license MUST be defined using a license node and referenced
+         using the ref attribute of the license element inside a package.
+     -->
+
+    <xsd:complexType name="usesLicenseType">
+        <xsd:annotation>
+            <xsd:documentation>
+                Describes the license used by a package. The license MUST be defined
+                using a license node and referenced using the ref attribute of the
+                license element inside a package.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:attribute name="ref" type="xsd:IDREF" />
+    </xsd:complexType>
+
+
+    <!-- A collection of files that can be downloaded for a given architecture.
+         The <archives> node is mandatory in the repository elements and the
+         collection must have at least one <archive> declared.
+         Each archive is a zip file that will be unzipped in a location that depends
+         on its package type.
+     -->
+
+    <xsd:complexType name="archivesType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A collection of files that can be downloaded for a given architecture.
+                The &lt;archives&gt; node is mandatory in the repository packages and the
+                collection must have at least one &lt;archive&gt; declared.
+                Each archive is a zip file that will be unzipped in a location that depends
+                on its package type.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:sequence minOccurs="1" maxOccurs="unbounded">
+            <!-- One archive file -->
+            <xsd:element name="archive">
+                <xsd:complexType>
+                    <!-- Properties of the archive file -->
+                    <xsd:all>
+                        <!-- The size in bytes of the archive to download. -->
+                        <xsd:element name="size"     type="xsd:positiveInteger" />
+                        <!-- The checksum of the archive file. -->
+                        <xsd:element name="checksum" type="sdk:checksumType" />
+                        <!-- The URL is an absolute URL if it starts with http://, https://
+                             or ftp://. Otherwise it is relative to the parent directory that
+                             contains this repository.xml -->
+                        <xsd:element name="url"      type="xsd:token" />
+                    </xsd:all>
+
+                    <!-- Attributes that identify the OS and architecture -->
+                    <xsd:attribute name="os" use="required">
+                        <xsd:simpleType>
+                            <xsd:restriction base="xsd:token">
+                                <xsd:enumeration value="any" />
+                                <xsd:enumeration value="linux" />
+                                <xsd:enumeration value="macosx" />
+                                <xsd:enumeration value="windows" />
+                            </xsd:restriction>
+                        </xsd:simpleType>
+                    </xsd:attribute>
+                    <xsd:attribute name="arch" use="optional">
+                        <xsd:simpleType>
+                            <xsd:restriction base="xsd:token">
+                                <xsd:enumeration value="any" />
+                                <xsd:enumeration value="ppc" />
+                                <xsd:enumeration value="x86" />
+                                <xsd:enumeration value="x86_64" />
+                            </xsd:restriction>
+                        </xsd:simpleType>
+                    </xsd:attribute>
+                </xsd:complexType>
+            </xsd:element>
+        </xsd:sequence>
+    </xsd:complexType>
+
+
+    <!-- A collection of file paths available in an &lt;extra&gt; package
+         that can be installed in an Android project.
+         If present, the &lt;project-files&gt; collection must contain at least one path.
+         Each path is relative to the root directory of the package.
+     -->
+
+    <xsd:complexType name="projectFilesType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A collection of file paths available in an &lt;extra&gt; package
+                that can be installed in an Android project.
+                If present, the &lt;project-files&gt; collection must contain at least one path.
+                Each path is relative to the root directory of the package.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:sequence minOccurs="1" maxOccurs="unbounded">
+            <!-- One JAR Path, relative to the root folder of the package. -->
+            <xsd:element name="path" type="xsd:string" />
+        </xsd:sequence>
+    </xsd:complexType>
+
+
+    <!-- The definition of a file checksum -->
+
+    <xsd:simpleType name="sha1Number">
+        <xsd:annotation>
+            <xsd:documentation>A SHA1 checksum.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:string">
+            <xsd:pattern value="([0-9a-fA-F]){40}"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:complexType name="checksumType">
+        <xsd:annotation>
+            <xsd:documentation>A file checksum, currently only SHA1.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:simpleContent>
+            <xsd:extension base="sdk:sha1Number">
+                <xsd:attribute name="type" type="xsd:token" fixed="sha1" />
+            </xsd:extension>
+        </xsd:simpleContent>
+    </xsd:complexType>
+
+</xsd:schema>
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/PkgProps.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/PkgProps.java
index d16a35f..579656a 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/PkgProps.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/PkgProps.java
@@ -47,7 +47,12 @@
     // AddonPackage

 

     public static final String ADDON_NAME               = "Addon.Name";             //$NON-NLS-1$

+    public static final String ADDON_NAME_ID            = "Addon.NameId";           //$NON-NLS-1$

+    public static final String ADDON_NAME_DISPLAY       = "Addon.NameDisplay";      //$NON-NLS-1$

+

     public static final String ADDON_VENDOR             = "Addon.Vendor";           //$NON-NLS-1$

+    public static final String ADDON_VENDOR_ID          = "Addon.VendorId";         //$NON-NLS-1$

+    public static final String ADDON_VENDOR_DISPLAY     = "Addon.VendorDisplay";    //$NON-NLS-1$

 

     // DocPackage

 

@@ -55,9 +60,12 @@
 

     public static final String EXTRA_PATH               = "Extra.Path";             //$NON-NLS-1$

     public static final String EXTRA_OLD_PATHS          = "Extra.OldPaths";         //$NON-NLS-1$

-    public static final String EXTRA_VENDOR             = "Extra.Vendor";           //$NON-NLS-1$

     public static final String EXTRA_MIN_API_LEVEL      = "Extra.MinApiLevel";      //$NON-NLS-1$

     public static final String EXTRA_PROJECT_FILES      = "Extra.ProjectFiles";     //$NON-NLS-1$

+    public static final String EXTRA_VENDOR             = "Extra.Vendor";           //$NON-NLS-1$

+    public static final String EXTRA_VENDOR_ID          = "Extra.VendorId";         //$NON-NLS-1$

+    public static final String EXTRA_VENDOR_DISPLAY     = "Extra.VendorDisplay";    //$NON-NLS-1$

+    public static final String EXTRA_NAME_DISPLAY       = "Extra.NameDisplay";      //$NON-NLS-1$

 

     // ILayoutlibVersion

 

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/RepoConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/RepoConstants.java
index b7f8774..53db79f 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/RepoConstants.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/RepoConstants.java
@@ -25,9 +25,6 @@
  */

 public class RepoConstants {

 

-    /** An extra package. */

-    public static final String NODE_EXTRA         = "extra";                    //$NON-NLS-1$

-

     /** The license definition. */

     public static final String NODE_LICENSE       = "license";                  //$NON-NLS-1$

     /** The optional uses-license for all packages or for a lib. */

@@ -60,10 +57,20 @@
     public static final String NODE_API_LEVEL = "api-level";                    //$NON-NLS-1$

     /** The codename, a string, for platform packages. */

     public static final String NODE_CODENAME = "codename";                      //$NON-NLS-1$

-    /** The vendor, a string, for add-on and extra packages. */

+    /** The *old* vendor, a string, for add-on and extra packages.

+     *  Replaced by {@link #NODE_VENDOR_DISPLAY} and {@link #NODE_VENDOR_ID} in addon-v4.xsd. */

     public static final String NODE_VENDOR    = "vendor";                       //$NON-NLS-1$

-    /** The name, a string, for add-on packages or for libraries. */

+    /** The vendor display string, for add-on and extra packages. */

+    public static final String NODE_VENDOR_DISPLAY    = "vendor-display";       //$NON-NLS-1$

+    /** The unique vendor id string, for add-on and extra packages. */

+    public static final String NODE_VENDOR_ID    = "vendor-id";                 //$NON-NLS-1$

+    /** The name, a string, for add-on packages or for libraries.

+     *  Replaced by {@link #NODE_NAME_DISPLAY} and {@link #NODE_NAME_ID} in addon-v4.xsd. */

     public static final String NODE_NAME      = "name";                         //$NON-NLS-1$

+    /** The name display string, for add-on packages or for libraries. */

+    public static final String NODE_NAME_DISPLAY      = "name-display";         //$NON-NLS-1$

+    /** The unique name id string, for add-on packages or for libraries. */

+    public static final String NODE_NAME_ID      = "name-id";                   //$NON-NLS-1$

 

 

     /** A layoutlib package. */

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkAddonConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkAddonConstants.java
index 122d40a..41b184e 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkAddonConstants.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkAddonConstants.java
@@ -26,8 +26,10 @@
  */

 public class SdkAddonConstants extends RepoConstants {

 

-    /** The default name looked for by {@link SdkSource} when trying to load an

-     * sdk-addon XML if the URL doesn't match an existing resource. */

+    /**

+     * The default name looked for by {@link SdkSource} when trying to load an

+     * sdk-addon XML if the URL doesn't match an existing resource.

+     */

     public static final String URL_DEFAULT_FILENAME = "addon.xml";         //$NON-NLS-1$

 

     /** The base of our sdk-addon XML namespace. */

@@ -40,9 +42,11 @@
      */

     public static final String NS_PATTERN = NS_BASE + "([1-9][0-9]*)";     //$NON-NLS-1$

 

-    /** The latest version of the sdk-addon XML Schema.

-     *  Valid version numbers are between 1 and this number, included. */

-    public static final int NS_LATEST_VERSION = 3;

+    /**

+     * The latest version of the sdk-addon XML Schema.

+     * Valid version numbers are between 1 and this number, included.

+     */

+    public static final int NS_LATEST_VERSION = 4;

 

     /** The XML namespace of the latest sdk-addon XML. */

     public static final String NS_URI = getSchemaUri(NS_LATEST_VERSION);

@@ -53,6 +57,9 @@
     /** An add-on package. */

     public static final String NODE_ADD_ON          = "add-on";            //$NON-NLS-1$

 

+    /** An extra package. */

+    public static final String NODE_EXTRA           = "extra";             //$NON-NLS-1$

+

     /**

      * List of possible nodes in a repository XML. Used to populate options automatically

      * in the no-GUI mode.

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepoConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepoConstants.java
index 710ec97..b4a75a9 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepoConstants.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepoConstants.java
@@ -26,25 +26,43 @@
  */

 public class SdkRepoConstants extends RepoConstants {

 

-    /** The latest version of the sdk-repository XML Schema.

-     *  Valid version numbers are between 1 and this number, included. */

-    public static final int NS_LATEST_VERSION = 5;

+    /**

+     * The latest version of the sdk-repository XML Schema.

+     * Valid version numbers are between 1 and this number, included.

+     */

+    public static final int NS_LATEST_VERSION = 6;

 

-    /** The URL of the official Google sdk-repository site.

-     *  The URL ends with a /, allowing easy concatenation. */

+    /**

+     * The min version of the sdk-repository XML Schema we'll try to load.

+     * When looking for a repository-N.xml on the server, we'll check from

+     * {@link #NS_LATEST_VERSION} down to this revision.

+     * We only introduced the "repository-N.xml" pattern start with revision

+     * 5, so we know that <em>our</em> server will never contain a repository

+     * XML with a schema version lower than this one.

+     */

+    public static final int NS_SERVER_MIN_VERSION = 5;

+

+    /**

+     * The URL of the official Google sdk-repository site.

+     * The URL ends with a /, allowing easy concatenation.

+     * */

     public static final String URL_GOOGLE_SDK_SITE =

         "https://dl-ssl.google.com/android/repository/";                        //$NON-NLS-1$

 

-    /** The default name looked for by {@link SdkSource} when trying to load an

-     * sdk-repository XML if the URL doesn't match an existing resource. */

+    /**

+     * The default name looked for by {@link SdkSource} when trying to load an

+     * sdk-repository XML if the URL doesn't match an existing resource.

+     */

     public static final String URL_DEFAULT_FILENAME = "repository.xml";         //$NON-NLS-1$

 

-    /** The pattern name looked by {@link SdkSource} when trying to load

-     * the latest sdk-repository XML that is specific to the current XSD

-     * schema revision.

+    /**

+     * The pattern name looked by {@link SdkSource} when trying to load

+     * an sdk-repository XML that is specific to a given XSD revision.

+     * <p/>

+     * This must be used with {@link String#format(String, Object...)} with

+     * one integer parameter between 1 and {@link #NS_LATEST_VERSION}.

      */

-    public static final String URL_DEFAULT_FILENAME2 =

-        String.format("repository-%d.xml", NS_LATEST_VERSION);                  //$NON-NLS-1$

+    public static final String URL_FILENAME_PATTERN = "repository-%1$d.xml";      //$NON-NLS-1$

 

     /** The base of our sdk-repository XML namespace. */

     private static final String NS_BASE =

@@ -94,7 +112,6 @@
         NODE_PLATFORM_TOOL,

         NODE_DOC,

         NODE_SAMPLE,

-        NODE_EXTRA,

         NODE_SOURCE,

     };

 

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-addon-4.xsd b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-addon-4.xsd
new file mode 100755
index 0000000..9289e1c
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-addon-4.xsd
@@ -0,0 +1,396 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<xsd:schema
+    targetNamespace="http://schemas.android.com/sdk/android/addon/4"
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+    xmlns:sdk="http://schemas.android.com/sdk/android/addon/4"
+    elementFormDefault="qualified"
+    attributeFormDefault="unqualified"
+    version="1">
+
+    <!-- The repository contains a collection of downloadable items known as
+         "packages". Each package has a type and various attributes and contains
+         a list of file "archives" that can be downloaded for specific OSes.
+
+         An Android Addon repository is a web site that contains an "addon.xml"
+         file that conforms to this XML Schema.
+
+         History:
+         - v1 is used by the SDK Updater in Tools r8. It is split out of the
+           main SDK Repository XML Schema and can only contain <addon> and
+           <extra> packages.
+
+         - v2 is used by the SDK Updater in Tools r12.
+            - <extra> element now has a <project-files> element that contains 1 or
+              or more <path>, each indicating the relative path of a file that this package
+              can contribute to installed projects.
+            - <addon> element now has an optional <layoutlib> that indicates the API
+              and revision of the layout library for this particular add-on, if any.
+
+         - v3 is used by the SDK Updater in Tools R14:
+            - <extra> now has an <old-paths> element, a ;-separated list of old paths that
+              should be detected and migrated to the new <path> for that package.
+    -->
+
+    <xsd:element name="sdk-addon" type="sdk:repositoryType" />
+
+    <xsd:complexType name="repositoryType">
+        <xsd:annotation>
+            <xsd:documentation>
+                The repository contains a collection of downloadable packages.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:choice minOccurs="0" maxOccurs="unbounded">
+            <xsd:element name="add-on"  type="sdk:addonType"        />
+            <xsd:element name="extra"   type="sdk:extraType"        />
+            <xsd:element name="license" type="sdk:licenseType"      />
+        </xsd:choice>
+    </xsd:complexType>
+
+    <!-- The definition of an SDK Add-on package. -->
+
+    <xsd:complexType name="addonType">
+        <xsd:annotation>
+            <xsd:documentation>An SDK add-on package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The internal name id of the add-on. Must be unique per vendor. -->
+            <xsd:element name="name-id"         type="sdk:idType" />
+            <!-- The displayed name of the add-on. -->
+            <xsd:element name="name-display"    type="xsd:normalizedString" />
+
+            <!-- The internal vendor id of the add-on. Must be unique amongst vendors. -->
+            <xsd:element name="vendor-id"       type="sdk:idType" />
+            <!-- The displayed vendor name of the add-on. -->
+            <xsd:element name="vendor-display"  type="xsd:normalizedString" />
+
+            <!-- The Android API Level for the add-on. An int > 0. -->
+            <xsd:element name="api-level"    type="xsd:positiveInteger"  />
+            <!-- Note: Add-ons do not support 'codenames' (a.k.a. API previews). -->
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"     type="xsd:positiveInteger" />
+
+            <!-- An add-on can declare 0 or more libraries.
+                 This element is mandatory but it can be empty.
+            -->
+
+            <xsd:element name="libs">
+                <xsd:complexType>
+                    <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+                        <xsd:element name="lib">
+                            <xsd:complexType>
+                                <xsd:all>
+                                    <!-- The name of the library. -->
+                                    <xsd:element name="name" type="xsd:normalizedString" />
+                                    <!-- The optional description of this add-on library. -->
+                                    <xsd:element name="description" type="xsd:string" minOccurs="0" />
+                                </xsd:all>
+                            </xsd:complexType>
+                        </xsd:element>
+                    </xsd:sequence>
+                </xsd:complexType>
+            </xsd:element>
+
+            <!-- optional elements -->
+
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sdk:archivesType" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"     type="xsd:string"      minOccurs="0" />
+
+            <!-- Optional information on the layoutlib packaged in this platform. -->
+            <xsd:element name="layoutlib" type="sdk:layoutlibType"  minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <xsd:simpleType name="idType">
+        <xsd:annotation>
+            <xsd:documentation>
+                An ID string for an addon/extra name-id or vendor-id
+                can only be simple alphanumeric string.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:token">
+            <xsd:pattern value="[a-zA-Z0-9_-]+"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+
+    <!-- The definition of a layout library used by an addon. -->
+
+    <xsd:complexType name="layoutlibType" >
+        <xsd:annotation>
+            <xsd:documentation>
+                Version information for a layoutlib included in an addon.
+            .</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The layoutlib API level, an int > 0,
+                 incremented with each new incompatible lib. -->
+            <xsd:element name="api"          type="xsd:positiveInteger" />
+            <!-- The incremental minor revision for that API, e.g. in case of bug fixes.
+                 Optional. An int >= 0, assumed to be 0 if the element is missing. -->
+            <xsd:element name="revision"     type="xsd:nonNegativeInteger" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of an SDK extra package. This kind of package is for
+         "free" content. Such packages are installed in SDK/extras/vendor/path.
+    -->
+
+    <xsd:complexType name="extraType" >
+        <xsd:annotation>
+            <xsd:documentation>
+                An SDK extra package. This kind of package is for "free" content.
+                Such packages are installed in SDK/vendor/path.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The displayed name of the extra. -->
+            <xsd:element name="name-display"    type="xsd:normalizedString" />
+
+            <!-- The internal vendor id of the extra. Must be unique amongst vendors. -->
+            <xsd:element name="vendor-id"       type="sdk:idType" />
+            <!-- The displayed vendor name of the extra. -->
+            <xsd:element name="vendor-display"  type="xsd:normalizedString" />
+
+            <!-- The install path sub-folder name. It must not be empty. -->
+            <xsd:element name="path" type="sdk:segmentType" />
+
+            <!-- A semi-colon separated list of "obsolete" path names which are equivalent
+                 to the current 'path' name. When a package is seen using an old-paths' name,
+                 the package manager will try to upgrade it to the new path. -->
+            <xsd:element name="old-paths" type="sdk:segmentListType"  minOccurs="0" />
+
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"     type="xsd:positiveInteger" />
+
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sdk:archivesType" />
+
+            <!--  optional elements -->
+
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sdk:usesLicenseType"  minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"           minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"            minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"           minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"            minOccurs="0" />
+            <!-- The minimal revision of tools required by this package.
+                 Optional. If present, must be an int > 0. -->
+            <xsd:element name="min-tools-rev" type="xsd:positiveInteger" minOccurs="0" />
+            <!-- The minimal API level required by this package.
+                 Optional. If present, must be an int > 0. -->
+            <xsd:element name="min-api-level" type="xsd:positiveInteger" minOccurs="0" />
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"      type="xsd:string"          minOccurs="0" />
+
+            <!-- A list of project files contributed by this package. Optional. -->
+            <xsd:element name="project-files" type="sdk:projectFilesType" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of a path segment used by the extra element. -->
+
+    <xsd:simpleType name="segmentType">
+        <xsd:annotation>
+            <xsd:documentation>
+                One path segment for the install path of an extra element.
+                It must be a single-segment path. It must not be empty.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:token">
+            <xsd:pattern value="[a-zA-Z0-9_]+"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:simpleType name="segmentListType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A semi-colon separated list of a segmentTypes.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:token">
+            <xsd:pattern value="[a-zA-Z0-9_;]+"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+
+    <!-- The definition of a license to be referenced by the uses-license element. -->
+
+    <xsd:complexType name="licenseType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A license definition. Such a license must be used later as a reference
+                using a uses-license element in one of the package elements.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:simpleContent>
+            <xsd:extension base="xsd:string">
+                <xsd:attribute name="id"   type="xsd:ID" />
+                <xsd:attribute name="type" type="xsd:token" fixed="text" />
+            </xsd:extension>
+        </xsd:simpleContent>
+    </xsd:complexType>
+
+
+    <!-- Type describing the license used by a package.
+         The license MUST be defined using a license node and referenced
+         using the ref attribute of the license element inside a package.
+     -->
+
+    <xsd:complexType name="usesLicenseType">
+        <xsd:annotation>
+            <xsd:documentation>
+                Describes the license used by a package. The license MUST be defined
+                using a license node and referenced using the ref attribute of the
+                license element inside a package.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:attribute name="ref" type="xsd:IDREF" />
+    </xsd:complexType>
+
+
+    <!-- A collection of files that can be downloaded for a given architecture.
+         The <archives> node is mandatory in the repository elements and the
+         collection must have at least one <archive> declared.
+         Each archive is a zip file that will be unzipped in a location that depends
+         on its package type.
+     -->
+
+    <xsd:complexType name="archivesType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A collection of files that can be downloaded for a given architecture.
+                The &lt;archives&gt; node is mandatory in the repository packages and the
+                collection must have at least one &lt;archive&gt; declared.
+                Each archive is a zip file that will be unzipped in a location that depends
+                on its package type.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:sequence minOccurs="1" maxOccurs="unbounded">
+            <!-- One archive file -->
+            <xsd:element name="archive">
+                <xsd:complexType>
+                    <!-- Properties of the archive file -->
+                    <xsd:all>
+                        <!-- The size in bytes of the archive to download. -->
+                        <xsd:element name="size"     type="xsd:positiveInteger" />
+                        <!-- The checksum of the archive file. -->
+                        <xsd:element name="checksum" type="sdk:checksumType" />
+                        <!-- The URL is an absolute URL if it starts with http://, https://
+                             or ftp://. Otherwise it is relative to the parent directory that
+                             contains this repository.xml -->
+                        <xsd:element name="url"      type="xsd:token" />
+                    </xsd:all>
+
+                    <!-- Attributes that identify the OS and architecture -->
+                    <xsd:attribute name="os" use="required">
+                        <xsd:simpleType>
+                            <xsd:restriction base="xsd:token">
+                                <xsd:enumeration value="any" />
+                                <xsd:enumeration value="linux" />
+                                <xsd:enumeration value="macosx" />
+                                <xsd:enumeration value="windows" />
+                            </xsd:restriction>
+                        </xsd:simpleType>
+                    </xsd:attribute>
+                    <xsd:attribute name="arch" use="optional">
+                        <xsd:simpleType>
+                            <xsd:restriction base="xsd:token">
+                                <xsd:enumeration value="any" />
+                                <xsd:enumeration value="ppc" />
+                                <xsd:enumeration value="x86" />
+                                <xsd:enumeration value="x86_64" />
+                            </xsd:restriction>
+                        </xsd:simpleType>
+                    </xsd:attribute>
+                </xsd:complexType>
+            </xsd:element>
+        </xsd:sequence>
+    </xsd:complexType>
+
+
+    <!-- A collection of file paths available in an &lt;extra&gt; package
+         that can be installed in an Android project.
+         If present, the &lt;project-files&gt; collection must contain at least one path.
+         Each path is relative to the root directory of the package.
+     -->
+
+    <xsd:complexType name="projectFilesType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A collection of file paths available in an &lt;extra&gt; package
+                that can be installed in an Android project.
+                If present, the &lt;project-files&gt; collection must contain at least one path.
+                Each path is relative to the root directory of the package.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:sequence minOccurs="1" maxOccurs="unbounded">
+            <!-- One JAR Path, relative to the root folder of the package. -->
+            <xsd:element name="path" type="xsd:string" />
+        </xsd:sequence>
+    </xsd:complexType>
+
+
+    <!-- The definition of a file checksum -->
+
+    <xsd:simpleType name="sha1Number">
+        <xsd:annotation>
+            <xsd:documentation>A SHA1 checksum.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:string">
+            <xsd:pattern value="([0-9a-fA-F]){40}"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:complexType name="checksumType">
+        <xsd:annotation>
+            <xsd:documentation>A file checksum, currently only SHA1.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:simpleContent>
+            <xsd:extension base="sdk:sha1Number">
+                <xsd:attribute name="type" type="xsd:token" fixed="sha1" />
+            </xsd:extension>
+        </xsd:simpleContent>
+    </xsd:complexType>
+
+</xsd:schema>
diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ArchiveInstallerTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ArchiveInstallerTest.java
index 91fc2e6..46390b5 100755
--- a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ArchiveInstallerTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ArchiveInstallerTest.java
@@ -108,7 +108,7 @@
 
     /** Test we can install a simple new archive. */
     public void testInstall_NewArchive() throws Exception {
-        SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+        SdkSource src1 = new SdkRepoSource("http://repo.example.com/url", "repo1");
         MockEmptyPackage p = createRemoteEmptyPackage(src1, "testPkg");
         ArchiveReplacement ar = new ArchiveReplacement(p.getArchives()[0], null /*replaced*/);
 
@@ -133,7 +133,7 @@
               "Archive.Os=ANY\n" +
               "Pkg.Revision=0\n" +
               "Archive.Arch=ANY\n" +
-              "Pkg.SourceUrl=http\\://repo.com/url\n" +
+              "Pkg.SourceUrl=http\\://repo.example.com/url\n" +
               "'>]",
               stripDate(Arrays.toString(mFile.getOutputStreams())));
 
@@ -145,7 +145,7 @@
 
     /** Test we can replace and rename an Extra package. */
     public void testInstall_InstallExtraArchive() throws Exception {
-        SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+        SdkSource src1 = new SdkRepoSource("http://repo.example.com/url", "repo1");
 
         MockExtraPackage newPkg = createRemoteExtraPackage(src1, "vendor1", "oldPath", 2, 1);
         MockExtraPackage oldPkg = new MockExtraPackage(src1, "vendor1", "oldPath", 1, 1);
@@ -181,26 +181,28 @@
                 "[</sdk/extras/vendor1/oldPath/source.properties: " +
                      "'### Android Tool: Source of this archive.\n" +
                 "#...date...\n" +
-                "Extra.Vendor=vendor1\n" +
+                "Extra.VendorDisplay=vendor1\n" +
                 "Pkg.Desc=desc\n" +
-                "Pkg.DescUrl=url\n" +
-                "Archive.Os=ANY\n" +
-                "Pkg.Revision=2\n" +
-                "Archive.Arch=ANY\n" +
                 "Extra.Path=oldPath\n" +
-                "Pkg.SourceUrl=http\\://repo.com/url\n" +
+                "Archive.Arch=ANY\n" +
+                "Pkg.DescUrl=url\n" +
+                "Extra.NameDisplay=Vendor1 OldPath\n" +
+                "Archive.Os=ANY\n" +
+                "Pkg.SourceUrl=http\\://repo.example.com/url\n" +
+                "Pkg.Revision=2\n" +
+                "Extra.VendorId=vendor1\n" +
                 "'>]"),
                 stripDate(Arrays.toString(mFile.getOutputStreams())));
 
         assertEquals(
-                "Installing Vendor1 OldPath package, revision 2\n" +
-                "Installed Vendor1 OldPath package, revision 2\n",
+                "Installing Vendor1 OldPath, revision 2\n" +
+                "Installed Vendor1 OldPath, revision 2\n",
                 mMon.getCapturedLog());
     }
 
     /** Test we can replace and rename an Extra package. */
     public void testInstall_InstallRenamedExtraArchive() throws Exception {
-        SdkSource src1 = new SdkRepoSource("http://repo.com/url", "repo1");
+        SdkSource src1 = new SdkRepoSource("http://repo.example.com/url", "repo1");
 
         MockExtraPackage newPkg = createRemoteExtraPackage(
                 src1,
@@ -254,21 +256,23 @@
                 "[</sdk/extras/vendor1/newPath/source.properties: " +
                      "'### Android Tool: Source of this archive.\n" +
                 "#...date...\n" +
+                "Extra.VendorDisplay=vendor1\n" +
                 "Pkg.Desc=desc\n" +
                 "Extra.OldPaths=oldPath\n" +
-                "Extra.Vendor=vendor1\n" +
                 "Extra.Path=newPath\n" +
                 "Archive.Arch=ANY\n" +
                 "Pkg.DescUrl=url\n" +
+                "Extra.NameDisplay=Vendor1 NewPath\n" +
                 "Archive.Os=ANY\n" +
-                "Pkg.SourceUrl=http\\://repo.com/url\n" +
+                "Pkg.SourceUrl=http\\://repo.example.com/url\n" +
                 "Pkg.Revision=2\n" +
+                "Extra.VendorId=vendor1\n" +
                 "'>]"),
                 stripDate(Arrays.toString(mFile.getOutputStreams())));
 
         assertEquals(
-                "Installing Vendor1 NewPath package, revision 2\n" +
-                "Installed Vendor1 NewPath package, revision 2\n",
+                "Installing Vendor1 NewPath, revision 2\n" +
+                "Installed Vendor1 NewPath, revision 2\n",
                 mMon.getCapturedLog());
     }
 
diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest_v3.java
similarity index 74%
copy from sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest.java
copy to sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest_v3.java
index 230e366..c895fc9 100755
--- a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest_v3.java
@@ -24,7 +24,11 @@
 import java.util.Arrays;
 import java.util.Properties;
 
-public class ExtraPackageTest extends MinToolsPackageTest {
+/**
+ * Tests {@link ExtraPackage} using anddon-3.xsd: it has a {@code <path>} and {@code <vendor>}.
+ * (it lacks name-display, vendor-id and vendor-display with are in addon-4.xsd)
+ */
+public class ExtraPackageTest_v3 extends MinToolsPackageTest {
 
     private static final char PS = File.pathSeparatorChar;
 
@@ -45,8 +49,8 @@
         return p;
     }
 
-    @Override
-    protected Properties createProps() {
+    /** Properties used to "load" the package. When saved, they become different. */
+    private Properties createLoadedProps() {
         Properties props = super.createProps();
 
         // ExtraPackage properties
@@ -60,12 +64,33 @@
         return props;
     }
 
+    /** Properties saved by the package. They differ from loaded ones in name and vendor. */
+    private Properties createSavedProps() {
+        Properties props = super.createProps();
+
+        // ExtraPackage properties
+        props.setProperty(PkgProps.EXTRA_VENDOR_ID, "vendor");
+        props.setProperty(PkgProps.EXTRA_VENDOR_DISPLAY, "vendor");
+        props.setProperty(PkgProps.EXTRA_NAME_DISPLAY, "Vendor The Path");
+        props.setProperty(PkgProps.EXTRA_PATH, "the_path");
+        props.setProperty(PkgProps.EXTRA_OLD_PATHS, "old_path1;oldpath2");
+        props.setProperty(PkgProps.EXTRA_MIN_API_LEVEL, "11");
+        props.setProperty(PkgProps.EXTRA_PROJECT_FILES,
+                "path1.jar" + PS + "dir2/jar 2.jar" + PS + "dir/3/path");
+
+        return props;
+    }
+
     protected void testCreatedExtraPackage(ExtraPackage p) {
         super.testCreatedPackage(p);
 
         // Package properties
-        assertEquals("vendor", p.getVendor());
+        // vendor becomes both vendor-id and vendor-display
+        assertEquals("vendor", p.getVendorId());
+        assertEquals("vendor", p.getVendorDisplay());
         assertEquals("the_path", p.getPath());
+        // path and vendor are combined in the default display name
+        assertEquals("Vendor The Path", p.getDisplayName());
         assertEquals("[old_path1, oldpath2]", Arrays.toString(p.getOldPaths()));
         assertEquals(11, p.getMinApiLevel());
         assertEquals(
@@ -77,7 +102,7 @@
 
     @Override
     public final void testCreate() {
-        Properties props = createProps();
+        Properties props = createLoadedProps();
         ExtraPackage p = createExtraPackage(props);
 
         testCreatedExtraPackage(p);
@@ -85,17 +110,17 @@
 
     @Override
     public void testSaveProperties() {
-        Properties props = createProps();
+        Properties props = createLoadedProps();
         ExtraPackage p = createExtraPackage(props);
 
         Properties props2 = new Properties();
         p.saveProperties(props2);
 
-        assertEquals(props2, props);
+        assertEquals(props2, createSavedProps());
     }
 
     public void testSameItemAs() {
-        Properties props1 = createProps();
+        Properties props1 = createLoadedProps();
         ExtraPackage p1 = createExtraPackage(props1);
         assertTrue(p1.sameItemAs(p1));
 
@@ -144,7 +169,7 @@
     }
 
     public void testInstallId() {
-        Properties props = createProps();
+        Properties props = createLoadedProps();
         ExtraPackage p = createExtraPackage(props);
 
         assertEquals("extra-vendor-the_path", p.installId());
diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest_v4.java
similarity index 86%
rename from sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest.java
rename to sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest_v4.java
index 230e366..19e50f0 100755
--- a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/ExtraPackageTest_v4.java
@@ -24,7 +24,11 @@
 import java.util.Arrays;
 import java.util.Properties;
 
-public class ExtraPackageTest extends MinToolsPackageTest {
+/**
+ * Tests {@link ExtraPackage} using anddon-4.xsd:
+ * it has name-display, vendor-id and vendor-display.
+ */
+public class ExtraPackageTest_v4 extends MinToolsPackageTest {
 
     private static final char PS = File.pathSeparatorChar;
 
@@ -50,7 +54,9 @@
         Properties props = super.createProps();
 
         // ExtraPackage properties
-        props.setProperty(PkgProps.EXTRA_VENDOR, "vendor");
+        props.setProperty(PkgProps.EXTRA_VENDOR_ID, "the_vendor");
+        props.setProperty(PkgProps.EXTRA_VENDOR_DISPLAY, "The Company, Inc.");
+        props.setProperty(PkgProps.EXTRA_NAME_DISPLAY, "Some Extra Package");
         props.setProperty(PkgProps.EXTRA_PATH, "the_path");
         props.setProperty(PkgProps.EXTRA_OLD_PATHS, "old_path1;oldpath2");
         props.setProperty(PkgProps.EXTRA_MIN_API_LEVEL, "11");
@@ -64,7 +70,9 @@
         super.testCreatedPackage(p);
 
         // Package properties
-        assertEquals("vendor", p.getVendor());
+        assertEquals("the_vendor", p.getVendorId());
+        assertEquals("The Company, Inc.", p.getVendorDisplay());
+        assertEquals("Some Extra Package", p.getDisplayName());
         assertEquals("the_path", p.getPath());
         assertEquals("[old_path1, oldpath2]", Arrays.toString(p.getOldPaths()));
         assertEquals(11, p.getMinApiLevel());
@@ -101,7 +109,8 @@
 
         // different vendor, same path
         Properties props2 = new Properties(props1);
-        props2.setProperty(PkgProps.EXTRA_VENDOR, "vendor2");
+        props2.setProperty(PkgProps.EXTRA_VENDOR_ID, "vendor2");
+        props2.setProperty(PkgProps.EXTRA_VENDOR_DISPLAY, "Another Vendor Name");
         ExtraPackage p2 = createExtraPackage(props2);
         assertFalse(p1.sameItemAs(p2));
         assertFalse(p2.sameItemAs(p1));
@@ -147,6 +156,6 @@
         Properties props = createProps();
         ExtraPackage p = createExtraPackage(props);
 
-        assertEquals("extra-vendor-the_path", p.installId());
+        assertEquals("extra-the_vendor-the_path", p.installId());
     }
 }
diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockAddonPackage.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockAddonPackage.java
index cdf7bcc..3953f79 100755
--- a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockAddonPackage.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockAddonPackage.java
@@ -23,8 +23,10 @@
 import com.android.sdklib.SdkConstants;

 import com.android.sdklib.SystemImage;

 import com.android.sdklib.io.FileOp;

+import com.android.sdklib.repository.PkgProps;

 

 import java.util.Map;

+import java.util.Properties;

 

 /**

  * A mock {@link AddonPackage} for testing.

@@ -60,7 +62,21 @@
             int revision) {

         super(source,

               new MockAddonTarget(name, basePlatform.getTarget(), revision),

-              null /*props*/);

+              createProperties(name, basePlatform.getTarget()));

+    }

+

+    private static Properties createProperties(String name, IAndroidTarget baseTarget) {

+        String vendor = baseTarget.getVendor();

+        Properties props = new Properties();

+        props.setProperty(PkgProps.ADDON_NAME_ID, name);

+        props.setProperty(PkgProps.ADDON_NAME_DISPLAY,

+                String.format("The %1$s from %2$s",                  //$NON-NLS-1$

+                        name, vendor));

+        props.setProperty(PkgProps.ADDON_VENDOR_ID,

+                String.format("vendor-id-%1$s", vendor));                   //$NON-NLS-1$

+        props.setProperty(PkgProps.ADDON_VENDOR_DISPLAY,

+                String.format("The %1$s", vendor));                  //$NON-NLS-1$

+        return props;

     }

 

     /**

diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/PackageTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/PackageTest.java
index ab560f6..5cf1c98 100755
--- a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/PackageTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/PackageTest.java
@@ -187,9 +187,9 @@
                 "[Android SDK Tools, revision 7, " +
                  "SDK Platform Android android-1, API 1, revision 2, " +
                  "Intel x86 Atom System Image, Android API 1, revision 4, " +
-                 "addon by vendor 1, Android API 1, revision 3, " +
+                 "addon, Android API 1, revision 3, " +
                  "Broken package for API 1, " +
-                 "Vendor Path package, revision 5]",
+                 "Vendor Path, revision 5]",
                 Arrays.toString(list.toArray()));
     }
 }
diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkAddonSourceTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkAddonSourceTest.java
index c11ff0d..11626e6 100755
--- a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkAddonSourceTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkAddonSourceTest.java
@@ -145,9 +145,9 @@
     }

 

     /**

-     * Validate we can still load a valid add-on schema version 1

+     * Validate we can load a valid add-on schema version 1

      */

-    public void testLoadOldXml_1() throws Exception {

+    public void testLoadAddonXml_1() throws Exception {

         InputStream xmlStream = getTestResource("/com/android/sdklib/testdata/addon_sample_1.xml");

 

         // guess the version from the XML document

@@ -171,12 +171,12 @@
         // Get the packages

         assertTrue(mSource._parsePackages(doc, uri, monitor));

 

-        assertEquals("Found My First add-on by John Doe, Android API 1, revision 1\n" +

-                     "Found My Second add-on by John Deer, Android API 2, revision 42\n" +

-                     "Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +

-                     "Found G USB Driver package, revision 43 (Obsolete)\n" +

-                     "Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +

-                     "Found Unkown Extra package, revision 2 (Obsolete)\n",

+        assertEquals("Found My First add-on, Android API 1, revision 1\n" +

+                     "Found My Second add-on, Android API 2, revision 42\n" +

+                     "Found This add-on has no libraries, Android API 4, revision 3\n" +

+                     "Found G USB Driver, revision 43 (Obsolete)\n" +

+                     "Found Android Vendor Extra API Dep, revision 2 (Obsolete)\n" +

+                     "Found Unknown Extra, revision 2 (Obsolete)\n",

                 monitor.getCapturedVerboseLog());

         assertEquals("", monitor.getCapturedLog());

         assertEquals("", monitor.getCapturedErrorLog());

@@ -199,16 +199,19 @@
         ArrayList<File>   extraInstall = new ArrayList<File>();

         for (Package p : pkgs) {

             if (p instanceof ExtraPackage) {

-                extraPaths.add(((ExtraPackage) p).getPath());

-                extraVendors.add(((ExtraPackage) p).getVendor());

-                extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));

+                ExtraPackage ep = (ExtraPackage) p;

+                extraPaths.add(ep.getPath());

+                extraVendors.add(ep.getVendorId() + "/" + ep.getVendorDisplay());

+                extraInstall.add(ep.getInstallFolder(osSdkPath, sdkManager));

             }

         }

         assertEquals(

                 "[extra_api_dep, usb_driver, extra0000005f]",

                 Arrays.toString(extraPaths.toArray()));

         assertEquals(

-                "[android_vendor, g, vendor0000005f]",

+                "[android_vendor/android_vendor, " +

+                 "g/g, " +

+                 "vendor0000005f/____]",

                 Arrays.toString(extraVendors.toArray()));

         assertEquals(

                 ("[SDK/extras/android_vendor/extra_api_dep, " +

@@ -220,7 +223,7 @@
     /**

      * Validate we can still load a valid add-on schema version 2

      */

-    public void testLoadOldXml_2() throws Exception {

+    public void testLoadAddonXml_2() throws Exception {

         InputStream xmlStream = getTestResource("/com/android/sdklib/testdata/addon_sample_2.xml");

 

         // guess the version from the XML document

@@ -244,12 +247,12 @@
         // Get the packages

         assertTrue(mSource._parsePackages(doc, uri, monitor));

 

-        assertEquals("Found My First add-on by John Doe, Android API 1, revision 1\n" +

-                     "Found My Second add-on by John Deer, Android API 2, revision 42\n" +

-                     "Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +

-                     "Found G USB Driver package, revision 43 (Obsolete)\n" +

-                     "Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +

-                     "Found Unkown Extra package, revision 2 (Obsolete)\n",

+        assertEquals("Found My First add-on, Android API 1, revision 1\n" +

+                     "Found My Second add-on, Android API 2, revision 42\n" +

+                     "Found This add-on has no libraries, Android API 4, revision 3\n" +

+                     "Found G USB Driver, revision 43 (Obsolete)\n" +

+                     "Found Android Vendor Extra API Dep, revision 2 (Obsolete)\n" +

+                     "Found Unknown Extra, revision 2 (Obsolete)\n",

                 monitor.getCapturedVerboseLog());

         assertEquals("", monitor.getCapturedLog());

         assertEquals("", monitor.getCapturedErrorLog());

@@ -275,8 +278,8 @@
         }

         assertEquals(

                  "[Pair [first=3, second=42], " +         // for #3 "This add-on has no libraries"

-                 "Pair [first=0, second=0], " +           // for #2 "My Second add-on"

-                 "Pair [first=5, second=0]]",            // for #1 "My First add-on"

+                  "Pair [first=0, second=0], " +          // for #2 "My Second add-on"

+                  "Pair [first=5, second=0]]",            // for #1 "My First add-on"

                 Arrays.toString(layoutlibVers.toArray()));

 

 

@@ -290,16 +293,19 @@
         ArrayList<File>   extraInstall = new ArrayList<File>();

         for (Package p : pkgs) {

             if (p instanceof ExtraPackage) {

-                extraPaths.add(((ExtraPackage) p).getPath());

-                extraVendors.add(((ExtraPackage) p).getVendor());

-                extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));

+                ExtraPackage ep = (ExtraPackage) p;

+                extraPaths.add(ep.getPath());

+                extraVendors.add(ep.getVendorId() + "/" + ep.getVendorDisplay());

+                extraInstall.add(ep.getInstallFolder(osSdkPath, sdkManager));

             }

         }

         assertEquals(

                 "[extra_api_dep, usb_driver, extra0000005f]",

                 Arrays.toString(extraPaths.toArray()));

         assertEquals(

-                "[android_vendor, g, vendor0000005f]",

+                "[android_vendor/android_vendor, " +

+                 "g/g, " +

+                 "vendor0000005f/____]",

                 Arrays.toString(extraVendors.toArray()));

         assertEquals(

                 ("[SDK/extras/android_vendor/extra_api_dep, " +

@@ -309,9 +315,9 @@
     }

 

     /**

-     * Validate we can still load a valid add-on schema version 3

+     * Validate we can load a valid add-on schema version 3

      */

-    public void testLoadOldXml_3() throws Exception {

+    public void testLoadAddonXml_3() throws Exception {

         InputStream xmlStream = getTestResource("/com/android/sdklib/testdata/addon_sample_3.xml");

 

         // guess the version from the XML document

@@ -335,12 +341,12 @@
         // Get the packages

         assertTrue(mSource._parsePackages(doc, uri, monitor));

 

-        assertEquals("Found My First add-on by John Doe, Android API 1, revision 1\n" +

-                     "Found My Second add-on by John Deer, Android API 2, revision 42\n" +

-                     "Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +

-                     "Found G USB Driver package, revision 43 (Obsolete)\n" +

-                     "Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +

-                     "Found Unkown Extra package, revision 2 (Obsolete)\n",

+        assertEquals("Found My First add-on, Android API 1, revision 1\n" +

+                     "Found My Second add-on, Android API 2, revision 42\n" +

+                     "Found This add-on has no libraries, Android API 4, revision 3\n" +

+                     "Found G USB Driver, revision 43 (Obsolete)\n" +

+                     "Found Android Vendor Extra API Dep, revision 2 (Obsolete)\n" +

+                     "Found Unknown Extra, revision 2 (Obsolete)\n",

                 monitor.getCapturedVerboseLog());

         assertEquals("", monitor.getCapturedLog());

         assertEquals("", monitor.getCapturedErrorLog());

@@ -366,8 +372,8 @@
         }

         assertEquals(

                  "[Pair [first=3, second=42], " +         // for #3 "This add-on has no libraries"

-                 "Pair [first=0, second=0], " +           // for #2 "My Second add-on"

-                 "Pair [first=5, second=0]]",            // for #1 "My First add-on"

+                  "Pair [first=0, second=0], " +          // for #2 "My Second add-on"

+                  "Pair [first=5, second=0]]",            // for #1 "My First add-on"

                 Arrays.toString(layoutlibVers.toArray()));

 

 

@@ -385,7 +391,7 @@
                 ExtraPackage ep = (ExtraPackage) p;

                 // combine path and old-paths in the form "path [old_path1, old_path2]"

                 extraPaths.add(ep.getPath() + " " + Arrays.toString(ep.getOldPaths()));

-                extraVendors.add(ep.getVendor());

+                extraVendors.add(ep.getVendorId() + "/" + ep.getVendorDisplay());

                 extraInstall.add(ep.getInstallFolder(osSdkPath, sdkManager));

 

                 ArrayList<String> filePaths = new ArrayList<String>();

@@ -401,9 +407,9 @@
                  "extra0000005f []]",

                 Arrays.toString(extraPaths.toArray()));

         assertEquals(

-                "[android_vendor, " +

-                 "g, " +

-                 "vendor0000005f]",

+                "[android_vendor/android_vendor, " +

+                 "g/g, " +

+                 "vendor0000005f/____]",

                 Arrays.toString(extraVendors.toArray()));

         assertEquals(

                 ("[SDK/extras/android_vendor/extra_api_dep, " +

@@ -418,6 +424,139 @@
     }

 

     /**

+     * Validate we can load a valid add-on schema version 4

+     */

+    public void testLoadAddonXml_4() throws Exception {

+        InputStream xmlStream = getTestResource("/com/android/sdklib/testdata/addon_sample_4.xml");

+

+        // guess the version from the XML document

+        int version = mSource._getXmlSchemaVersion(xmlStream);

+        assertEquals(4, version);

+

+        Boolean[] validatorFound = new Boolean[] { Boolean.FALSE };

+        String[] validationError = new String[] { null };

+        String url = "not-a-valid-url://" + SdkAddonConstants.URL_DEFAULT_FILENAME;

+

+        String uri = mSource._validateXml(xmlStream, url, version, validationError, validatorFound);

+        assertEquals(Boolean.TRUE, validatorFound[0]);

+        assertEquals(null, validationError[0]);

+        assertEquals(SdkAddonConstants.getSchemaUri(4), uri);

+

+        // Validation was successful, load the document

+        MockMonitor monitor = new MockMonitor();

+        Document doc = mSource._getDocument(xmlStream, monitor);

+        assertNotNull(doc);

+

+        // Get the packages

+        assertTrue(mSource._parsePackages(doc, uri, monitor));

+

+        assertEquals("Found My First add-on, Android API 1, revision 1\n" +

+                     "Found My Second add-on, Android API 2, revision 42\n" +

+                     "Found This add-on has no libraries, Android API 4, revision 3\n" +

+                     "Found Random name, not an id!, revision 43 (Obsolete)\n" +

+                     "Found Yet another extra, by Android, revision 2\n" +

+                     "Found . -..- - .-. .-, revision 2 (Obsolete)\n",

+                monitor.getCapturedVerboseLog());

+        assertEquals("", monitor.getCapturedLog());

+        assertEquals("", monitor.getCapturedErrorLog());

+

+        // check the packages we found... we expected to find 6 packages with each at least

+        // one archive.

+        // Note the order doesn't necessary match the one from the

+        // assertEquald(getCapturedVerboseLog) because packages are sorted using the

+        // Packages' sorting order, e.g. all platforms are sorted by descending API level, etc.

+        Package[] pkgs = mSource.getPackages();

+

+        assertEquals(6, pkgs.length);

+        for (Package p : pkgs) {

+            assertTrue(p.getArchives().length >= 1);

+        }

+

+        // Check the addon packages: vendor/name id vs display

+        ArrayList<String> addonNames   = new ArrayList<String>();

+        ArrayList<String> addonVendors = new ArrayList<String>();

+        for (Package p : pkgs) {

+            if (p instanceof AddonPackage) {

+                AddonPackage ap = (AddonPackage) p;

+                addonNames.add(ap.getNameId() + "/" + ap.getDisplayName());

+                addonVendors.add(ap.getVendorId() + "/" + ap.getDisplayVendor());

+            }

+        }

+        // Addons are sorted by addon/vendor id and thus their order differs from the

+        // XML or the parsed package list.

+        assertEquals(

+                "[no_libs/This add-on has no libraries, " +

+                 "My_Second_add-on/My Second add-on, " +

+                 "My_First_add-on/My First add-on]",

+                Arrays.toString(addonNames.toArray()));

+        assertEquals(

+                "[Joe_Bar/Joe Bar, " +

+                 "John_Deer/John Deer, " +

+                 "John_Doe/John Doe]",

+                Arrays.toString(addonVendors.toArray()));

+

+        // Check the layoutlib of the platform packages.

+        ArrayList<Pair<Integer, Integer>> layoutlibVers = new ArrayList<Pair<Integer,Integer>>();

+        for (Package p : pkgs) {

+            if (p instanceof AddonPackage) {

+                layoutlibVers.add(((AddonPackage) p).getLayoutlibVersion());

+            }

+        }

+        assertEquals(

+                 "[Pair [first=3, second=42], " +         // for #3 "This add-on has no libraries"

+                  "Pair [first=0, second=0], " +          // for #2 "My Second add-on"

+                  "Pair [first=5, second=0]]",            // for #1 "My First add-on"

+                Arrays.toString(layoutlibVers.toArray()));

+

+

+        // Check the extra packages: path, vendor, install folder, old-paths

+        final String osSdkPath = "SDK";

+        final SdkManager sdkManager = new MockEmptySdkManager(osSdkPath);

+

+        ArrayList<String> extraPaths   = new ArrayList<String>();

+        ArrayList<String> extraVendors = new ArrayList<String>();

+        ArrayList<File>   extraInstall = new ArrayList<File>();

+        ArrayList<ArrayList<String>> extraFilePaths = new ArrayList<ArrayList<String>>();

+        for (Package p : pkgs) {

+            if (p instanceof ExtraPackage) {

+                ExtraPackage ep = (ExtraPackage) p;

+                // combine path and old-paths in the form "path [old_path1, old_path2]"

+                extraPaths.add(ep.getPath() + " " + Arrays.toString(ep.getOldPaths()));

+                extraVendors.add(ep.getVendorId() + "/" + ep.getVendorDisplay());

+                extraInstall.add(ep.getInstallFolder(osSdkPath, sdkManager));

+

+                ArrayList<String> filePaths = new ArrayList<String>();

+                for (String filePath : ep.getProjectFiles()) {

+                    filePaths.add(filePath);

+                }

+                extraFilePaths.add(filePaths);

+            }

+        }

+        // Extras are sorted by vendor-id/path and thus their order differs from the

+        // XML or the parsed package list.

+        assertEquals(

+                "[extra0000005f [], " +                             // for extra #3

+                 "extra_api_dep [path1, old_path2, oldPath3], " +   // for extra #2

+                 "usb_driver []]",                                  // for extra #1

+                Arrays.toString(extraPaths.toArray()));

+        assertEquals(

+                "[____/____, " +

+                 "android_vendor/Android Vendor, " +

+                 "cyclop/The big bus]",

+                Arrays.toString(extraVendors.toArray()));

+        assertEquals(

+                ("[SDK/extras/____/extra0000005f, " +

+                  "SDK/extras/android_vendor/extra_api_dep, " +

+                  "SDK/extras/cyclop/usb_driver]").replace('/', File.separatorChar),

+                Arrays.toString(extraInstall.toArray()));

+        assertEquals(

+                "[[], " +

+                 "[v8/veggies_8.jar, root.jar, dir1/dir 2 with space/mylib.jar], " +

+                 "[]]",

+                Arrays.toString(extraFilePaths.toArray()));

+    }

+

+    /**

      * Returns an SdkLib file resource as a {@link ByteArrayInputStream},

      * which has the advantage that we can use {@link InputStream#reset()} on it

      * at any time to read it multiple times.

diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkRepoSourceTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkRepoSourceTest.java
index bd784e4..77e9312 100755
--- a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkRepoSourceTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkRepoSourceTest.java
@@ -225,15 +225,15 @@
 

         assertEquals("Found SDK Platform Android 1.0, API 1, revision 3\n" +

                      "Found Documentation for Android SDK, API 1, revision 1\n" +

-                     "Found My First add-on by John Doe, Android API 1, revision 1\n" +

+                     "Found My First add-on, Android API 1, revision 1\n" +

                      "Found SDK Platform Android 1.1, API 2, revision 12\n" +

-                     "Found My Second add-on by John Deer, Android API 2, revision 42\n" +

+                     "Found My Second add-on, Android API 2, revision 42\n" +

                      "Found SDK Platform Android Pastry Preview, revision 3\n" +

                      "Found Android SDK Tools, revision 1\n" +

                      "Found Documentation for Android SDK, API 2, revision 42\n" +

                      "Found Android SDK Tools, revision 42\n" +

-                     "Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +

-                     "Found Usb Driver package, revision 43\n",

+                     "Found This add-on has no libraries, Android API 4, revision 3\n" +

+                     "Found Usb Driver, revision 43\n",

                 monitor.getCapturedVerboseLog());

         assertEquals("", monitor.getCapturedLog());

         assertEquals("", monitor.getCapturedErrorLog());

@@ -256,16 +256,17 @@
         ArrayList<File>   extraInstall = new ArrayList<File>();

         for (Package p : pkgs) {

             if (p instanceof ExtraPackage) {

-                extraPaths.add(((ExtraPackage) p).getPath());

-                extraVendors.add(((ExtraPackage) p).getVendor());

-                extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));

+                ExtraPackage ep = (ExtraPackage) p;

+                extraPaths.add(ep.getPath());

+                extraVendors.add(ep.getVendorId() + "/" + ep.getVendorDisplay());

+                extraInstall.add(ep.getInstallFolder(osSdkPath, sdkManager));

             }

         }

         assertEquals(

                 "[usb_driver]",

                 Arrays.toString(extraPaths.toArray()));

         assertEquals(

-                "[]",

+                "[/]",

                 Arrays.toString(extraVendors.toArray()));

         assertEquals(

                 "[SDK/extras/usb_driver]".replace('/', File.separatorChar),

@@ -302,16 +303,16 @@
 

         assertEquals("Found SDK Platform Android 1.0, API 1, revision 3\n" +

                      "Found Documentation for Android SDK, API 1, revision 1\n" +

-                     "Found My First add-on by John Doe, Android API 1, revision 1\n" +

+                     "Found My First add-on, Android API 1, revision 1\n" +

                      "Found SDK Platform Android 1.1, API 2, revision 12\n" +

-                     "Found My Second add-on by John Deer, Android API 2, revision 42\n" +

+                     "Found My Second add-on, Android API 2, revision 42\n" +

                      "Found SDK Platform Android Pastry Preview, revision 3\n" +

                      "Found Android SDK Tools, revision 1\n" +

                      "Found Documentation for Android SDK, API 2, revision 42\n" +

                      "Found Android SDK Tools, revision 42\n" +

-                     "Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +

-                     "Found Usb Driver package, revision 43 (Obsolete)\n" +

-                     "Found Extra API Dep package, revision 2 (Obsolete)\n" +

+                     "Found This add-on has no libraries, Android API 4, revision 3\n" +

+                     "Found Usb Driver, revision 43 (Obsolete)\n" +

+                     "Found Extra API Dep, revision 2 (Obsolete)\n" +

                      "Found Samples for SDK API 14, revision 24 (Obsolete)\n",

                 monitor.getCapturedVerboseLog());

         assertEquals("", monitor.getCapturedLog());

@@ -335,16 +336,17 @@
         ArrayList<File>   extraInstall = new ArrayList<File>();

         for (Package p : pkgs) {

             if (p instanceof ExtraPackage) {

-                extraPaths.add(((ExtraPackage) p).getPath());

-                extraVendors.add(((ExtraPackage) p).getVendor());

-                extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));

+                ExtraPackage ep = (ExtraPackage) p;

+                extraPaths.add(ep.getPath());

+                extraVendors.add(ep.getVendorId() + "/" + ep.getVendorDisplay());

+                extraInstall.add(ep.getInstallFolder(osSdkPath, sdkManager));

             }

         }

         assertEquals(

                 "[extra_api_dep, usb_driver]",

                 Arrays.toString(extraPaths.toArray()));

         assertEquals(

-                "[, ]",

+                "[/, /]",

                 Arrays.toString(extraVendors.toArray()));

         assertEquals(

                 "[SDK/extras/extra_api_dep, SDK/extras/usb_driver]".replace('/', File.separatorChar),

@@ -387,8 +389,8 @@
                     "Found Documentation for Android SDK, API 2, revision 42\n" +

                     "Found Android SDK Tools, revision 42\n" +

                     "Found Android SDK Platform-tools, revision 3\n" +

-                    "Found A USB Driver package, revision 43 (Obsolete)\n" +

-                    "Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +

+                    "Found A USB Driver, revision 43 (Obsolete)\n" +

+                    "Found Android Vendor Extra API Dep, revision 2 (Obsolete)\n" +

                     "Found Samples for SDK API 14, revision 24 (Obsolete)\n",

                 monitor.getCapturedVerboseLog());

         assertEquals("", monitor.getCapturedLog());

@@ -412,16 +414,17 @@
         ArrayList<File>   extraInstall = new ArrayList<File>();

         for (Package p : pkgs) {

             if (p instanceof ExtraPackage) {

-                extraPaths.add(((ExtraPackage) p).getPath());

-                extraVendors.add(((ExtraPackage) p).getVendor());

-                extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));

+                ExtraPackage ep = (ExtraPackage) p;

+                extraPaths.add(ep.getPath());

+                extraVendors.add(ep.getVendorId() + "/" + ep.getVendorDisplay());

+                extraInstall.add(ep.getInstallFolder(osSdkPath, sdkManager));

             }

         }

         assertEquals(

                 "[extra_api_dep, usb_driver]",

                 Arrays.toString(extraPaths.toArray()));

         assertEquals(

-                "[android_vendor, a]",

+                "[android_vendor/android_vendor, a/a]",

                 Arrays.toString(extraVendors.toArray()));

         assertEquals(

                 "[SDK/extras/android_vendor/extra_api_dep, SDK/extras/a/usb_driver]"

@@ -465,8 +468,8 @@
                     "Found Documentation for Android SDK, API 2, revision 42\n" +

                     "Found Android SDK Tools, revision 42\n" +

                     "Found Android SDK Platform-tools, revision 3\n" +

-                    "Found A USB Driver package, revision 43 (Obsolete)\n" +

-                    "Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +

+                    "Found A USB Driver, revision 43 (Obsolete)\n" +

+                    "Found Android Vendor Extra API Dep, revision 2 (Obsolete)\n" +

                     "Found Samples for SDK API 14, revision 24 (Obsolete)\n",

                 monitor.getCapturedVerboseLog());

         assertEquals("", monitor.getCapturedLog());

@@ -508,12 +511,13 @@
         ArrayList<ArrayList<String>> extraFilePaths = new ArrayList<ArrayList<String>>();

         for (Package p : pkgs) {

             if (p instanceof ExtraPackage) {

-                extraPaths.add(((ExtraPackage) p).getPath());

-                extraVendors.add(((ExtraPackage) p).getVendor());

-                extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));

+                ExtraPackage ep = (ExtraPackage) p;

+                extraPaths.add(ep.getPath());

+                extraVendors.add(ep.getVendorId() + "/" + ep.getVendorDisplay());

+                extraInstall.add(ep.getInstallFolder(osSdkPath, sdkManager));

 

                 ArrayList<String> filePaths = new ArrayList<String>();

-                for (String filePath : ((ExtraPackage) p).getProjectFiles()) {

+                for (String filePath : ep.getProjectFiles()) {

                     filePaths.add(filePath);

                 }

                 extraFilePaths.add(filePaths);

@@ -523,7 +527,7 @@
                 "[extra_api_dep, usb_driver]",

                 Arrays.toString(extraPaths.toArray()));

         assertEquals(

-                "[android_vendor, a]",

+                "[android_vendor/android_vendor, a/a]",

                 Arrays.toString(extraVendors.toArray()));

         assertEquals(

                 "[SDK/extras/android_vendor/extra_api_dep, SDK/extras/a/usb_driver]"

@@ -575,8 +579,8 @@
                      "Found Documentation for Android SDK, API 2, revision 42\n" +

                      "Found Android SDK Tools, revision 42\n" +

                      "Found Android SDK Platform-tools, revision 3\n" +

-                     "Found A USB Driver package, revision 43 (Obsolete)\n" +

-                     "Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +

+                     "Found A USB Driver, revision 43 (Obsolete)\n" +

+                     "Found Android Vendor Extra API Dep, revision 2 (Obsolete)\n" +

                      "Found Samples for SDK API 14, revision 24 (Obsolete)\n" +

                      "Found ARM EABI System Image, Android API 42, revision 12\n" +

                      "Found Sources for Android SDK, API 42, revision 12\n",

@@ -631,7 +635,7 @@
                 ExtraPackage ep = (ExtraPackage) p;

                 // combine path and old-paths in the form "path [old_path1, old_path2]"

                 extraPaths.add(ep.getPath() + " " + Arrays.toString(ep.getOldPaths()));

-                extraVendors.add(ep.getVendor());

+                extraVendors.add(ep.getVendorId() + "/" + ep.getVendorDisplay());

                 extraInstall.add(ep.getInstallFolder(osSdkPath, sdkManager));

 

                 ArrayList<String> filePaths = new ArrayList<String>();

@@ -646,8 +650,8 @@
                  "usb_driver []]",

                 Arrays.toString(extraPaths.toArray()));

         assertEquals(

-                "[android_vendor, " +

-                 "a]",

+                "[android_vendor/android_vendor, " +

+                 "a/a]",

                 Arrays.toString(extraVendors.toArray()));

         assertEquals(

                 ("[SDK/extras/android_vendor/extra_api_dep, " +

@@ -689,6 +693,147 @@
     }

 

     /**

+     * Validate what we can load from repository in schema version 6

+     */

+    public void testLoadXml_6() throws Exception {

+        InputStream xmlStream = getTestResource(

+                    "/com/android/sdklib/testdata/repository_sample_6.xml");

+

+        // guess the version from the XML document

+        int version = mSource._getXmlSchemaVersion(xmlStream);

+        assertEquals(6, version);

+

+        Boolean[] validatorFound = new Boolean[] { Boolean.FALSE };

+        String[] validationError = new String[] { null };

+        String url = "not-a-valid-url://" + SdkRepoConstants.URL_DEFAULT_FILENAME;

+

+        String uri = mSource._validateXml(xmlStream, url, version, validationError, validatorFound);

+        assertEquals(Boolean.TRUE, validatorFound[0]);

+        assertEquals(null, validationError[0]);

+        assertEquals(SdkRepoConstants.getSchemaUri(6), uri);

+

+        // Validation was successful, load the document

+        MockMonitor monitor = new MockMonitor();

+        Document doc = mSource._getDocument(xmlStream, monitor);

+        assertNotNull(doc);

+

+        // Get the packages

+        assertTrue(mSource._parsePackages(doc, uri, monitor));

+

+        assertEquals("Found SDK Platform Android 1.0, API 1, revision 3\n" +

+                     "Found Documentation for Android SDK, API 1, revision 1\n" +

+                     "Found Sources for Android SDK, API 1, revision 1\n" +

+                     "Found SDK Platform Android 1.1, API 2, revision 12\n" +

+                     "Found Intel x86 Atom System Image, Android API 2, revision 1\n" +

+                     "Found ARM EABI v7a System Image, Android API 2, revision 2\n" +

+                     "Found Sources for Android SDK, API 2, revision 2\n" +

+                     "Found SDK Platform Android Pastry Preview, revision 3\n" +

+                     "Found Android SDK Tools, revision 1\n" +

+                     "Found Documentation for Android SDK, API 2, revision 42\n" +

+                     "Found Android SDK Tools, revision 42\n" +

+                     "Found Android SDK Platform-tools, revision 3\n" +

+                     "Found Samples for SDK API 14, revision 24 (Obsolete)\n" +

+                     "Found ARM EABI System Image, Android API 42, revision 12\n" +

+                     "Found Sources for Android SDK, API 42, revision 12\n",

+                monitor.getCapturedVerboseLog());

+        assertEquals("", monitor.getCapturedLog());

+        assertEquals("", monitor.getCapturedErrorLog());

+

+        // check the packages we found... we expected to find 13 packages with each at least

+        // one archive.

+        // Note the order doesn't necessary match the one from the

+        // assertEquald(getCapturedVerboseLog) because packages are sorted using the

+        // Packages' sorting order, e.g. all platforms are sorted by descending API level, etc.

+        Package[] pkgs = mSource.getPackages();

+

+        assertEquals(15, pkgs.length);

+        for (Package p : pkgs) {

+            assertTrue(p.getArchives().length >= 1);

+        }

+

+        // Check the layoutlib & included-abi of the platform packages.

+        ArrayList<Pair<Integer, Integer>> layoutlibVers = new ArrayList<Pair<Integer,Integer>>();

+        ArrayList<String> includedAbi = new ArrayList<String>();

+        for (Package p : pkgs) {

+            if (p instanceof PlatformPackage) {

+                layoutlibVers.add(((PlatformPackage) p).getLayoutlibVersion());

+                String abi = ((PlatformPackage) p).getIncludedAbi();

+                includedAbi.add(abi == null ? "(null)" : abi);

+            }

+        }

+        assertEquals(

+                "[Pair [first=1, second=0], " +         // platform API 5 preview

+                 "Pair [first=5, second=31415], " +     // platform API 2

+                 "Pair [first=5, second=0]]",           // platform API 1

+                Arrays.toString(layoutlibVers.toArray()));

+        assertEquals(

+                "[(null), " +                           // platform API 5 preview

+                 "x86, " +                              // platform API 2

+                 "armeabi]",                            // platform API 1

+                Arrays.toString(includedAbi.toArray()));

+

+        // Check the extra packages path, vendor, install folder, project-files, old-paths

+

+        final String osSdkPath = "SDK";

+        final SdkManager sdkManager = new MockEmptySdkManager(osSdkPath);

+

+        ArrayList<String> extraPaths    = new ArrayList<String>();

+        ArrayList<String> extraVendors  = new ArrayList<String>();

+        ArrayList<File>   extraInstall  = new ArrayList<File>();

+        ArrayList<ArrayList<String>> extraFilePaths = new ArrayList<ArrayList<String>>();

+        for (Package p : pkgs) {

+            if (p instanceof ExtraPackage) {

+                ExtraPackage ep = (ExtraPackage) p;

+                // combine path and old-paths in the form "path [old_path1, old_path2]"

+                extraPaths.add(ep.getPath() + " " + Arrays.toString(ep.getOldPaths()));

+                extraVendors.add(ep.getVendorId() + "/" + ep.getVendorDisplay());

+                extraInstall.add(ep.getInstallFolder(osSdkPath, sdkManager));

+

+                ArrayList<String> filePaths = new ArrayList<String>();

+                for (String filePath : ep.getProjectFiles()) {

+                    filePaths.add(filePath);

+                }

+                extraFilePaths.add(filePaths);

+            }

+        }

+

+        // There are no extra packages anymore in repository-6

+        assertEquals("[]", Arrays.toString(extraPaths.toArray()));

+        assertEquals("[]", Arrays.toString(extraVendors.toArray()));

+        assertEquals("[]", Arrays.toString(extraInstall.toArray()));

+        assertEquals("[]", Arrays.toString(extraFilePaths.toArray()));

+

+        // Check the system-image packages

+        ArrayList<String> sysImgVersionAbi = new ArrayList<String>();

+        for (Package p : pkgs) {

+            if (p instanceof SystemImagePackage) {

+                SystemImagePackage sip = (SystemImagePackage) p;

+                String v = sip.getVersion().getApiString();

+                String a = sip.getAbi();

+                sysImgVersionAbi.add(String.format("%1$s %2$s", v, a)); //$NON-NLS-1$

+            }

+        }

+        assertEquals(

+                "[42 armeabi, " +

+                "2 armeabi-v7a, " +

+                "2 x86]",

+                Arrays.toString(sysImgVersionAbi.toArray()));

+

+        // Check the source packages

+        ArrayList<String> sourceVersion = new ArrayList<String>();

+        for (Package p : pkgs) {

+            if (p instanceof SourcePackage) {

+                SourcePackage sp = (SourcePackage) p;

+                String v = sp.getVersion().getApiString();

+                sourceVersion.add(v);

+            }

+        }

+        assertEquals(

+                "[42, 2, 1]",

+                Arrays.toString(sourceVersion.toArray()));

+    }

+

+    /**

      * Returns an SdkLib file resource as a {@link ByteArrayInputStream},

      * which has the advantage that we can use {@link InputStream#reset()} on it

      * at any time to read it multiple times.

diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/ValidateAddonXmlTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/ValidateAddonXmlTest.java
index c90fc90..d88d566 100755
--- a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/ValidateAddonXmlTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/ValidateAddonXmlTest.java
@@ -120,7 +120,7 @@
         handler.verify();

     }

 

-    /** Validate a valid sample using namespace version 2 using an InputStream */

+    /** Validate a valid sample using namespace version 3 using an InputStream */

     public void testValidateLocalAddonFile3() throws Exception {

         InputStream xmlStream = this.getClass().getResourceAsStream(

                     "/com/android/sdklib/testdata/addon_sample_3.xml");

@@ -132,6 +132,18 @@
         handler.verify();

     }

 

+    /** Validate a valid sample using namespace version 4 using an InputStream */

+    public void testValidateLocalAddonFile4() throws Exception {

+        InputStream xmlStream = this.getClass().getResourceAsStream(

+                    "/com/android/sdklib/testdata/addon_sample_4.xml");

+        Source source = new StreamSource(xmlStream);

+

+        CaptureErrorHandler handler = new CaptureErrorHandler();

+        Validator validator = getAddonValidator(4, handler);

+        validator.validate(source);

+        handler.verify();

+    }

+

     // IMPORTANT: each time you add a test here, you should add a corresponding

     // test in SdkAddonSourceTest to validate the XML content is parsed correctly.

 

@@ -148,7 +160,9 @@
             OPEN_TAG_ADDON +

             "<r:license id=\"lic1\"> some license </r:license> " +

             "<r:add-on> <r:uses-license ref=\"lic1\" /> <r:revision>1</r:revision> " +

-            "<r:name>AddonName</r:name> <r:vendor>AddonVendor</r:vendor> <r:api-level>42</r:api-level> " +

+            "<r:name-id>AddonName</r:name-id> <r:name-display>The Addon Name</r:name-display> " +

+            "<r:vendor-id>AddonVendor</r:vendor-id> <r:vendor-display>The Addon Vendor</r:vendor-display> " +

+            "<r:api-level>42</r:api-level> " +

             "<r:codename>Addons do not support codenames</r:codenames> " +

             "<r:libs><r:lib><r:name>com.example.LibName</r:name></r:lib></r:libs> " +

             "<r:archives> <r:archive os=\"any\"> <r:size>1</r:size> <r:checksum>2822ae37115ebf13412bbef91339ee0d9454525e</r:checksum> " +

@@ -170,4 +184,29 @@
         // If we get here, the validator has not failed as we expected it to.

         fail();

     }

+

+    /** A document with a slash in an extra path. */

+    public void testExtraPathWithSlash() throws Exception {

+        String document = "<?xml version=\"1.0\"?>" +

+            OPEN_TAG_ADDON +

+            "<r:extra> <r:revision>1</r:revision> <r:path>path/cannot\\contain\\segments</r:path> " +

+            "<r:archives> <r:archive os=\"any\"> <r:size>1</r:size> <r:checksum>2822ae37115ebf13412bbef91339ee0d9454525e</r:checksum> " +

+            "<r:url>url</r:url> </r:archive> </r:archives> </r:extra>" +

+            CLOSE_TAG_ADDON;

+

+        Source source = new StreamSource(new StringReader(document));

+

+        // don't capture the validator errors, we want it to fail and catch the exception

+        Validator validator = getAddonValidator(SdkAddonConstants.NS_LATEST_VERSION, null);

+        try {

+            validator.validate(source);

+        } catch (SAXParseException e) {

+            // We expect a parse error referring to this grammar rule

+            assertRegex("cvc-pattern-valid: Value 'path/cannot\\\\contain\\\\segments' is not facet-valid with respect to pattern.*",

+                    e.getMessage());

+            return;

+        }

+        // If we get here, the validator has not failed as we expected it to.

+        fail();

+    }

 }

diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/ValidateRepositoryXmlTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/ValidateRepositoryXmlTest.java
index 2752c32..ce21650 100755
--- a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/ValidateRepositoryXmlTest.java
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/ValidateRepositoryXmlTest.java
@@ -298,12 +298,11 @@
         fail();

     }

 

-    /** A document a slash in an extra path. */

+    /** The latest XSD repository-6 should fail when an 'extra' is present. */

     public void testExtraPathWithSlash() throws Exception {

-        // we define a license named "lic1" and then reference "lic2" instead

         String document = "<?xml version=\"1.0\"?>" +

             OPEN_TAG_REPO +

-            "<r:extra> <r:revision>1</r:revision> <r:path>path/cannot\\contain\\segments</r:path> " +

+            "<r:extra> <r:revision>1</r:revision> <r:path>path</r:path> " +

             "<r:archives> <r:archive os=\"any\"> <r:size>1</r:size> <r:checksum>2822ae37115ebf13412bbef91339ee0d9454525e</r:checksum> " +

             "<r:url>url</r:url> </r:archive> </r:archives> </r:extra>" +

             CLOSE_TAG_REPO;

@@ -316,7 +315,7 @@
             validator.validate(source);

         } catch (SAXParseException e) {

             // We expect a parse error referring to this grammar rule

-            assertRegex("cvc-pattern-valid: Value 'path/cannot\\\\contain\\\\segments' is not facet-valid with respect to pattern.*",

+            assertRegex("cvc-complex-type.2.4.a: Invalid content was found starting with element 'r:extra'.*",

                     e.getMessage());

             return;

         }

diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addon_sample_4.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addon_sample_4.xml
new file mode 100755
index 0000000..6fba439
--- /dev/null
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addon_sample_4.xml
@@ -0,0 +1,203 @@
+<?xml version="1.0"?>
+<!--
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<sdk:sdk-addon
+    xmlns:sdk="http://schemas.android.com/sdk/android/addon/4">
+
+    <!-- Define a couple of licenses. These will be referenced by uses-license later. -->
+
+    <sdk:license type="text" id="license1">
+        This is the license
+        for this platform.
+    </sdk:license>
+
+    <sdk:license id="license2">
+        Licenses are only of type 'text' right now, so this is implied.
+    </sdk:license>
+
+    <!-- Inner elements must be either platform, add-on, doc or tool.
+         There can be 0 or more of each, in any order. -->
+
+    <sdk:add-on>
+        <sdk:name-id>My_First_add-on</sdk:name-id>
+        <sdk:name-display>My First add-on</sdk:name-display>
+
+        <sdk:vendor-id>John_Doe</sdk:vendor-id>
+        <sdk:vendor-display>John Doe</sdk:vendor-display>
+
+        <sdk:api-level>1</sdk:api-level>
+        <sdk:revision>1</sdk:revision>
+        <sdk:uses-license ref="license2" />
+        <sdk:description>Some optional description</sdk:description>
+        <sdk:desc-url>http://www.example.com/myfirstaddon</sdk:desc-url>
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>http://www.example.com/add-ons/first.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <!-- The libs node is mandatory, however it can be empty. -->
+        <sdk:libs>
+            <sdk:lib>
+                <sdk:name>android.blah.somelib</sdk:name>
+                <sdk:description>The description for this library.</sdk:description>
+            </sdk:lib>
+            <sdk:lib>
+                <!-- sdk:description is optional, name is not -->
+                <sdk:name>com.android.mymaps</sdk:name>
+            </sdk:lib>
+        </sdk:libs>
+        <sdk:layoutlib>
+            <sdk:api>5</sdk:api>
+            <sdk:revision>0</sdk:revision>
+        </sdk:layoutlib>
+    </sdk:add-on>
+
+    <sdk:add-on>
+        <sdk:name-id>My_Second_add-on</sdk:name-id>
+        <sdk:name-display>My Second add-on</sdk:name-display>
+
+        <sdk:vendor-id>John_Deer</sdk:vendor-id>
+        <sdk:vendor-display>John Deer</sdk:vendor-display>
+
+        <sdk:api-level>2</sdk:api-level>
+        <sdk:revision>42</sdk:revision>
+        <sdk:archives>
+            <sdk:archive os="windows">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/second-42-win.zip</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="linux">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/second-42-linux.tar.bz2</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <sdk:libs>
+            <sdk:lib>
+                <sdk:name>android.blah.somelib</sdk:name>
+                <sdk:description>The description for this library.</sdk:description>
+            </sdk:lib>
+            <sdk:lib>
+                <sdk:name>com.android.mymaps</sdk:name>
+            </sdk:lib>
+        </sdk:libs>
+        <sdk:uses-license ref="license2" />
+        <!-- No layoutlib element in this package. It's optional. -->
+    </sdk:add-on>
+
+    <sdk:add-on>
+        <sdk:name-id>no_libs</sdk:name-id>
+        <sdk:name-display>This add-on has no libraries</sdk:name-display>
+
+        <sdk:vendor-id>Joe_Bar</sdk:vendor-id>
+        <sdk:vendor-display>Joe Bar</sdk:vendor-display>
+
+        <sdk:uses-license ref="license2" />
+        <sdk:api-level>4</sdk:api-level>
+        <sdk:revision>3</sdk:revision>
+        <sdk:archives>
+            <sdk:archive os="any" arch="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/imnotanarchiveimadoctorjim.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <!-- The libs node is mandatory, however it can be empty. -->
+        <sdk:libs />
+        <sdk:layoutlib>
+            <sdk:api>3</sdk:api>
+            <sdk:revision>42</sdk:revision>
+        </sdk:layoutlib>
+    </sdk:add-on>
+
+    <sdk:extra>
+        <sdk:name-display>Random name, not an id!</sdk:name-display>
+
+        <sdk:vendor-id>cyclop</sdk:vendor-id>
+        <sdk:vendor-display>The big bus</sdk:vendor-display>
+
+        <sdk:path>usb_driver</sdk:path>
+        <sdk:uses-license ref="license2" />
+        <sdk:revision>43</sdk:revision>
+        <sdk:archives>
+            <sdk:archive os="any" arch="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/extraduff.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <sdk:description>An Extra package for the USB driver, it will install in $SDK/usb_driver</sdk:description>
+        <sdk:desc-url>http://www.example.com/extra.html</sdk:desc-url>
+        <sdk:min-tools-rev>3</sdk:min-tools-rev>
+        <sdk:obsolete/>
+    </sdk:extra>
+
+    <sdk:extra>
+        <sdk:name-display>Yet another extra, by Android</sdk:name-display>
+
+        <sdk:vendor-id>android_vendor</sdk:vendor-id>
+        <sdk:vendor-display>Android Vendor</sdk:vendor-display>
+
+        <sdk:path>extra_api_dep</sdk:path>
+        <sdk:uses-license ref="license2" />
+        <sdk:revision>2</sdk:revision>
+        <sdk:archives>
+            <sdk:archive os="any" arch="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/extra_mega_duff.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <sdk:description>Some extra package that has a min-api-level of 42</sdk:description>
+        <sdk:desc-url>http://www.example.com/extra.html</sdk:desc-url>
+        <sdk:min-tools-rev>3</sdk:min-tools-rev>
+        <sdk:min-api-level>42</sdk:min-api-level>
+        <sdk:project-files>
+            <sdk:path>v8/veggies_8.jar</sdk:path>
+            <sdk:path>root.jar</sdk:path>
+            <sdk:path>dir1/dir 2 with space/mylib.jar</sdk:path>
+        </sdk:project-files>
+        <sdk:old-paths>path1;old_path2;oldPath3</sdk:old-paths>
+    </sdk:extra>
+
+    <sdk:extra>
+        <sdk:name-display>. -..- - .-. .-</sdk:name-display>
+
+        <sdk:vendor-id>____</sdk:vendor-id>
+        <sdk:vendor-display>____</sdk:vendor-display>
+
+        <sdk:path>____</sdk:path>
+        <sdk:uses-license ref="license2" />
+        <sdk:revision>2</sdk:revision>
+        <sdk:archives>
+            <sdk:archive os="any" arch="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/extra_mega_duff.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <sdk:description>Some extra package that has a min-api-level of 42</sdk:description>
+        <sdk:desc-url>http://www.example.com/extra.html</sdk:desc-url>
+        <sdk:min-tools-rev>3</sdk:min-tools-rev>
+        <sdk:min-api-level>42</sdk:min-api-level>
+        <sdk:obsolete></sdk:obsolete>
+        <!-- No project-files element in this package. -->
+    </sdk:extra>
+
+</sdk:sdk-addon>
diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_6.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_6.xml
new file mode 100755
index 0000000..f29ebdf
--- /dev/null
+++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_6.xml
@@ -0,0 +1,312 @@
+<?xml version="1.0"?>
+<!--
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<sdk:sdk-repository
+    xmlns:sdk="http://schemas.android.com/sdk/android/repository/6">
+
+    <!-- Define a couple of licenses. These will be referenced by uses-license later. -->
+
+    <sdk:license type="text" id="license1">
+        This is the license
+        for this platform.
+    </sdk:license>
+
+    <sdk:license id="license2">
+        Licenses are only of type 'text' right now, so this is implied.
+    </sdk:license>
+
+    <!-- Inner elements must be either platform, add-on, doc or tool.
+         There can be 0 or more of each, in any order. -->
+
+    <sdk:platform>
+        <sdk:version>1.0</sdk:version>
+        <sdk:api-level>1</sdk:api-level>
+        <sdk:revision>3</sdk:revision>
+        <sdk:uses-license ref="license1" />
+        <sdk:description>Some optional description</sdk:description>
+        <sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url>
+        <sdk:release-note>This is an optional release note
+            for this package. It's a free multi-line text.
+        </sdk:release-note>
+        <sdk:release-url>http://some/url/for/the/release/note.html</sdk:release-url>
+        <sdk:min-tools-rev>2</sdk:min-tools-rev>
+        <!-- The archives node is mandatory and it cannot be empty. -->
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>http://www.example.com/files/plat1.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <sdk:layoutlib>
+            <sdk:api>5</sdk:api>
+            <sdk:revision>0</sdk:revision>
+        </sdk:layoutlib>
+        <sdk:included-abi>armeabi</sdk:included-abi>
+    </sdk:platform>
+
+    <sdk:doc>
+        <sdk:api-level>1</sdk:api-level>
+        <sdk:revision>1</sdk:revision>
+        <!-- the license element is not mandatory. -->
+        <sdk:description>Some optional description</sdk:description>
+        <sdk:desc-url>http://www.example.com/docs.html</sdk:desc-url>
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>http://www.example.com/docs/docs1.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+    </sdk:doc>
+
+    <sdk:source>
+        <sdk:api-level>1</sdk:api-level>
+        <sdk:revision>1</sdk:revision>
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>65535</sdk:size>
+                <sdk:checksum type="sha1">1234ae37115ebf13412bbef91339ee0d94541234</sdk:checksum>
+                <sdk:url>http://www.example.com/plat1/sources1.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+    </sdk:source>
+
+    <sdk:platform>
+        <sdk:version>1.1</sdk:version>
+        <sdk:api-level>2</sdk:api-level>
+        <sdk:revision>12</sdk:revision>
+        <sdk:uses-license ref="license1" />
+        <!-- sdk:description and sdk:desc-url are optional -->
+        <sdk:archives>
+            <sdk:archive os="windows">
+                <!--  arch attribute is optional -->
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/platform-2-12-win.zip</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="macosx" arch="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/platform-2-12-mac.zip</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="macosx" arch="ppc">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/platform-2-12-mac.zip</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="linux" arch="x86">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/platform-2-12-linux.tar.bz2</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="linux" arch="x86_64">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/platform-2-12-linux.tar.bz2</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <sdk:layoutlib>
+            <sdk:api>5</sdk:api>
+            <sdk:revision>31415</sdk:revision>
+        </sdk:layoutlib>
+        <sdk:included-abi>x86</sdk:included-abi>
+    </sdk:platform>
+
+    <sdk:system-image>
+        <sdk:api-level>2</sdk:api-level>
+        <sdk:revision>1</sdk:revision>
+        <sdk:abi>x86</sdk:abi>
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>65535</sdk:size>
+                <sdk:checksum type="sha1">1234ae37115ebf13412bbef91339ee0d94541234</sdk:checksum>
+                <sdk:url>http://www.example.com/plat1/x86/image1.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+    </sdk:system-image>
+
+    <sdk:system-image>
+        <sdk:api-level>2</sdk:api-level>
+        <sdk:revision>2</sdk:revision>
+        <sdk:abi>armeabi-v7a</sdk:abi>
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>65534</sdk:size>
+                <sdk:checksum type="sha1">1234ae37115ebf13412bbef91339ee0d94541234</sdk:checksum>
+                <sdk:url>http://www.example.com/plat1/armv7/image2.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+    </sdk:system-image>
+
+    <sdk:source>
+        <sdk:api-level>2</sdk:api-level>
+        <sdk:revision>2</sdk:revision>
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>65534</sdk:size>
+                <sdk:checksum type="sha1">1234ae37115ebf13412bbef91339ee0d94541234</sdk:checksum>
+                <sdk:url>http://www.example.com/plat1/sources2.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+    </sdk:source>
+
+   <sdk:platform>
+        <sdk:version>Pastry</sdk:version>
+        <sdk:api-level>5</sdk:api-level>
+        <sdk:codename>Pastry</sdk:codename>
+        <sdk:revision>3</sdk:revision>
+        <sdk:uses-license ref="license1" />
+        <sdk:description>Preview version for Pastry</sdk:description>
+        <sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url>
+        <!-- The archives node is mandatory and it cannot be empty. -->
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>http://www.example.com/files/plat1.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <sdk:layoutlib>
+            <sdk:api>1</sdk:api>
+        </sdk:layoutlib>
+    </sdk:platform>
+
+    <sdk:tool>
+        <sdk:revision>1</sdk:revision>
+        <sdk:description>Some optional description</sdk:description>
+        <sdk:desc-url>http://www.example.com/tools.html</sdk:desc-url>
+        <sdk:uses-license ref="license1" />
+        <sdk:min-platform-tools-rev>4</sdk:min-platform-tools-rev>
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>http://www.example.com/files/tools1.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+    </sdk:tool>
+
+    <sdk:doc>
+        <sdk:api-level>2</sdk:api-level>
+        <sdk:revision>42</sdk:revision>
+        <sdk:uses-license ref="license2" />
+        <sdk:archives>
+            <sdk:archive os="windows">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/docs/2.zip</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="linux">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/docs2-linux.tar.bz2</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="macosx">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/docs2-mac.tar.bz2</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+    </sdk:doc>
+
+    <sdk:tool>
+        <sdk:revision>42</sdk:revision>
+        <sdk:uses-license ref="license1" />
+        <sdk:min-platform-tools-rev>3</sdk:min-platform-tools-rev>
+        <sdk:archives>
+            <sdk:archive os="windows">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/tools/2.zip</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="linux">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/tools2-linux.tar.bz2</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="macosx">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/tools2-mac.tar.bz2</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+    </sdk:tool>
+
+    <sdk:platform-tool>
+        <sdk:revision>3</sdk:revision>
+        <sdk:uses-license ref="license1" />
+        <sdk:archives>
+            <sdk:archive os="windows">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">3822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/platform-tools/2.zip</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="linux">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">3822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/platform-tools2-linux.tar.bz2</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="macosx">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">3822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/platform-tools2-mac.tar.bz2</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+    </sdk:platform-tool>
+
+    <sdk:sample>
+        <sdk:api-level>14</sdk:api-level>
+        <sdk:revision>24</sdk:revision>
+        <sdk:archives>
+            <sdk:archive os="any" arch="any">
+                <sdk:size>65537</sdk:size>
+                <sdk:checksum type="sha1">3822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/sample_duff.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <sdk:description>Some sample package</sdk:description>
+        <sdk:desc-url>http://www.example.com/sample.html</sdk:desc-url>
+        <sdk:min-tools-rev>5</sdk:min-tools-rev>
+        <sdk:obsolete>This is obsolete</sdk:obsolete>
+    </sdk:sample>
+
+    <sdk:system-image>
+        <sdk:api-level>42</sdk:api-level>
+        <sdk:revision>12</sdk:revision>
+        <sdk:abi>armeabi</sdk:abi>
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>1234</sdk:size>
+                <sdk:checksum type="sha1">12345637115ebf13412bbef91339ee0d94541234</sdk:checksum>
+                <sdk:url>http://www.example.com/plat42/86/image12.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+    </sdk:system-image>
+
+    <sdk:source>
+        <sdk:api-level>42</sdk:api-level>
+        <sdk:revision>12</sdk:revision>
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>1234</sdk:size>
+                <sdk:checksum type="sha1">12345637115ebf13412bbef91339ee0d94541234</sdk:checksum>
+                <sdk:url>http://www.example.com/plat42/source12.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+    </sdk:source>
+
+</sdk:sdk-repository>
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterNoWindow.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterNoWindow.java
index 7b94c94..89f084c 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterNoWindow.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SdkUpdaterNoWindow.java
@@ -347,11 +347,37 @@
         @Override
         public boolean displayPrompt(final String title, final String message) {
             // TODO Make it interactive if mForce==false
-            mSdkLog.printf("\n%s\n%s\n[y/n] => %s\n",
+            mSdkLog.printf("\n%1$s\n%2$s\n%3$s",        //$NON-NLS-1$
                     title,
                     message,
-                    mForce ? "yes" : "no (use --force to override)");
-            return mForce;
+                    mForce ? "--force used, will reply yes\n" :
+                             "Note: you  can use --force to override to yes.\n");
+            if (mForce) {
+                return true;
+            }
+
+            while (true) {
+                mSdkLog.printf("%1$s", "[y/n] =>");     //$NON-NLS-1$
+                try {
+                    byte[] readBuffer = new byte[2048];
+                    String reply = readLine(readBuffer).trim();
+                    mSdkLog.printf("\n");               //$NON-NLS-1$
+                    if (reply.length() > 0 && reply.length() <= 3) {
+                        char c = reply.charAt(0);
+                        if (c == 'y' || c == 'Y') {
+                            return true;
+                        } else if (c == 'n' || c == 'N') {
+                            return false;
+                        }
+                    }
+                    mSdkLog.printf("Unknown reply '%s'. Please use y[es]/n[o].\n");  //$NON-NLS-1$
+
+                } catch (IOException e) {
+                    // Exception. Be conservative and say no.
+                    mSdkLog.printf("\n");               //$NON-NLS-1$
+                    return false;
+                }
+            }
         }
 
         /**
@@ -417,6 +443,14 @@
             return new UserCredentials(login, password, workstation, domain);
         }
 
+        /**
+         * Reads current console input in the given buffer.
+         *
+         * @param buffer Buffer to hold the user input. Must be larger than the largest
+         *   expected input. Cannot be null.
+         * @return A new string. May be empty but not null.
+         * @throws IOException in case the buffer isn't long enough.
+         */
         private String readLine(byte[] buffer) throws IOException {
             int count = System.in.read(buffer);
 
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java
index dce4110..2311df2 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/AdtUpdateDialog.java
@@ -309,7 +309,7 @@
             boolean accept(Package pkg) {

                 if (pkg instanceof ExtraPackage) {

                     ExtraPackage ep = (ExtraPackage) pkg;

-                    if (ep.getVendor().equals(mVendor)) {

+                    if (ep.getVendorId().equals(mVendor)) {

                         // Check actual extra <path> field first

                         if (ep.getPath().equals(mPath)) {

                             return true;

diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java
index 49395ef..944dffb 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogic.java
@@ -221,8 +221,8 @@
                 Package p = item.getMainPackage();
                 if (p instanceof ExtraPackage && item.getState() == PkgState.NEW) {
                     ExtraPackage ep = (ExtraPackage) p;
-                    if (ep.getVendor().equals("google") &&          //$NON-NLS-1$
-                            ep.getPath().equals("usb_driver")) {    //$NON-NLS-1$
+                    if (ep.getVendorId().equals("google") &&            //$NON-NLS-1$
+                            ep.getPath().equals("usb_driver")) {        //$NON-NLS-1$
                         item.setChecked(true);
                     }
                 }
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java
index 58d7daa..d3dc7b6 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java
@@ -519,7 +519,9 @@
                 value = button.getSelection();
             }
 
-            item.setSelection(value);
+            if (!item.isDisposed()) {
+                item.setSelection(value);
+            }
         }
 
     }
@@ -1564,12 +1566,25 @@
             if (element instanceof IDescription) {
                 String s = ((IDescription) element).getLongDescription();
                 if (element instanceof Package) {
-                    SdkSource src = ((Package) element).getParentSource();
+                    Package p = (Package) element;
+
+                    if (!p.isLocal()) {
+                        // For non-installed item, try to find a download size
+                        for (Archive a : p.getArchives()) {
+                            if (!a.isLocal() && a.isCompatible()) {
+                                s += '\n' + a.getSizeDescription();
+                                break;
+                            }
+                        }
+                    }
+
+                    // Display info about where this package comes/came from
+                    SdkSource src = p.getParentSource();
                     if (src != null) {
                         try {
                             URL url = new URL(src.getUrl());
                             String host = url.getHost();
-                            if (((Package) element).isLocal()) {
+                            if (p.isLocal()) {
                                 s += String.format("\nInstalled from %1$s", host);
                             } else {
                                 s += String.format("\nProvided by %1$s", host);
diff --git a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogicTest.java b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogicTest.java
index eafe5ac..cf4b232 100755
--- a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogicTest.java
+++ b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/sdkman2/PackagesDiffLogicTest.java
@@ -326,8 +326,8 @@
                 "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
                 "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=2>\n" +
-                "-- <INSTALLED, pkg:Android USB Driver package, revision 5>\n" +
-                "-- <INSTALLED, pkg:Carrier Custom Rom package, revision 1>\n",
+                "-- <INSTALLED, pkg:Android USB Driver, revision 5>\n" +
+                "-- <INSTALLED, pkg:Carrier Custom Rom, revision 1>\n",
                 getTree(m, true /*displaySortByApi*/));
 
         assertEquals(
@@ -335,8 +335,8 @@
                 "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
                 "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
                 "PkgCategorySource <source=repo2 (example.com), #items=2>\n" +
-                "-- <INSTALLED, pkg:Android USB Driver package, revision 5>\n" +
-                "-- <INSTALLED, pkg:Carrier Custom Rom package, revision 1>\n",
+                "-- <INSTALLED, pkg:Android USB Driver, revision 5>\n" +
+                "-- <INSTALLED, pkg:Carrier Custom Rom, revision 1>\n",
                 getTree(m, false /*displaySortByApi*/));
     }
 
@@ -371,8 +371,8 @@
                 "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
                 "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=2>\n" +
-                "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
-                "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
+                "-- <INSTALLED, pkg:Android USB Driver, revision 4, updated by:Android USB Driver, revision 5>\n" +
+                "-- <NEW, pkg:Carrier Custom Rom, revision 1>\n",
                 getTree(m, true /*displaySortByApi*/));
 
         // Next update adds platforms and addon, sorted in a category based on their API level
@@ -423,15 +423,15 @@
                 "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n" +
                 "PkgCategoryApi <API=API 2, label=Android android-2 (API 2), #items=3>\n" +
                 "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
-                "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 9>\n" +
-                "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
+                "-- <NEW, pkg:The addon B from vendor 2, Android API 2, revision 9>\n" +
+                "-- <NEW, pkg:The addon C from vendor 2, Android API 2, revision 9>\n" +
                 "PkgCategoryApi <API=API 1, label=Android android-1 (API 1), #items=3>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
-                "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n" +
-                "-- <INSTALLED, pkg:addon D by vendor 1, Android API 1, revision 10, updated by:addon D by vendor 1, Android API 1, revision 13>\n" +
+                "-- <INSTALLED, pkg:The addon A from vendor 1, Android API 1, revision 5, updated by:The addon A from vendor 1, Android API 1, revision 6>\n" +
+                "-- <INSTALLED, pkg:The addon D from vendor 1, Android API 1, revision 10, updated by:The addon D from vendor 1, Android API 1, revision 13>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=2>\n" +
-                "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
-                "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
+                "-- <INSTALLED, pkg:Android USB Driver, revision 4, updated by:Android USB Driver, revision 5>\n" +
+                "-- <NEW, pkg:Carrier Custom Rom, revision 1>\n",
                 getTree(m, true /*displaySortByApi*/));
 
         // Reloading the same thing should have no impact except for the update methods
@@ -479,15 +479,15 @@
                 "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n" +
                 "PkgCategoryApi <API=API 2, label=Android android-2 (API 2), #items=3>\n" +
                 "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
-                "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 9>\n" +
-                "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
+                "-- <NEW, pkg:The addon B from vendor 2, Android API 2, revision 9>\n" +
+                "-- <NEW, pkg:The addon C from vendor 2, Android API 2, revision 9>\n" +
                 "PkgCategoryApi <API=API 1, label=Android android-1 (API 1), #items=3>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
-                "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n" +
-                "-- <INSTALLED, pkg:addon D by vendor 1, Android API 1, revision 10, updated by:addon D by vendor 1, Android API 1, revision 13>\n" +
+                "-- <INSTALLED, pkg:The addon A from vendor 1, Android API 1, revision 5, updated by:The addon A from vendor 1, Android API 1, revision 6>\n" +
+                "-- <INSTALLED, pkg:The addon D from vendor 1, Android API 1, revision 10, updated by:The addon D from vendor 1, Android API 1, revision 13>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=2>\n" +
-                "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
-                "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
+                "-- <INSTALLED, pkg:Android USB Driver, revision 4, updated by:Android USB Driver, revision 5>\n" +
+                "-- <NEW, pkg:Carrier Custom Rom, revision 1>\n",
                 getTree(m, true /*displaySortByApi*/));
     }
 
@@ -714,8 +714,8 @@
                 "PkgCategorySource <source=repo1 (1.example.com), #items=4>\n" +
                 "-- <INSTALLED, pkg:Android SDK Tools, revision 10>\n" +
                 "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
-                "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
-                "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
+                "-- <INSTALLED, pkg:Android USB Driver, revision 4, updated by:Android USB Driver, revision 5>\n" +
+                "-- <NEW, pkg:Carrier Custom Rom, revision 1>\n",
                 getTree(m, false /*displaySortByApi*/));
 
         // Next update adds platforms and addon, sorted in a category based on their API level
@@ -766,13 +766,13 @@
                 "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n" +
                 "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
-                "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
-                "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n" +
+                "-- <INSTALLED, pkg:Android USB Driver, revision 4, updated by:Android USB Driver, revision 5>\n" +
+                "-- <NEW, pkg:Carrier Custom Rom, revision 1>\n" +
                 "PkgCategorySource <source=repo2 (2.example.com), #items=4>\n" +
-                "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 9>\n" +
-                "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
-                "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n" +
-                "-- <INSTALLED, pkg:addon D by vendor 1, Android API 1, revision 10, updated by:addon D by vendor 1, Android API 1, revision 13>\n",
+                "-- <NEW, pkg:The addon B from vendor 2, Android API 2, revision 9>\n" +
+                "-- <NEW, pkg:The addon C from vendor 2, Android API 2, revision 9>\n" +
+                "-- <INSTALLED, pkg:The addon A from vendor 1, Android API 1, revision 5, updated by:The addon A from vendor 1, Android API 1, revision 6>\n" +
+                "-- <INSTALLED, pkg:The addon D from vendor 1, Android API 1, revision 10, updated by:The addon D from vendor 1, Android API 1, revision 13>\n",
                 getTree(m, false /*displaySortByApi*/));
 
         // Reloading the same thing should have no impact except for the update methods
@@ -820,13 +820,13 @@
                 "-- <INSTALLED, pkg:SDK Platform Android android-3, API 3, revision 6>\n" +
                 "-- <NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
-                "-- <INSTALLED, pkg:Android USB Driver package, revision 4, updated by:Android USB Driver package, revision 5>\n" +
-                "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n" +
+                "-- <INSTALLED, pkg:Android USB Driver, revision 4, updated by:Android USB Driver, revision 5>\n" +
+                "-- <NEW, pkg:Carrier Custom Rom, revision 1>\n" +
                 "PkgCategorySource <source=repo2 (2.example.com), #items=4>\n" +
-                "-- <NEW, pkg:addon B by vendor 2, Android API 2, revision 9>\n" +
-                "-- <NEW, pkg:addon C by vendor 2, Android API 2, revision 9>\n" +
-                "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 5, updated by:addon A by vendor 1, Android API 1, revision 6>\n" +
-                "-- <INSTALLED, pkg:addon D by vendor 1, Android API 1, revision 10, updated by:addon D by vendor 1, Android API 1, revision 13>\n",
+                "-- <NEW, pkg:The addon B from vendor 2, Android API 2, revision 9>\n" +
+                "-- <NEW, pkg:The addon C from vendor 2, Android API 2, revision 9>\n" +
+                "-- <INSTALLED, pkg:The addon A from vendor 1, Android API 1, revision 5, updated by:The addon A from vendor 1, Android API 1, revision 6>\n" +
+                "-- <INSTALLED, pkg:The addon D from vendor 1, Android API 1, revision 10, updated by:The addon D from vendor 1, Android API 1, revision 13>\n",
                 getTree(m, false /*displaySortByApi*/));
     }
 
@@ -970,13 +970,13 @@
                 "-- < * NEW, pkg:SDK Platform Android android-2, API 2, revision 4>\n" +
                 "-- < * NEW, pkg:ARM EABI System Image, Android API 2, revision 1>\n" +
                 "-- < * NEW, pkg:Intel x86 Atom System Image, Android API 2, revision 1>\n" +
-                "-- < * NEW, pkg:addon B by vendor 2, Android API 2, revision 7>\n" +
+                "-- < * NEW, pkg:The addon B from vendor 2, Android API 2, revision 7>\n" +
                 "PkgCategoryApi <API=API 1, label=Android android-1 (API 1), #items=2>\n" +
                 "-- <NEW, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
-                "-- <NEW, pkg:addon A by vendor 1, Android API 1, revision 5>\n" +
+                "-- <NEW, pkg:The addon A from vendor 1, Android API 1, revision 5>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=2>\n" +
-                "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n" +
-                "-- <NEW, pkg:Google USB Driver package, revision 5>\n",
+                "-- <NEW, pkg:Carrier Custom Rom, revision 1>\n" +
+                "-- <NEW, pkg:Google USB Driver, revision 5>\n",
                 getTree(m, true /*displaySortByApi*/));
         assertEquals(
                 "PkgCategorySource <source=repo1 (1.example.com), #items=7>\n" +
@@ -986,11 +986,11 @@
                 "-- <NEW, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
                 "-- < * NEW, pkg:ARM EABI System Image, Android API 2, revision 1>\n" +
                 "-- < * NEW, pkg:Intel x86 Atom System Image, Android API 2, revision 1>\n" +
-                "-- <NEW, pkg:Google USB Driver package, revision 5>\n" +
+                "-- <NEW, pkg:Google USB Driver, revision 5>\n" +
                 "PkgCategorySource <source=repo2 (2.example.com), #items=3>\n" +
-                "-- < * NEW, pkg:addon B by vendor 2, Android API 2, revision 7>\n" +
-                "-- <NEW, pkg:addon A by vendor 1, Android API 1, revision 5>\n" +
-                "-- <NEW, pkg:Carrier Custom Rom package, revision 1>\n",
+                "-- < * NEW, pkg:The addon B from vendor 2, Android API 2, revision 7>\n" +
+                "-- <NEW, pkg:The addon A from vendor 1, Android API 1, revision 5>\n" +
+                "-- <NEW, pkg:Carrier Custom Rom, revision 1>\n",
                 getTree(m, false /*displaySortByApi*/));
 
         // We don't install the USB driver by default on Mac or Linux, only on Windows
@@ -1005,11 +1005,11 @@
         assertEquals(
                 "PkgCategoryApi <API=TOOLS, label=Tools, #items=0>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=1>\n" +
-                "-- <NEW, pkg:Google USB Driver package, revision 5>\n",
+                "-- <NEW, pkg:Google USB Driver, revision 5>\n",
                 getTree(m, true /*displaySortByApi*/));
         assertEquals(
                 "PkgCategorySource <source=repo1 (1.example.com), #items=1>\n" +
-                "-- <NEW, pkg:Google USB Driver package, revision 5>\n",
+                "-- <NEW, pkg:Google USB Driver, revision 5>\n",
                 getTree(m, false /*displaySortByApi*/));
 
         m.clear();
@@ -1023,11 +1023,11 @@
         assertEquals(
                 "PkgCategoryApi <API=TOOLS, label=Tools, #items=0>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=1>\n" +
-                "-- <NEW, pkg:Google USB Driver package, revision 5>\n",
+                "-- <NEW, pkg:Google USB Driver, revision 5>\n",
                 getTree(m, true /*displaySortByApi*/));
         assertEquals(
                 "PkgCategorySource <source=repo1 (1.example.com), #items=1>\n" +
-                "-- <NEW, pkg:Google USB Driver package, revision 5>\n",
+                "-- <NEW, pkg:Google USB Driver, revision 5>\n",
                 getTree(m, false /*displaySortByApi*/));
 
         m.clear();
@@ -1041,11 +1041,11 @@
         assertEquals(
                 "PkgCategoryApi <API=TOOLS, label=Tools, #items=0>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=1>\n" +
-                "-- < * NEW, pkg:Google USB Driver package, revision 5>\n",
+                "-- < * NEW, pkg:Google USB Driver, revision 5>\n",
                 getTree(m, true /*displaySortByApi*/));
         assertEquals(
                 "PkgCategorySource <source=repo1 (1.example.com), #items=1>\n" +
-                "-- < * NEW, pkg:Google USB Driver package, revision 5>\n",
+                "-- < * NEW, pkg:Google USB Driver, revision 5>\n",
                 getTree(m, false /*displaySortByApi*/));
 
     }
@@ -1213,8 +1213,8 @@
                 "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
                 "PkgCategoryApi <API=API 1, label=Android android-1 (API 1), #items=3>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
-                "-- <NEW, pkg:addon A by vendor 1, Android API 1, revision 5>\n" + // from src2+3+4
-                "-- <NEW, pkg:addon B by vendor 1, Android API 1, revision 7>\n" + // from src3+4
+                "-- <NEW, pkg:The addon A from vendor 1, Android API 1, revision 5>\n" + // from src2+3+4
+                "-- <NEW, pkg:The addon B from vendor 1, Android API 1, revision 7>\n" + // from src3+4
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=0>\n",
                 getTree(m, true /*displaySortByApi*/));
         // When sorting by source, the src4 packages are not listed at all since
@@ -1226,9 +1226,9 @@
                 "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
                 "PkgCategorySource <source=repo2 (example.com), #items=1>\n" +
-                "-- <NEW, pkg:addon A by vendor 1, Android API 1, revision 5>\n" + // from src2+3+4
+                "-- <NEW, pkg:The addon A from vendor 1, Android API 1, revision 5>\n" + // from src2+3+4
                 "PkgCategorySource <source=repo3 (example.com), #items=1>\n" +
-                "-- <NEW, pkg:addon B by vendor 1, Android API 1, revision 7>\n",  // from src3+4
+                "-- <NEW, pkg:The addon B from vendor 1, Android API 1, revision 7>\n",  // from src3+4
                 getTree(m, false /*displaySortByApi*/));
     }
 
@@ -1254,13 +1254,13 @@
         assertEquals(
                 "PkgCategoryApi <API=TOOLS, label=Tools, #items=0>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=2>\n" +
-                "-- <NEW, pkg:Vendor1 New Path2 package, revision 2>\n" +
-                "-- <INSTALLED, pkg:Vendor1 Old Path1 package, revision 1>\n",
+                "-- <NEW, pkg:Vendor1 New Path2, revision 2>\n" +
+                "-- <INSTALLED, pkg:Vendor1 Old Path1, revision 1>\n",
                 getTree(m, true /*displaySortByApi*/));
         assertEquals(
                 "PkgCategorySource <source=repo1 (example.com), #items=2>\n" +
-                "-- <NEW, pkg:Vendor1 New Path2 package, revision 2>\n" +
-                "-- <INSTALLED, pkg:Vendor1 Old Path1 package, revision 1>\n",
+                "-- <NEW, pkg:Vendor1 New Path2, revision 2>\n" +
+                "-- <INSTALLED, pkg:Vendor1 Old Path1, revision 1>\n",
                 getTree(m, false /*displaySortByApi*/));
 
         // Now, start again, but this time the new package uses the old-path attribute
@@ -1280,11 +1280,11 @@
         assertEquals(
                 "PkgCategoryApi <API=TOOLS, label=Tools, #items=0>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=1>\n" +
-                "-- <INSTALLED, pkg:Vendor1 Old Path1 package, revision 1, updated by:Vendor1 New Path2 package, revision 2>\n",
+                "-- <INSTALLED, pkg:Vendor1 Old Path1, revision 1, updated by:Vendor1 New Path2, revision 2>\n",
                 getTree(m, true /*displaySortByApi*/));
         assertEquals(
                 "PkgCategorySource <source=repo1 (example.com), #items=1>\n" +
-                "-- <INSTALLED, pkg:Vendor1 Old Path1 package, revision 1, updated by:Vendor1 New Path2 package, revision 2>\n",
+                "-- <INSTALLED, pkg:Vendor1 Old Path1, revision 1, updated by:Vendor1 New Path2, revision 2>\n",
                 getTree(m, false /*displaySortByApi*/));
     }
 
@@ -1313,14 +1313,14 @@
                 "PkgCategoryApi <API=TOOLS, label=Tools, #items=0>\n" +
                 "PkgCategoryApi <API=API 1, label=Android android-1 (API 1), #items=2>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
-                "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 4>\n" +
+                "-- <INSTALLED, pkg:The addon A from vendor 1, Android API 1, revision 4>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=0>\n",
                 getTree(m, true /*displaySortByApi*/));
         assertEquals(
                 "PkgCategorySource <source=repo1 (1.example.com), #items=1>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
                 "PkgCategorySource <source=repo2 (2.example.com), #items=1>\n" +
-                "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 4>\n",
+                "-- <INSTALLED, pkg:The addon A from vendor 1, Android API 1, revision 4>\n",
                 getTree(m, false /*displaySortByApi*/));
 
         // Now user deletes the platform on disk and reload.
@@ -1340,7 +1340,7 @@
                 "PkgCategoryApi <API=TOOLS, label=Tools, #items=0>\n" +
                 "PkgCategoryApi <API=API 1, label=Android android-1 (API 1), #items=2>\n" +
                 "-- <NEW, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
-                "-- <NEW, pkg:addon A by vendor 1, Android API 1, revision 4>\n" +
+                "-- <NEW, pkg:The addon A from vendor 1, Android API 1, revision 4>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=1>\n" +
                 "-- <INSTALLED, pkg:Broken package for API 1>\n",
                 getTree(m, true /*displaySortByApi*/));
@@ -1348,7 +1348,7 @@
                 "PkgCategorySource <source=repo1 (1.example.com), #items=1>\n" +
                 "-- <NEW, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
                 "PkgCategorySource <source=repo2 (2.example.com), #items=1>\n" +
-                "-- <NEW, pkg:addon A by vendor 1, Android API 1, revision 4>\n" +
+                "-- <NEW, pkg:The addon A from vendor 1, Android API 1, revision 4>\n" +
                 "PkgCategorySource <source=Local Packages (no.source), #items=1>\n" +
                 "-- <INSTALLED, pkg:Broken package for API 1>\n",
                 getTree(m, false /*displaySortByApi*/));
@@ -1370,14 +1370,14 @@
                 "PkgCategoryApi <API=TOOLS, label=Tools, #items=0>\n" +
                 "PkgCategoryApi <API=API 1, label=Android android-1 (API 1), #items=2>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
-                "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 4>\n" +
+                "-- <INSTALLED, pkg:The addon A from vendor 1, Android API 1, revision 4>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=0>\n",
                 getTree(m, true /*displaySortByApi*/));
         assertEquals(
                 "PkgCategorySource <source=repo1 (1.example.com), #items=1>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
                 "PkgCategorySource <source=repo2 (2.example.com), #items=1>\n" +
-                "-- <INSTALLED, pkg:addon A by vendor 1, Android API 1, revision 4>\n",
+                "-- <INSTALLED, pkg:The addon A from vendor 1, Android API 1, revision 4>\n",
                 getTree(m, false /*displaySortByApi*/));
     }
 
@@ -1411,8 +1411,8 @@
                 "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3, updated by:Android SDK Platform-tools, revision 4>\n" +
                 "PkgCategoryApi <API=API 1, label=Android android-1 (API 1), #items=3>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
-                "-- <NEW, pkg:addon A by vendor 1, Android API 1, revision 5>\n" +
-                "-- <NEW, pkg:addon B by vendor 1, Android API 1, revision 6>\n" +
+                "-- <NEW, pkg:The addon A from vendor 1, Android API 1, revision 5>\n" +
+                "-- <NEW, pkg:The addon B from vendor 1, Android API 1, revision 6>\n" +
                 "PkgCategoryApi <API=EXTRAS, label=Extras, #items=0>\n",
                 getTree(m, true /*displaySortByApi*/));
         assertEquals(
@@ -1422,8 +1422,8 @@
                 "-- <INSTALLED, pkg:Android SDK Platform-tools, revision 3, updated by:Android SDK Platform-tools, revision 4>\n" +
                 "-- <INSTALLED, pkg:SDK Platform Android android-1, API 1, revision 2>\n" +
                 "PkgCategorySource <source=repo2 (2.example.com), #items=2>\n" +
-                "-- <NEW, pkg:addon A by vendor 1, Android API 1, revision 5>\n" +
-                "-- <NEW, pkg:addon B by vendor 1, Android API 1, revision 6>\n",
+                "-- <NEW, pkg:The addon A from vendor 1, Android API 1, revision 5>\n" +
+                "-- <NEW, pkg:The addon B from vendor 1, Android API 1, revision 6>\n",
                 getTree(m, false /*displaySortByApi*/));
     }
     // ----