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