• 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;
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          */
ChecksumType(String algorithmName)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          */
getMessageDigest()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 
Os(String uiName)80         private Os(String uiName) {
81             mUiName = uiName;
82         }
83 
84         /** Returns the UI name of the OS. */
getUiName()85         public String getUiName() {
86             return mUiName;
87         }
88 
89         /** Returns the XML name of the OS. */
getXmlName()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          */
getCurrentOs()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. */
isCompatible()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 
Arch(String uiName)132         private Arch(String uiName) {
133             mUiName = uiName;
134         }
135 
136         /** Returns the UI name of the architecture. */
getUiName()137         public String getUiName() {
138             return mUiName;
139         }
140 
141         /** Returns the XML name of the architecture. */
getXmlName()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          */
getCurrentArch()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. */
isCompatible()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      */
Archive(Package pkg, Os os, Arch arch, String url, long size, String checksum)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)
Archive(Package pkg, Properties props, Os os, Arch arch, String localOsPath)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      */
saveProperties(Properties props)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      */
isLocal()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      */
getParentPackage()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      */
getSize()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      */
getChecksum()258     public String getChecksum() {
259         return mChecksum;
260     }
261 
262     /**
263      * Returns the checksum type, always {@link ChecksumType#SHA1} right now.
264      */
getChecksumType()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      */
getUrl()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      */
getLocalOsPath()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      */
getOs()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      */
getArch()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      */
getOsDescription()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
toString()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      */
getShortDescription()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      */
getLongDescription()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      */
isCompatible()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      */
deleteLocal()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      */
compareTo(Archive rhs)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
hashCode()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
equals(Object obj)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