Home | History | Annotate | Download | only in repository
      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.io.FileOp;
     22 
     23 import java.io.File;
     24 import java.security.MessageDigest;
     25 import java.security.NoSuchAlgorithmException;
     26 import java.util.Properties;
     27 
     28 
     29 /**
     30  * A {@link Archive} is the base class for "something" that can be downloaded from
     31  * the SDK repository.
     32  * <p/>
     33  * A package has some attributes (revision, description) and a list of archives
     34  * which represent the downloadable bits.
     35  * <p/>
     36  * Packages are offered by a {@link SdkSource} (a download site).
     37  * The {@link ArchiveInstaller} takes care of downloading, unpacking and installing an archive.
     38  */
     39 public class Archive implements IDescription, Comparable<Archive> {
     40 
     41     private static final String PROP_OS   = "Archive.Os";       //$NON-NLS-1$
     42     private static final String PROP_ARCH = "Archive.Arch";     //$NON-NLS-1$
     43 
     44     /** The checksum type. */
     45     public enum ChecksumType {
     46         /** A SHA1 checksum, represented as a 40-hex string. */
     47         SHA1("SHA-1");  //$NON-NLS-1$
     48 
     49         private final String mAlgorithmName;
     50 
     51         /**
     52          * Constructs a {@link ChecksumType} with the algorigth name
     53          * suitable for {@link MessageDigest#getInstance(String)}.
     54          * <p/>
     55          * These names are officially documented at
     56          * http://java.sun.com/javase/6/docs/technotes/guides/security/StandardNames.html#MessageDigest
     57          */
     58         private ChecksumType(String algorithmName) {
     59             mAlgorithmName = algorithmName;
     60         }
     61 
     62         /**
     63          * Returns a new {@link MessageDigest} instance for this checksum type.
     64          * @throws NoSuchAlgorithmException if this algorithm is not available.
     65          */
     66         public MessageDigest getMessageDigest() throws NoSuchAlgorithmException {
     67             return MessageDigest.getInstance(mAlgorithmName);
     68         }
     69     }
     70 
     71     /** The OS that this archive can be downloaded on. */
     72     public enum Os {
     73         ANY("Any"),
     74         LINUX("Linux"),
     75         MACOSX("MacOS X"),
     76         WINDOWS("Windows");
     77 
     78         private final String mUiName;
     79 
     80         private Os(String uiName) {
     81             mUiName = uiName;
     82         }
     83 
     84         /** Returns the UI name of the OS. */
     85         public String getUiName() {
     86             return mUiName;
     87         }
     88 
     89         /** Returns the XML name of the OS. */
     90         public String getXmlName() {
     91             return toString().toLowerCase();
     92         }
     93 
     94         /**
     95          * Returns the current OS as one of the {@link Os} enum values or null.
     96          */
     97         public static Os getCurrentOs() {
     98             String os = System.getProperty("os.name");          //$NON-NLS-1$
     99             if (os.startsWith("Mac")) {                         //$NON-NLS-1$
    100                 return Os.MACOSX;
    101 
    102             } else if (os.startsWith("Windows")) {              //$NON-NLS-1$
    103                 return Os.WINDOWS;
    104 
    105             } else if (os.startsWith("Linux")) {                //$NON-NLS-1$
    106                 return Os.LINUX;
    107             }
    108 
    109             return null;
    110         }
    111 
    112         /** Returns true if this OS is compatible with the current one. */
    113         public boolean isCompatible() {
    114             if (this == ANY) {
    115                 return true;
    116             }
    117 
    118             Os os = getCurrentOs();
    119             return this == os;
    120         }
    121     }
    122 
    123     /** The Architecture that this archive can be downloaded on. */
    124     public enum Arch {
    125         ANY("Any"),
    126         PPC("PowerPC"),
    127         X86("x86"),
    128         X86_64("x86_64");
    129 
    130         private final String mUiName;
    131 
    132         private Arch(String uiName) {
    133             mUiName = uiName;
    134         }
    135 
    136         /** Returns the UI name of the architecture. */
    137         public String getUiName() {
    138             return mUiName;
    139         }
    140 
    141         /** Returns the XML name of the architecture. */
    142         public String getXmlName() {
    143             return toString().toLowerCase();
    144         }
    145 
    146         /**
    147          * Returns the current architecture as one of the {@link Arch} enum values or null.
    148          */
    149         public static Arch getCurrentArch() {
    150             // Values listed from http://lopica.sourceforge.net/os.html
    151             String arch = System.getProperty("os.arch");
    152 
    153             if (arch.equalsIgnoreCase("x86_64") || arch.equalsIgnoreCase("amd64")) {
    154                 return Arch.X86_64;
    155 
    156             } else if (arch.equalsIgnoreCase("x86")
    157                     || arch.equalsIgnoreCase("i386")
    158                     || arch.equalsIgnoreCase("i686")) {
    159                 return Arch.X86;
    160 
    161             } else if (arch.equalsIgnoreCase("ppc") || arch.equalsIgnoreCase("PowerPC")) {
    162                 return Arch.PPC;
    163             }
    164 
    165             return null;
    166         }
    167 
    168         /** Returns true if this architecture is compatible with the current one. */
    169         public boolean isCompatible() {
    170             if (this == ANY) {
    171                 return true;
    172             }
    173 
    174             Arch arch = getCurrentArch();
    175             return this == arch;
    176         }
    177     }
    178 
    179     private final Os     mOs;
    180     private final Arch   mArch;
    181     private final String mUrl;
    182     private final long   mSize;
    183     private final String mChecksum;
    184     private final ChecksumType mChecksumType = ChecksumType.SHA1;
    185     private final Package mPackage;
    186     private final String mLocalOsPath;
    187     private final boolean mIsLocal;
    188 
    189     /**
    190      * Creates a new remote archive.
    191      */
    192     Archive(Package pkg, Os os, Arch arch, String url, long size, String checksum) {
    193         mPackage = pkg;
    194         mOs = os;
    195         mArch = arch;
    196         mUrl = url == null ? null : url.trim();
    197         mLocalOsPath = null;
    198         mSize = size;
    199         mChecksum = checksum;
    200         mIsLocal = false;
    201     }
    202 
    203     /**
    204      * Creates a new local archive.
    205      * Uses the properties from props first, if possible. Props can be null.
    206      */
    207     @VisibleForTesting(visibility=Visibility.PACKAGE)
    208     protected Archive(Package pkg, Properties props, Os os, Arch arch, String localOsPath) {
    209         mPackage = pkg;
    210 
    211         mOs   = props == null ? os   : Os.valueOf(  props.getProperty(PROP_OS,   os.toString()));
    212         mArch = props == null ? arch : Arch.valueOf(props.getProperty(PROP_ARCH, arch.toString()));
    213 
    214         mUrl = null;
    215         mLocalOsPath = localOsPath;
    216         mSize = 0;
    217         mChecksum = "";
    218         mIsLocal = localOsPath != null;
    219     }
    220 
    221     /**
    222      * Save the properties of the current archive in the give {@link Properties} object.
    223      * These properties will later be give the constructor that takes a {@link Properties} object.
    224      */
    225     void saveProperties(Properties props) {
    226         props.setProperty(PROP_OS,   mOs.toString());
    227         props.setProperty(PROP_ARCH, mArch.toString());
    228     }
    229 
    230     /**
    231      * Returns true if this is a locally installed archive.
    232      * Returns false if this is a remote archive that needs to be downloaded.
    233      */
    234     public boolean isLocal() {
    235         return mIsLocal;
    236     }
    237 
    238     /**
    239      * Returns the package that created and owns this archive.
    240      * It should generally not be null.
    241      */
    242     public Package getParentPackage() {
    243         return mPackage;
    244     }
    245 
    246     /**
    247      * Returns the archive size, an int > 0.
    248      * Size will be 0 if this a local installed folder of unknown size.
    249      */
    250     public long getSize() {
    251         return mSize;
    252     }
    253 
    254     /**
    255      * Returns the SHA1 archive checksum, as a 40-char hex.
    256      * Can be empty but not null for local installed folders.
    257      */
    258     public String getChecksum() {
    259         return mChecksum;
    260     }
    261 
    262     /**
    263      * Returns the checksum type, always {@link ChecksumType#SHA1} right now.
    264      */
    265     public ChecksumType getChecksumType() {
    266         return mChecksumType;
    267     }
    268 
    269     /**
    270      * Returns the download archive URL, either absolute or relative to the repository xml.
    271      * Always return null for a local installed folder.
    272      * @see #getLocalOsPath()
    273      */
    274     public String getUrl() {
    275         return mUrl;
    276     }
    277 
    278     /**
    279      * Returns the local OS folder where a local archive is installed.
    280      * Always return null for remote archives.
    281      * @see #getUrl()
    282      */
    283     public String getLocalOsPath() {
    284         return mLocalOsPath;
    285     }
    286 
    287     /**
    288      * Returns the archive {@link Os} enum.
    289      * Can be null for a local installed folder on an unknown OS.
    290      */
    291     public Os getOs() {
    292         return mOs;
    293     }
    294 
    295     /**
    296      * Returns the archive {@link Arch} enum.
    297      * Can be null for a local installed folder on an unknown architecture.
    298      */
    299     public Arch getArch() {
    300         return mArch;
    301     }
    302 
    303     /**
    304      * Generates a description for this archive of the OS/Arch supported by this archive.
    305      */
    306     public String getOsDescription() {
    307         String os;
    308         if (mOs == null) {
    309             os = "unknown OS";
    310         } else if (mOs == Os.ANY) {
    311             os = "any OS";
    312         } else {
    313             os = mOs.getUiName();
    314         }
    315 
    316         String arch = "";                               //$NON-NLS-1$
    317         if (mArch != null && mArch != Arch.ANY) {
    318             arch = mArch.getUiName();
    319         }
    320 
    321         return String.format("%1$s%2$s%3$s",
    322                 os,
    323                 arch.length() > 0 ? " " : "",           //$NON-NLS-2$
    324                 arch);
    325     }
    326 
    327     /**
    328      * Returns the short description of the source, if not null.
    329      * Otherwise returns the default Object toString result.
    330      * <p/>
    331      * This is mostly helpful for debugging.
    332      * For UI display, use the {@link IDescription} interface.
    333      */
    334     @Override
    335     public String toString() {
    336         String s = getShortDescription();
    337         if (s != null) {
    338             return s;
    339         }
    340         return super.toString();
    341     }
    342 
    343     /**
    344      * Generates a short description for this archive.
    345      */
    346     public String getShortDescription() {
    347         return String.format("Archive for %1$s", getOsDescription());
    348     }
    349 
    350     /**
    351      * Generates a longer description for this archive.
    352      */
    353     public String getLongDescription() {
    354         long size = getSize();
    355         String sizeStr;
    356         if (size < 1024) {
    357             sizeStr = String.format("%d Bytes", size);
    358         } else if (size < 1024 * 1024) {
    359             sizeStr = String.format("%d KiB", Math.round(size / 1024.0));
    360         } else if (size < 1024 * 1024 * 1024) {
    361             sizeStr = String.format("%.1f MiB",
    362                     Math.round(10.0 * size / (1024 * 1024.0))/ 10.0);
    363         } else {
    364             sizeStr = String.format("%.1f GiB",
    365                     Math.round(10.0 * size / (1024 * 1024 * 1024.0))/ 10.0);
    366         }
    367 
    368         return String.format("%1$s\nSize: %2$s\nSHA1: %3$s",
    369                 getShortDescription(), sizeStr, getChecksum());
    370     }
    371 
    372     /**
    373      * Returns true if this archive can be installed on the current platform.
    374      */
    375     public boolean isCompatible() {
    376         return getOs().isCompatible() && getArch().isCompatible();
    377     }
    378 
    379     /**
    380      * Delete the archive folder if this is a local archive.
    381      */
    382     public void deleteLocal() {
    383         if (isLocal()) {
    384             new FileOp().deleteFileOrFolder(new File(getLocalOsPath()));
    385         }
    386     }
    387 
    388     /**
    389      * Archives are compared using their {@link Package} ordering.
    390      *
    391      * @see Package#compareTo(Package)
    392      */
    393     public int compareTo(Archive rhs) {
    394         if (mPackage != null && rhs != null) {
    395             return mPackage.compareTo(rhs.getParentPackage());
    396         }
    397         return 0;
    398     }
    399 
    400     /**
    401      * Note: An {@link Archive}'s hash code does NOT depend on the parent {@link Package} hash code.
    402      * <p/>
    403      * {@inheritDoc}
    404      */
    405     @Override
    406     public int hashCode() {
    407         final int prime = 31;
    408         int result = 1;
    409         result = prime * result + ((mArch == null) ? 0 : mArch.hashCode());
    410         result = prime * result + ((mChecksum == null) ? 0 : mChecksum.hashCode());
    411         result = prime * result + ((mChecksumType == null) ? 0 : mChecksumType.hashCode());
    412         result = prime * result + (mIsLocal ? 1231 : 1237);
    413         result = prime * result + ((mLocalOsPath == null) ? 0 : mLocalOsPath.hashCode());
    414         result = prime * result + ((mOs == null) ? 0 : mOs.hashCode());
    415         result = prime * result + (int) (mSize ^ (mSize >>> 32));
    416         result = prime * result + ((mUrl == null) ? 0 : mUrl.hashCode());
    417         return result;
    418     }
    419 
    420     /**
    421      * Note: An {@link Archive}'s equality does NOT depend on the parent {@link Package} equality.
    422      * <p/>
    423      * {@inheritDoc}
    424      */
    425     @Override
    426     public boolean equals(Object obj) {
    427         if (this == obj) {
    428             return true;
    429         }
    430         if (obj == null) {
    431             return false;
    432         }
    433         if (!(obj instanceof Archive)) {
    434             return false;
    435         }
    436         Archive other = (Archive) obj;
    437         if (mArch == null) {
    438             if (other.mArch != null) {
    439                 return false;
    440             }
    441         } else if (!mArch.equals(other.mArch)) {
    442             return false;
    443         }
    444         if (mChecksum == null) {
    445             if (other.mChecksum != null) {
    446                 return false;
    447             }
    448         } else if (!mChecksum.equals(other.mChecksum)) {
    449             return false;
    450         }
    451         if (mChecksumType == null) {
    452             if (other.mChecksumType != null) {
    453                 return false;
    454             }
    455         } else if (!mChecksumType.equals(other.mChecksumType)) {
    456             return false;
    457         }
    458         if (mIsLocal != other.mIsLocal) {
    459             return false;
    460         }
    461         if (mLocalOsPath == null) {
    462             if (other.mLocalOsPath != null) {
    463                 return false;
    464             }
    465         } else if (!mLocalOsPath.equals(other.mLocalOsPath)) {
    466             return false;
    467         }
    468         if (mOs == null) {
    469             if (other.mOs != null) {
    470                 return false;
    471             }
    472         } else if (!mOs.equals(other.mOs)) {
    473             return false;
    474         }
    475         if (mSize != other.mSize) {
    476             return false;
    477         }
    478         if (mUrl == null) {
    479             if (other.mUrl != null) {
    480                 return false;
    481             }
    482         } else if (!mUrl.equals(other.mUrl)) {
    483             return false;
    484         }
    485         return true;
    486     }
    487 }
    488