1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.sdklib.internal.repository; 18 19 import com.android.annotations.VisibleForTesting; 20 import com.android.annotations.VisibleForTesting.Visibility; 21 import com.android.sdklib.AndroidVersion; 22 import com.android.sdklib.IAndroidTarget; 23 import com.android.sdklib.SdkConstants; 24 import com.android.sdklib.SdkManager; 25 import com.android.sdklib.internal.repository.Archive.Arch; 26 import com.android.sdklib.internal.repository.Archive.Os; 27 import com.android.sdklib.repository.PkgProps; 28 import com.android.sdklib.repository.SdkRepoConstants; 29 import com.android.util.Pair; 30 31 import org.w3c.dom.Node; 32 33 import java.io.File; 34 import java.util.Map; 35 import java.util.Properties; 36 37 /** 38 * Represents a platform XML node in an SDK repository. 39 */ 40 public class PlatformPackage extends MinToolsPackage implements IPackageVersion, ILayoutlibVersion { 41 42 /** The package version, for platform, add-on and doc packages. */ 43 private final AndroidVersion mVersion; 44 45 /** The version, a string, for platform packages. */ 46 private final String mVersionName; 47 48 /** The ABI of the system-image included in this platform. Can be null but not empty. */ 49 private final String mIncludedAbi; 50 51 /** The helper handling the layoutlib version. */ 52 private final LayoutlibVersionMixin mLayoutlibVersion; 53 54 /** 55 * Creates a new platform package from the attributes and elements of the given XML node. 56 * This constructor should throw an exception if the package cannot be created. 57 * 58 * @param source The {@link SdkSource} where this is loaded from. 59 * @param packageNode The XML element being parsed. 60 * @param nsUri The namespace URI of the originating XML document, to be able to deal with 61 * parameters that vary according to the originating XML schema. 62 * @param licenses The licenses loaded from the XML originating document. 63 */ 64 PlatformPackage(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) { 65 super(source, packageNode, nsUri, licenses); 66 67 mVersionName = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_VERSION); 68 69 int apiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0); 70 String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME); 71 if (codeName.length() == 0) { 72 codeName = null; 73 } 74 mVersion = new AndroidVersion(apiLevel, codeName); 75 76 mIncludedAbi = XmlParserUtils.getOptionalXmlString( 77 packageNode, SdkRepoConstants.NODE_ABI_INCLUDED); 78 79 mLayoutlibVersion = new LayoutlibVersionMixin(packageNode); 80 } 81 82 /** 83 * Creates a new platform package based on an actual {@link IAndroidTarget} (which 84 * must have {@link IAndroidTarget#isPlatform()} true) from the {@link SdkManager}. 85 * This is used to list local SDK folders in which case there is one archive which 86 * URL is the actual target location. 87 * <p/> 88 * By design, this creates a package with one and only one archive. 89 */ 90 static Package create(IAndroidTarget target, Properties props) { 91 return new PlatformPackage(target, props); 92 } 93 94 @VisibleForTesting(visibility=Visibility.PRIVATE) 95 protected PlatformPackage(IAndroidTarget target, Properties props) { 96 this(null /*source*/, target, props); 97 } 98 99 @VisibleForTesting(visibility=Visibility.PRIVATE) 100 protected PlatformPackage(SdkSource source, IAndroidTarget target, Properties props) { 101 super( source, //source 102 props, //properties 103 target.getRevision(), //revision 104 null, //license 105 target.getDescription(), //description 106 null, //descUrl 107 Os.getCurrentOs(), //archiveOs 108 Arch.getCurrentArch(), //archiveArch 109 target.getLocation() //archiveOsPath 110 ); 111 112 mVersion = target.getVersion(); 113 mVersionName = target.getVersionName(); 114 mLayoutlibVersion = new LayoutlibVersionMixin(props); 115 mIncludedAbi = props == null ? null : props.getProperty(PkgProps.PLATFORM_INCLUDED_ABI); 116 } 117 118 /** 119 * Save the properties of the current packages in the given {@link Properties} object. 120 * These properties will later be given to a constructor that takes a {@link Properties} object. 121 */ 122 @Override 123 void saveProperties(Properties props) { 124 super.saveProperties(props); 125 126 mVersion.saveProperties(props); 127 mLayoutlibVersion.saveProperties(props); 128 129 if (mVersionName != null) { 130 props.setProperty(PkgProps.PLATFORM_VERSION, mVersionName); 131 } 132 133 if (mIncludedAbi != null) { 134 props.setProperty(PkgProps.PLATFORM_INCLUDED_ABI, mIncludedAbi); 135 } 136 137 } 138 139 /** Returns the version, a string, for platform packages. */ 140 public String getVersionName() { 141 return mVersionName; 142 } 143 144 /** Returns the package version, for platform, add-on and doc packages. */ 145 public AndroidVersion getVersion() { 146 return mVersion; 147 } 148 149 /** 150 * Returns the ABI of the system-image included in this platform. 151 * 152 * @return Null if the platform does not include any system-image. 153 * Otherwise should be a valid non-empty ABI string (e.g. "x86" or "armeabi-v7a"). 154 */ 155 public String getIncludedAbi() { 156 return mIncludedAbi; 157 } 158 159 /** 160 * Returns the layoutlib version. Mandatory starting with repository XSD rev 4. 161 * <p/> 162 * The first integer is the API of layoublib, which should be > 0. 163 * It will be equal to {@link ILayoutlibVersion#LAYOUTLIB_API_NOT_SPECIFIED} (0) 164 * if the layoutlib version isn't specified. 165 * <p/> 166 * The second integer is the revision for that given API. It is >= 0 167 * and works as a minor revision number, incremented for the same API level. 168 * 169 * @since sdk-repository-4.xsd 170 */ 171 public Pair<Integer, Integer> getLayoutlibVersion() { 172 return mLayoutlibVersion.getLayoutlibVersion(); 173 } 174 175 /** 176 * Returns a string identifier to install this package from the command line. 177 * For platforms, we use "android-N" where N is the API or the preview codename. 178 * <p/> 179 * {@inheritDoc} 180 */ 181 @Override 182 public String installId() { 183 return "android-" + mVersion.getApiString(); //$NON-NLS-1$ 184 } 185 186 /** 187 * Returns a description of this package that is suitable for a list display. 188 * <p/> 189 * {@inheritDoc} 190 */ 191 @Override 192 public String getListDescription() { 193 String s; 194 195 if (mVersion.isPreview()) { 196 s = String.format("SDK Platform Android %1$s Preview%2$s", 197 getVersionName(), 198 isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$ 199 } else { 200 s = String.format("SDK Platform Android %1$s%2$s", 201 getVersionName(), 202 isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$ 203 } 204 205 return s; 206 } 207 208 /** 209 * Returns a short description for an {@link IDescription}. 210 */ 211 @Override 212 public String getShortDescription() { 213 String s; 214 215 if (mVersion.isPreview()) { 216 s = String.format("SDK Platform Android %1$s Preview, revision %2$s%3$s", 217 getVersionName(), 218 getRevision(), 219 isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$ 220 } else { 221 s = String.format("SDK Platform Android %1$s, API %2$d, revision %3$s%4$s", 222 getVersionName(), 223 mVersion.getApiLevel(), 224 getRevision(), 225 isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$ 226 } 227 228 return s; 229 } 230 231 /** 232 * Returns a long description for an {@link IDescription}. 233 * 234 * The long description is whatever the XML contains for the <description> field, 235 * or the short description if the former is empty. 236 */ 237 @Override 238 public String getLongDescription() { 239 String s = getDescription(); 240 if (s == null || s.length() == 0) { 241 s = getShortDescription(); 242 } 243 244 if (s.indexOf("revision") == -1) { 245 s += String.format("\nRevision %1$d%2$s", 246 getRevision(), 247 isObsolete() ? " (Obsolete)" : ""); 248 } 249 250 return s; 251 } 252 253 /** 254 * Computes a potential installation folder if an archive of this package were 255 * to be installed right away in the given SDK root. 256 * <p/> 257 * A platform package is typically installed in SDK/platforms/android-"version". 258 * However if we can find a different directory under SDK/platform that already 259 * has this platform version installed, we'll use that one. 260 * 261 * @param osSdkRoot The OS path of the SDK root folder. 262 * @param sdkManager An existing SDK manager to list current platforms and addons. 263 * @return A new {@link File} corresponding to the directory to use to install this package. 264 */ 265 @Override 266 public File getInstallFolder(String osSdkRoot, SdkManager sdkManager) { 267 268 // First find if this platform is already installed. If so, reuse the same directory. 269 for (IAndroidTarget target : sdkManager.getTargets()) { 270 if (target.isPlatform() && target.getVersion().equals(mVersion)) { 271 return new File(target.getLocation()); 272 } 273 } 274 275 File platforms = new File(osSdkRoot, SdkConstants.FD_PLATFORMS); 276 File folder = new File(platforms, 277 String.format("android-%s", getVersion().getApiString())); //$NON-NLS-1$ 278 279 return folder; 280 } 281 282 @Override 283 public boolean sameItemAs(Package pkg) { 284 if (pkg instanceof PlatformPackage) { 285 PlatformPackage newPkg = (PlatformPackage)pkg; 286 287 // check they are the same version. 288 return newPkg.getVersion().equals(this.getVersion()); 289 } 290 291 return false; 292 } 293 294 @Override 295 public int hashCode() { 296 final int prime = 31; 297 int result = super.hashCode(); 298 result = prime * result + 299 ((mLayoutlibVersion == null) ? 0 : mLayoutlibVersion.hashCode()); 300 result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode()); 301 result = prime * result + ((mVersionName == null) ? 0 : mVersionName.hashCode()); 302 return result; 303 } 304 305 @Override 306 public boolean equals(Object obj) { 307 if (this == obj) { 308 return true; 309 } 310 if (!super.equals(obj)) { 311 return false; 312 } 313 if (!(obj instanceof PlatformPackage)) { 314 return false; 315 } 316 PlatformPackage other = (PlatformPackage) obj; 317 if (mLayoutlibVersion == null) { 318 if (other.mLayoutlibVersion != null) { 319 return false; 320 } 321 } else if (!mLayoutlibVersion.equals(other.mLayoutlibVersion)) { 322 return false; 323 } 324 if (mVersion == null) { 325 if (other.mVersion != null) { 326 return false; 327 } 328 } else if (!mVersion.equals(other.mVersion)) { 329 return false; 330 } 331 if (mVersionName == null) { 332 if (other.mVersionName != null) { 333 return false; 334 } 335 } else if (!mVersionName.equals(other.mVersionName)) { 336 return false; 337 } 338 return true; 339 } 340 } 341