Merge change 3572
* changes:
Changing plugin to use the draw event (not drawContext)
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index fb34afa..9d25512 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -1562,6 +1562,13 @@
</intent-filter>
</activity>
+ <activity android:name=".graphics.ColorFilters" android:label="Graphics/ColorFilters">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".graphics.CreateBitmap" android:label="Graphics/CreateBitmap">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/samples/ApiDemos/res/drawable/btn_check_off.png b/samples/ApiDemos/res/drawable/btn_check_off.png
new file mode 100644
index 0000000..56d3861
--- /dev/null
+++ b/samples/ApiDemos/res/drawable/btn_check_off.png
Binary files differ
diff --git a/samples/ApiDemos/res/drawable/btn_check_on.png b/samples/ApiDemos/res/drawable/btn_check_on.png
new file mode 100644
index 0000000..791ac1d
--- /dev/null
+++ b/samples/ApiDemos/res/drawable/btn_check_on.png
Binary files differ
diff --git a/samples/ApiDemos/res/drawable/btn_circle_normal.png b/samples/ApiDemos/res/drawable/btn_circle_normal.png
new file mode 100644
index 0000000..fc5af1c
--- /dev/null
+++ b/samples/ApiDemos/res/drawable/btn_circle_normal.png
Binary files differ
diff --git a/samples/ApiDemos/res/drawable/btn_default_normal.9.png b/samples/ApiDemos/res/drawable/btn_default_normal.9.png
new file mode 100644
index 0000000..a2d5ccd
--- /dev/null
+++ b/samples/ApiDemos/res/drawable/btn_default_normal.9.png
Binary files differ
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/ColorFilters.java b/samples/ApiDemos/src/com/example/android/apis/graphics/ColorFilters.java
new file mode 100644
index 0000000..92d18ba
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/ColorFilters.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2009 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.example.android.apis.graphics;
+
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.*;
+import android.graphics.drawable.*;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.*;
+
+public class ColorFilters extends GraphicsActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(new SampleView(this));
+
+ }
+
+ private static class SampleView extends View {
+ private Activity mActivity;
+ private Drawable mDrawable;
+ private Drawable[] mDrawables;
+ private Paint mPaint;
+ private Paint mPaint2;
+ private float mPaintTextOffset;
+ private int[] mColors;
+ private PorterDuff.Mode[] mModes;
+ private int mModeIndex;
+
+ private static void addToTheRight(Drawable curr, Drawable prev) {
+ Rect r = prev.getBounds();
+ int x = r.right + 12;
+ int center = (r.top + r.bottom) >> 1;
+ int h = curr.getIntrinsicHeight();
+ int y = center - (h >> 1);
+
+ curr.setBounds(x, y, x + curr.getIntrinsicWidth(), y + h);
+ }
+
+ public SampleView(Activity activity) {
+ super(activity);
+ mActivity = activity;
+ Context context = activity;
+ setFocusable(true);
+
+ mDrawable = context.getResources().getDrawable(R.drawable.btn_default_normal);
+ mDrawable.setBounds(0, 0, 150, 48);
+ mDrawable.setDither(true);
+
+ int[] resIDs = new int[] {
+ R.drawable.btn_circle_normal,
+ R.drawable.btn_check_off,
+ R.drawable.btn_check_on
+ };
+ mDrawables = new Drawable[resIDs.length];
+ Drawable prev = mDrawable;
+ for (int i = 0; i < resIDs.length; i++) {
+ mDrawables[i] = context.getResources().getDrawable(resIDs[i]);
+ mDrawables[i].setDither(true);
+ addToTheRight(mDrawables[i], prev);
+ prev = mDrawables[i];
+ }
+
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setTextSize(16);
+ mPaint.setTextAlign(Paint.Align.CENTER);
+
+ mPaint2 = new Paint(mPaint);
+ mPaint2.setAlpha(64);
+
+ Paint.FontMetrics fm = mPaint.getFontMetrics();
+ mPaintTextOffset = (fm.descent + fm.ascent) * 0.5f;
+
+ mColors = new int[] {
+ 0,
+ 0xCC0000FF,
+ 0x880000FF,
+ 0x440000FF,
+ 0xFFCCCCFF,
+ 0xFF8888FF,
+ 0xFF4444FF,
+ };
+
+ mModes = new PorterDuff.Mode[] {
+ PorterDuff.Mode.SRC_ATOP,
+ PorterDuff.Mode.MULTIPLY,
+ };
+ mModeIndex = 0;
+
+ updateTitle();
+ }
+
+ private void swapPaintColors() {
+ if (mPaint.getColor() == 0xFF000000) {
+ mPaint.setColor(0xFFFFFFFF);
+ mPaint2.setColor(0xFF000000);
+ } else {
+ mPaint.setColor(0xFF000000);
+ mPaint2.setColor(0xFFFFFFFF);
+ }
+ mPaint2.setAlpha(64);
+ }
+
+ private void updateTitle() {
+ mActivity.setTitle(mModes[mModeIndex].toString());
+ }
+
+ private void drawSample(Canvas canvas, ColorFilter filter) {
+ Rect r = mDrawable.getBounds();
+ float x = (r.left + r.right) * 0.5f;
+ float y = (r.top + r.bottom) * 0.5f - mPaintTextOffset;
+
+ mDrawable.setColorFilter(filter);
+ mDrawable.draw(canvas);
+ canvas.drawText("Label", x+1, y+1, mPaint2);
+ canvas.drawText("Label", x, y, mPaint);
+
+ for (Drawable dr : mDrawables) {
+ dr.setColorFilter(filter);
+ dr.draw(canvas);
+ }
+ }
+
+ @Override protected void onDraw(Canvas canvas) {
+ canvas.drawColor(0xFFCCCCCC);
+
+ canvas.translate(8, 12);
+ for (int color : mColors) {
+ ColorFilter filter;
+ if (color == 0) {
+ filter = null;
+ } else {
+ filter = new PorterDuffColorFilter(color,
+ mModes[mModeIndex]);
+ }
+ drawSample(canvas, filter);
+ canvas.translate(0, 55);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ float x = event.getX();
+ float y = event.getY();
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ break;
+ case MotionEvent.ACTION_MOVE:
+ break;
+ case MotionEvent.ACTION_UP:
+ // update mode every other time we change paint colors
+ if (mPaint.getColor() == 0xFFFFFFFF) {
+ mModeIndex = (mModeIndex + 1) % mModes.length;
+ updateTitle();
+ }
+ swapPaintColors();
+ invalidate();
+ break;
+ }
+ return true;
+ }
+ }
+}
+
diff --git a/simulator/app/PropertyServer.cpp b/simulator/app/PropertyServer.cpp
index 4b1aedd..0047b5d 100644
--- a/simulator/app/PropertyServer.cpp
+++ b/simulator/app/PropertyServer.cpp
@@ -110,6 +110,8 @@
{ "ro.EMPTY_APP_MEM", "16384" },
{ "ro.HOME_APP_ADJ", "4" },
{ "ro.HOME_APP_MEM", "4096" },
+ { "ro.BACKUP_APP_ADJ", "2" },
+ { "ro.BACKUP_APP_MEM", "4096" },
//{ "init.svc.adbd", "running" }, // causes ADB-JDWP
{ "init.svc.usbd", "running" },
{ "init.svc.debuggerd", "running" },
@@ -180,7 +182,7 @@
if (strlen(prop.value) >= PROPERTY_VALUE_MAX) {
fprintf(stderr,
"GLITCH: properties table holds '%s' '%s' (len=%d)\n",
- prop.key, prop.value, strlen(prop.value));
+ prop.key, prop.value, (int) strlen(prop.value));
abort();
}
assert(strlen(prop.value) < PROPERTY_VALUE_MAX);
@@ -323,7 +325,7 @@
if (actual != PROPERTY_KEY_MAX) {
fprintf(stderr, "Bad read on get: %d of %d\n",
- actual, PROPERTY_KEY_MAX);
+ (int) actual, PROPERTY_KEY_MAX);
return false;
}
if (GetProperty(reqBuf, valueBuf+1))
@@ -340,7 +342,7 @@
actual = read(fd, reqBuf, PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX);
if (actual != PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX) {
fprintf(stderr, "Bad read on set: %d of %d\n",
- actual, PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX);
+ (int) actual, PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX);
return false;
}
//printf("SET property '%s'\n", reqBuf);
diff --git a/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java b/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
index d3b06f7..3874e6b 100644
--- a/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
+++ b/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
@@ -26,7 +26,7 @@
/**
* A class visitor that filters out all members (fields, methods and inner classes) that are
- * either private or rejected by the {@link Filter}.
+ * either private, default-access or rejected by the {@link Filter}.
*/
class FilterClassAdapter extends ClassAdapter {
@@ -50,12 +50,12 @@
public void visitEnd() {
super.visitEnd();
}
-
+
/**
* Visits a field.
- *
+ *
* {@inheritDoc}
- *
+ *
* Examples:
* name = mArg
* desc = Ljava/Lang/String;
@@ -64,11 +64,11 @@
@Override
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
- // exclude private fields
- if ((access & Opcodes.ACC_PRIVATE) != 0) {
+ // only accept public/protected fields
+ if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
return null;
}
-
+
// filter on field name
String filterName = String.format("%s#%s", mClassName, name);
@@ -76,7 +76,7 @@
System.out.println("- Remove field " + filterName);
return null;
}
-
+
// TODO we should produce an error if a filtered desc/signature is being used.
return super.visitField(access, name, desc, signature, value);
@@ -84,9 +84,9 @@
/**
* Visits a method.
- *
+ *
* {@inheritDoc}
- *
+ *
* Examples:
* name = <init>
* desc = ()V
@@ -96,11 +96,11 @@
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
- // exclude private methods
- if ((access & Opcodes.ACC_PRIVATE) != 0) {
+ // only accept public/protected methods
+ if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
return null;
}
-
+
// filter on method name using the non-generic descriptor
String filterName = String.format("%s#%s%s", mClassName, name, desc);
@@ -112,7 +112,7 @@
// filter on method name using the generic signature
if (signature != null) {
filterName = String.format("%s#%s%s", mClassName, name, signature);
-
+
if (!mFilter.accept(filterName)) {
System.out.println("- Remove method " + filterName);
return null;
@@ -126,7 +126,7 @@
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-
+
// TODO produce an error if a filtered annotation type is being used
return super.visitAnnotation(desc, visible);
}
@@ -139,8 +139,8 @@
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
- // exclude private methods
- if ((access & Opcodes.ACC_PRIVATE) != 0) {
+ // only accept public/protected inner classes
+ if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
return;
}
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
index bd76a4c..761c236 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
@@ -74,14 +74,15 @@
}
/**
- * Creates a new platform package based on an actual {@link IAndroidTarget} (with
+ * Creates a new platform package based on an actual {@link IAndroidTarget} (which
* {@link IAndroidTarget#isPlatform()} false) from the {@link SdkManager}.
* This is used to list local SDK folders.
*/
AddonPackage(IAndroidTarget target) {
super( null, //source
0, //revision
- target.getDescription(), //description
+ null, //license
+ target.getDescription(), //description
null, //descUrl
Os.getCurrentOs(), //archiveOs
Arch.getCurrentArch(), //archiveArch
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
index 9686cbd..bd6b5be 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
@@ -16,8 +16,16 @@
package com.android.sdklib.internal.repository;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
/**
@@ -32,6 +40,8 @@
*/
public class Archive implements IDescription {
+ public static final int NUM_MONITOR_INC = 100;
+
/** The checksum type. */
public enum ChecksumType {
/** A SHA1 checksum, represented as a 40-hex string. */
@@ -269,5 +279,450 @@
return true;
}
+
+ /**
+ * Install this {@link Archive}s.
+ * The archive will be skipped if it is incompatible.
+ *
+ * @return True if the archive was installed, false otherwise.
+ */
+ public boolean install(String osSdkRoot, ITaskMonitor monitor) {
+
+ File archiveFile = null;
+ try {
+ String name = getParentPackage().getShortDescription();
+
+ // TODO: we should not see this test fail if we had the filter UI above.
+ if (!isCompatible()) {
+ monitor.setResult("Skipping incompatible archive: %1$s", name);
+ return false;
+ }
+
+ archiveFile = downloadFile(monitor);
+ if (archiveFile != null) {
+ if (unarchive(osSdkRoot, archiveFile, monitor)) {
+ monitor.setResult("Installed: %1$s", name);
+ return true;
+ }
+ }
+
+ } finally {
+ // Delete the temp archive if it exists
+ deleteFileOrFolder(archiveFile);
+ }
+
+ return false;
+ }
+
+ /**
+ * Downloads an archive and returns the temp file with it.
+ * Caller is responsible with deleting the temp file when done.
+ */
+ private File downloadFile(ITaskMonitor monitor) {
+
+ File tmpFileToDelete = null;
+ try {
+ File tmpFile = File.createTempFile("sdkupload", ".bin"); //$NON-NLS-1$ //$NON-NLS-2$
+ tmpFileToDelete = tmpFile;
+
+ String name = getParentPackage().getShortDescription();
+ String desc = String.format("Downloading %1$s", name);
+ monitor.setDescription(desc);
+
+ String link = getUrl();
+ if (!link.startsWith("http://") //$NON-NLS-1$
+ && !link.startsWith("https://") //$NON-NLS-1$
+ && !link.startsWith("ftp://")) { //$NON-NLS-1$
+ // Make the URL absolute by prepending the source
+ Package pkg = getParentPackage();
+ RepoSource src = pkg.getParentSource();
+ if (src == null) {
+ monitor.setResult("Internal error: no source for archive %1$s", name);
+ return null;
+ }
+
+ // take the URL to the repository.xml and remove the last component
+ // to get the base
+ String repoXml = src.getUrl();
+ int pos = repoXml.lastIndexOf('/');
+ String base = repoXml.substring(0, pos + 1);
+
+ link = base + link;
+ }
+
+ if (fetchUrl(tmpFile, link, desc, monitor)) {
+ // Fetching was successful, don't delete the temp file here!
+ tmpFileToDelete = null;
+ return tmpFile;
+ }
+
+ } catch (IOException e) {
+ monitor.setResult(e.getMessage());
+
+ } finally {
+ deleteFileOrFolder(tmpFileToDelete);
+ }
+
+ return null;
+ }
+
+ /**
+ * Actually performs the download.
+ * Also computes the SHA1 of the file on the fly.
+ * <p/>
+ * Success is defined as downloading as many bytes as was expected and having the same
+ * SHA1 as expected. Returns true on success or false if any of those checks fail.
+ * <p/>
+ * Increments the monitor by {@link #NUM_MONITOR_INC}.
+ */
+ private boolean fetchUrl(File tmpFile,
+ String urlString,
+ String description,
+ ITaskMonitor monitor) {
+ URL url;
+
+ description += " (%1$d%%, %2$.0f KiB/s, %3$d %4$s left)";
+
+ FileOutputStream os = null;
+ InputStream is = null;
+ try {
+ url = new URL(urlString);
+ is = url.openStream();
+ os = new FileOutputStream(tmpFile);
+
+ MessageDigest digester = getChecksumType().getMessageDigest();
+
+ byte[] buf = new byte[65536];
+ int n;
+
+ long total = 0;
+ long size = getSize();
+ long inc = size / NUM_MONITOR_INC;
+ long next_inc = inc;
+
+ long startMs = System.currentTimeMillis();
+ long nextMs = startMs + 2000; // start update after 2 seconds
+
+ while ((n = is.read(buf)) >= 0) {
+ if (n > 0) {
+ os.write(buf, 0, n);
+ digester.update(buf, 0, n);
+ }
+
+ long timeMs = System.currentTimeMillis();
+
+ total += n;
+ if (total >= next_inc) {
+ monitor.incProgress(1);
+ next_inc += inc;
+ }
+
+ if (timeMs > nextMs) {
+ long delta = timeMs - startMs;
+ if (total > 0 && delta > 0) {
+ // percent left to download
+ int percent = (int) (100 * total / size);
+ // speed in KiB/s
+ float speed = (float)total / (float)delta * (1000.f / 1024.f);
+ // time left to download the rest at the current KiB/s rate
+ int timeLeft = (speed > 1e-3) ?
+ (int)(((size - total) / 1024.0f) / speed) :
+ 0;
+ String timeUnit = "seconds";
+ if (timeLeft > 120) {
+ timeUnit = "minutes";
+ timeLeft /= 60;
+ }
+
+ monitor.setDescription(description, percent, speed, timeLeft, timeUnit);
+ }
+ nextMs = timeMs + 1000; // update every second
+ }
+
+ if (monitor.isCancelRequested()) {
+ monitor.setResult("Download aborted by user at %1$d bytes.", total);
+ return false;
+ }
+
+ }
+
+ if (total != size) {
+ monitor.setResult("Download finished with wrong size. Expected %1$d bytes, got %2$d bytes.",
+ size, total);
+ return false;
+ }
+
+ // Create an hex string from the digest
+ byte[] digest = digester.digest();
+ n = digest.length;
+ String hex = "0123456789abcdef"; //$NON-NLS-1$
+ char[] hexDigest = new char[n * 2];
+ for (int i = 0; i < n; i++) {
+ int b = digest[i] & 0x0FF;
+ hexDigest[i*2 + 0] = hex.charAt(b >>> 4);
+ hexDigest[i*2 + 1] = hex.charAt(b & 0x0f);
+ }
+
+ String expected = getChecksum();
+ String actual = new String(hexDigest);
+ if (!actual.equalsIgnoreCase(expected)) {
+ monitor.setResult("Download finished with wrong checksum. Expected %1$s, got %2$s.",
+ expected, actual);
+ return false;
+ }
+
+ return true;
+
+ } catch (Exception e) {
+ monitor.setResult(e.getMessage());
+
+ } finally {
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException e) {
+ // pass
+ }
+ }
+
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // pass
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Install the given archive in the given folder.
+ */
+ private boolean unarchive(String osSdkRoot, File archiveFile, ITaskMonitor monitor) {
+ String name = getParentPackage().getShortDescription();
+ String desc = String.format("Installing %1$s", name);
+ monitor.setDescription(desc);
+
+ File destFolder = getParentPackage().getInstallFolder(osSdkRoot);
+
+ File unzipDestFolder = destFolder;
+ File renamedDestFolder = null;
+
+ try {
+ // If this folder already exists, unzip in a temporary folder and then move/unlink.
+ if (destFolder.exists()) {
+ // Find a new temp folder that doesn't exist yet
+ unzipDestFolder = findTempFolder(destFolder, "new"); //$NON-NLS-1$
+
+ if (unzipDestFolder == null) {
+ // this should not seriously happen.
+ monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",
+ destFolder.getPath());
+ return false;
+ }
+
+ if (!unzipDestFolder.mkdirs()) {
+ monitor.setResult("Failed to create temp directory %1$s",
+ unzipDestFolder.getPath());
+ return false;
+ }
+ }
+
+ if (!unzipFolder(archiveFile, getSize(), unzipDestFolder, desc, monitor)) {
+ return false;
+ }
+
+ if (unzipDestFolder != destFolder) {
+ // Swap the old folder by the new one.
+ // Both original folders will be deleted in the finally clause below.
+ renamedDestFolder = findTempFolder(destFolder, "old"); //$NON-NLS-1$
+ if (renamedDestFolder == null) {
+ // this should not seriously happen.
+ monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",
+ destFolder.getPath());
+ return false;
+ }
+
+ if (!destFolder.renameTo(renamedDestFolder)) {
+ monitor.setResult("Failed to rename directory %1$s to %2$s",
+ destFolder.getPath(), renamedDestFolder.getPath());
+ return false;
+
+ }
+ if (!unzipDestFolder.renameTo(destFolder)) {
+ monitor.setResult("Failed to rename directory %1$s to %2$s",
+ unzipDestFolder.getPath(), destFolder.getPath());
+ return false;
+ }
+ }
+
+ return true;
+
+ } finally {
+ // Cleanup if the unzip folder is still set.
+ deleteFileOrFolder(renamedDestFolder);
+ if (unzipDestFolder != destFolder) {
+ deleteFileOrFolder(unzipDestFolder);
+ }
+ }
+ }
+
+ private boolean unzipFolder(File archiveFile,
+ long compressedSize,
+ File unzipDestFolder,
+ String description,
+ ITaskMonitor monitor) {
+
+ description += " (%1$d%%)";
+
+ FileInputStream fis = null;
+ ZipInputStream zis = null;
+ try {
+ fis = new FileInputStream(archiveFile);
+ zis = new ZipInputStream(fis);
+
+ // To advance the percent and the progress bar, we don't know the number of
+ // items left to unzip. However we know the size of the archive and the size of
+ // each uncompressed item. The zip file format overhead is negligible so that's
+ // a good approximation.
+ long incStep = compressedSize / NUM_MONITOR_INC;
+ long incTotal = 0;
+ long incCurr = 0;
+ int lastPercent = 0;
+
+ byte[] buf = new byte[65536];
+
+ ZipEntry entry;
+ while ((entry = zis.getNextEntry()) != null) {
+
+ String name = entry.getName();
+
+ // ZipFile entries should have forward slashes, but not all Zip
+ // implementations can be expected to do that.
+ name = name.replace('\\', '/');
+
+ File destFile = new File(unzipDestFolder, name);
+
+ if (name.endsWith("/")) { //$NON-NLS-1$
+ // Create directory if it doesn't exist yet. This allows us to create
+ // empty directories.
+ if (!destFile.isDirectory() && !destFile.mkdirs()) {
+ monitor.setResult("Failed to create temp directory %1$s",
+ destFile.getPath());
+ return false;
+ }
+ continue;
+ } else if (name.indexOf('/') != -1) {
+ // Otherwise it's a file in a sub-directory.
+ // Make sure the parent directory has been created.
+ File parentDir = destFile.getParentFile();
+ if (!parentDir.isDirectory()) {
+ if (!parentDir.mkdirs()) {
+ monitor.setResult("Failed to create temp directory %1$s",
+ parentDir.getPath());
+ return false;
+ }
+ }
+ }
+
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream(destFile);
+ int n;
+ while ((n = zis.read(buf)) != -1) {
+ if (n > 0) {
+ fos.write(buf, 0, n);
+ }
+ }
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+
+ // Increment progress bar to match. We update only between files.
+ for(incTotal += entry.getCompressedSize(); incCurr < incTotal; incCurr += incStep) {
+ monitor.incProgress(1);
+ }
+
+ int percent = (int) (100 * incTotal / compressedSize);
+ if (percent != lastPercent) {
+ monitor.setDescription(description, percent);
+ lastPercent = percent;
+ }
+
+ if (monitor.isCancelRequested()) {
+ return false;
+ }
+ }
+
+ return true;
+
+ } catch (IOException e) {
+ monitor.setResult("Unzip failed: %1$s", e.getMessage());
+
+ } finally {
+ if (zis != null) {
+ try {
+ zis.close();
+ } catch (IOException e) {
+ // pass
+ }
+ }
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ // pass
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Finds a temp folder which name is similar to the one of the ideal folder
+ * and with a ".tmpN" appended.
+ * <p/>
+ * This operation is not atomic so there's no guarantee the folder can't get
+ * created in between. This is however unlikely and the caller can assume the
+ * returned folder does not exist yet.
+ * <p/>
+ * Returns null if no such folder can be found (e.g. if all candidates exist),
+ * which is rather unlikely.
+ */
+ private File findTempFolder(File idealFolder, String suffix) {
+ String basePath = idealFolder.getPath();
+
+ for (int i = 1; i < 100; i++) {
+ File folder = new File(String.format("%1$s.%2$s%3$02d", basePath, suffix, i)); //$NON-NLS-1$
+ if (!folder.exists()) {
+ return folder;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Deletes a file or a directory.
+ * Directories are deleted recursively.
+ * The argument can be null.
+ */
+ private void deleteFileOrFolder(File fileOrFolder) {
+ if (fileOrFolder != null) {
+ if (fileOrFolder.isDirectory()) {
+ // Must delete content recursively first
+ for (File item : fileOrFolder.listFiles()) {
+ deleteFileOrFolder(item);
+ }
+ }
+ if (!fileOrFolder.delete()) {
+ fileOrFolder.deleteOnExit();
+ }
+ }
+ }
}
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
index 8f07255..e2c2cf5 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
@@ -49,6 +49,7 @@
DocPackage(RepoSource source,
int apiLevel,
int revision,
+ String license,
String description,
String descUrl,
Os archiveOs,
@@ -58,6 +59,7 @@
String archiveChecksum) {
super(source,
revision,
+ license,
description,
descUrl,
archiveOs,
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
index f150510..8d067f2 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
@@ -167,6 +167,7 @@
pkg = new ToolPackage(
null, //source
0, //revision
+ null, //license
"Tools", //description
null, //descUrl
Os.getCurrentOs(), //archiveOs
@@ -229,6 +230,7 @@
null, //source
0, //apiLevel
0, //revision
+ null, //license
String.format("Documentation for %1$s", found), //description
null, //descUrl
Os.getCurrentOs(), //archiveOs
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
index 55ecaef..4d28f08 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
@@ -40,6 +40,7 @@
public abstract class Package implements IDescription {
private final int mRevision;
+ private final String mLicense;
private final String mDescription;
private final String mDescUrl;
private final Archive[] mArchives;
@@ -55,7 +56,7 @@
mRevision = getXmlInt (packageNode, SdkRepository.NODE_REVISION, 0);
mDescription = getXmlString(packageNode, SdkRepository.NODE_DESCRIPTION);
mDescUrl = getXmlString(packageNode, SdkRepository.NODE_DESC_URL);
-
+ mLicense = getXmlString(packageNode, SdkRepository.NODE_LICENSE);
mArchives = parseArchives(getFirstChild(packageNode, SdkRepository.NODE_ARCHIVES));
}
@@ -65,6 +66,7 @@
*/
public Package(RepoSource source,
int revision,
+ String license,
String description,
String descUrl,
Os archiveOs,
@@ -74,6 +76,7 @@
String archiveChecksum) {
mSource = source;
mRevision = revision;
+ mLicense = license;
mDescription = description;
mDescUrl = descUrl;
mArchives = new Archive[1];
@@ -142,6 +145,14 @@
/**
* Returns the optional description for all packages (platform, add-on, tool, doc) or
+ * for a lib. It is null if the element has not been specified in the repository XML.
+ */
+ public String getLicense() {
+ return mLicense;
+ }
+
+ /**
+ * Returns the optional description for all packages (platform, add-on, tool, doc) or
* for a lib. Can be empty but not null.
*/
public String getDescription() {
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
index 0d51c58..ae6bc77 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
@@ -47,14 +47,15 @@
}
/**
- * Creates a new platform package based on an actual {@link IAndroidTarget} (with
+ * Creates a new platform package based on an actual {@link IAndroidTarget} (which
* must have {@link IAndroidTarget#isPlatform()} true) from the {@link SdkManager}.
* This is used to list local SDK folders.
*/
PlatformPackage(IAndroidTarget target) {
super( null, //source
0, //revision
- target.getDescription(), //description
+ null, //license
+ target.getDescription(), //description
null, //descUrl
Os.getCurrentOs(), //archiveOs
Arch.getCurrentArch(), //archiveArch
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
index 71e35c4..4cac706 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
@@ -44,6 +44,7 @@
*/
ToolPackage(RepoSource source,
int revision,
+ String license,
String description,
String descUrl,
Os archiveOs,
@@ -53,6 +54,7 @@
String archiveChecksum) {
super(source,
revision,
+ license,
description,
descUrl,
archiveOs,
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java
index 673e43f..4adcb49 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java
@@ -46,6 +46,8 @@
/** The revision, an int > 0, for all packages (platform, add-on, tool, doc). */
public static final String NODE_REVISION = "revision"; //$NON-NLS-1$
+ /** The optional license for all packages (platform, add-on, tool, doc) or for a lib. */
+ public static final String NODE_LICENSE = "license"; //$NON-NLS-1$
/** The optional description for all packages (platform, add-on, tool, doc) or for a lib. */
public static final String NODE_DESCRIPTION = "description"; //$NON-NLS-1$
/** The optional description URL for all packages (platform, add-on, tool, doc). */
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd
index 6aff444..1862ae8 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd
@@ -56,6 +56,9 @@
<!-- 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="license" type="xsd:string" minOccurs="0" />
<!-- The optional description of this package. -->
<xsd:element name="description" type="xsd:string" minOccurs="0" />
<!-- The optional description URL of this package -->
@@ -84,6 +87,9 @@
<!-- 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="license" type="xsd:string" minOccurs="0" />
<!-- The optional description of this package. -->
<xsd:element name="description" type="xsd:string" minOccurs="0" />
<!-- The optional description URL of this package -->
@@ -124,6 +130,9 @@
<!-- 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="license" type="xsd:string" minOccurs="0" />
<!-- The optional description of this package. -->
<xsd:element name="description" type="xsd:string" minOccurs="0" />
<!-- The optional description URL of this package -->
@@ -148,6 +157,9 @@
<!-- 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="license" type="xsd:string" minOccurs="0" />
<!-- The optional description of this package. -->
<xsd:element name="description" type="xsd:string" minOccurs="0" />
<!-- The optional description URL of this package -->
diff --git a/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml b/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml
index fb6283d..a2d0cda 100755
--- a/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml
+++ b/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml
@@ -26,6 +26,8 @@
<sdk:api-level>1</sdk:api-level>
<sdk:revision>3</sdk:revision>
<sdk:description>Some optional description</sdk:description>
+ <sdk:license>This is the license
+ for this platform.</sdk:license>
<sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url>
<!-- The archives node is mandatory and it cannot be empty. -->
<sdk:archives>
@@ -41,6 +43,7 @@
<sdk:api-level>1</sdk:api-level>
<sdk:revision>1</sdk:revision>
<sdk:description>Some optional description</sdk:description>
+ <!-- the license element is not mandatory. -->
<sdk:desc-url>http://www.example.com/docs.html</sdk:desc-url>
<sdk:archives>
<sdk:archive os="any">
@@ -56,6 +59,8 @@
<sdk:api-level>1</sdk:api-level>
<sdk:vendor>John Doe</sdk:vendor>
<sdk:revision>1</sdk:revision>
+ <!-- license can be empty. -->
+ <sdk:license></sdk:license>
<sdk:description>Some optional description</sdk:description>
<sdk:desc-url>http://www.example.com/myfirstaddon</sdk:desc-url>
<sdk:archives>
@@ -82,6 +87,7 @@
<sdk:version>1.1</sdk:version>
<sdk:api-level>2</sdk:api-level>
<sdk:revision>12</sdk:revision>
+ <sdk:license>This is the license for this package.</sdk:license>
<!-- sdk:description and sdk:desc-url are optional -->
<sdk:archives>
<sdk:archive os="windows">
@@ -139,12 +145,14 @@
<sdk:name>com.android.mymaps</sdk:name>
</sdk:lib>
</sdk:libs>
+ <sdk:license>This is the license for this package.</sdk:license>
</sdk:add-on>
<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:license>This is the license for this package.</sdk:license>
<sdk:archives>
<sdk:archive os="any">
<sdk:size>65536</sdk:size>
@@ -157,6 +165,7 @@
<sdk:doc>
<sdk:api-level>2</sdk:api-level>
<sdk:revision>42</sdk:revision>
+ <sdk:license>This is the license for this package.</sdk:license>
<sdk:archives>
<sdk:archive os="windows">
<sdk:size>65536</sdk:size>
@@ -178,6 +187,7 @@
<sdk:tool>
<sdk:revision>42</sdk:revision>
+ <sdk:license>This is the license for this package.</sdk:license>
<sdk:archives>
<sdk:archive os="windows">
<sdk:size>65536</sdk:size>
@@ -198,6 +208,7 @@
</sdk:tool>
<sdk:add-on>
+ <sdk:license>This is the license for this package.</sdk:license>
<sdk:name>This add-on has no libraries</sdk:name>
<sdk:api-level>4</sdk:api-level>
<sdk:vendor>Joe Bar</sdk:vendor>
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
index ddf53f0..ead8d61 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
@@ -21,7 +21,6 @@
import com.android.sdklib.internal.repository.Archive;
import com.android.sdklib.internal.repository.ITask;
import com.android.sdklib.internal.repository.ITaskMonitor;
-import com.android.sdklib.internal.repository.Package;
import com.android.sdklib.internal.repository.RepoSource;
import com.android.sdklib.repository.SdkRepository;
@@ -42,26 +41,16 @@
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
-import java.net.URL;
-import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
/**
* This is the private implementation of the UpdateWindow.
*/
public class UpdaterWindowImpl {
- private static final int NUM_MONITOR_INC = 100;
-
/** Internal data shared between the window and its pages. */
private final UpdaterData mUpdaterData = new UpdaterData();
/** The array of pages instances. Only one is visible at a time. */
@@ -351,7 +340,7 @@
mTaskFactory.start("Installing Archives", new ITask() {
public void run(ITaskMonitor monitor) {
- final int progressPerArchive = 2 * NUM_MONITOR_INC + 10;
+ final int progressPerArchive = 2 * Archive.NUM_MONITOR_INC;
monitor.setProgressMax(archives.size() * progressPerArchive);
monitor.setDescription("Preparing to install archives");
@@ -359,34 +348,20 @@
for (Archive archive : archives) {
int nextProgress = monitor.getProgress() + progressPerArchive;
- File archiveFile = null;
try {
if (monitor.isCancelRequested()) {
break;
}
- String name = archive.getParentPackage().getShortDescription();
-
- // TODO: we should not see this test fail if we had the filter UI above.
- if (!archive.isCompatible()) {
- monitor.setResult("Skipping incompatible archive: %1$s", name);
- continue;
+ if (archive.install(mUpdaterData.getOsSdkRoot(), monitor)) {
+ numInstalled++;
}
- archiveFile = downloadArchive(archive, monitor);
- if (archiveFile != null) {
- if (installArchive(archive, archiveFile, monitor)) {
- monitor.setResult("Installed: %1$s", name);
- numInstalled++;
- }
- }
} catch (Throwable t) {
// Display anything unexpected in the monitor.
monitor.setResult("Unexpected Error: %1$s", t.getMessage());
} finally {
- // Delete the temp archive if it exists
- deleteFileOrFolder(archiveFile);
// Always move the progress bar to the desired position.
// This allows internal methods to not have to care in case
@@ -406,417 +381,6 @@
});
}
- /**
- * Downloads an archive and returns the temp file with it.
- * Caller is responsible with deleting the temp file when done.
- */
- private File downloadArchive(Archive archive, ITaskMonitor monitor) {
-
- File tmpFileToDelete = null;
- try {
- File tmpFile = File.createTempFile("sdkupload", ".bin"); //$NON-NLS-1$ //$NON-NLS-2$
- tmpFileToDelete = tmpFile;
-
- String name = archive.getParentPackage().getShortDescription();
- String desc = String.format("Downloading %1$s", name);
- monitor.setDescription(desc);
-
- String link = archive.getUrl();
- if (!link.startsWith("http://") //$NON-NLS-1$
- && !link.startsWith("https://") //$NON-NLS-1$
- && !link.startsWith("ftp://")) { //$NON-NLS-1$
- // Make the URL absolute by prepending the source
- Package pkg = archive.getParentPackage();
- RepoSource src = pkg.getParentSource();
- if (src == null) {
- monitor.setResult("Internal error: no source for archive %1$s", name);
- return null;
- }
-
- // take the URL to the repository.xml and remove the last component
- // to get the base
- String repoXml = src.getUrl();
- int pos = repoXml.lastIndexOf('/');
- String base = repoXml.substring(0, pos + 1);
-
- link = base + link;
- }
-
- if (fetchUrl(tmpFile, archive, link, desc, monitor)) {
- // Fetching was successful, don't delete the temp file here!
- tmpFileToDelete = null;
- return tmpFile;
- }
-
- } catch (IOException e) {
- monitor.setResult(e.getMessage());
-
- } finally {
- deleteFileOrFolder(tmpFileToDelete);
- }
- return null;
- }
-
- /**
- * Actually performs the download.
- * Also computes the SHA1 of the file on the fly.
- * <p/>
- * Success is defined as downloading as many bytes as was expected and having the same
- * SHA1 as expected. Returns true on success or false if any of those checks fail.
- * <p/>
- * Increments the monitor by {@link #NUM_MONITOR_INC}.
- */
- private boolean fetchUrl(File tmpFile,
- Archive archive,
- String urlString,
- String description,
- ITaskMonitor monitor) {
- URL url;
-
- description += " (%1$d%%, %2$.0f KiB/s, %3$d %4$s left)";
-
- FileOutputStream os = null;
- InputStream is = null;
- try {
- url = new URL(urlString);
- is = url.openStream();
- os = new FileOutputStream(tmpFile);
-
- MessageDigest digester = archive.getChecksumType().getMessageDigest();
-
- byte[] buf = new byte[65536];
- int n;
-
- long total = 0;
- long size = archive.getSize();
- long inc = size / NUM_MONITOR_INC;
- long next_inc = inc;
-
- long startMs = System.currentTimeMillis();
- long nextMs = startMs + 2000; // start update after 2 seconds
-
- while ((n = is.read(buf)) >= 0) {
- if (n > 0) {
- os.write(buf, 0, n);
- digester.update(buf, 0, n);
- }
-
- long timeMs = System.currentTimeMillis();
-
- total += n;
- if (total >= next_inc) {
- monitor.incProgress(1);
- next_inc += inc;
- }
-
- if (timeMs > nextMs) {
- long delta = timeMs - startMs;
- if (total > 0 && delta > 0) {
- // percent left to download
- int percent = (int) (100 * total / size);
- // speed in KiB/s
- float speed = (float)total / (float)delta * (1000.f / 1024.f);
- // time left to download the rest at the current KiB/s rate
- int timeLeft = (speed > 1e-3) ?
- (int)(((size - total) / 1024.0f) / speed) :
- 0;
- String timeUnit = "seconds";
- if (timeLeft > 120) {
- timeUnit = "minutes";
- timeLeft /= 60;
- }
-
- monitor.setDescription(description, percent, speed, timeLeft, timeUnit);
- }
- nextMs = timeMs + 1000; // update every second
- }
-
- if (monitor.isCancelRequested()) {
- monitor.setResult("Download aborted by user at %1$d bytes.", total);
- return false;
- }
-
- }
-
- if (total != size) {
- monitor.setResult("Download finished with wrong size. Expected %1$d bytes, got %2$d bytes.",
- size, total);
- return false;
- }
-
- // Create an hex string from the digest
- byte[] digest = digester.digest();
- n = digest.length;
- String hex = "0123456789abcdef"; //$NON-NLS-1$
- char[] hexDigest = new char[n * 2];
- for (int i = 0; i < n; i++) {
- int b = digest[i] & 0x0FF;
- hexDigest[i*2 + 0] = hex.charAt(b >>> 4);
- hexDigest[i*2 + 1] = hex.charAt(b & 0x0f);
- }
-
- String expected = archive.getChecksum();
- String actual = new String(hexDigest);
- if (!actual.equalsIgnoreCase(expected)) {
- monitor.setResult("Download finished with wrong checksum. Expected %1$s, got %2$s.",
- expected, actual);
- return false;
- }
-
- return true;
-
- } catch (Exception e) {
- monitor.setResult(e.getMessage());
-
- } finally {
- if (os != null) {
- try {
- os.close();
- } catch (IOException e) {
- // pass
- }
- }
-
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- // pass
- }
- }
- }
-
- return false;
- }
-
- /**
- * Install the given archive in the given folder.
- */
- private boolean installArchive(Archive archive, File archiveFile, ITaskMonitor monitor) {
- String name = archive.getParentPackage().getShortDescription();
- String desc = String.format("Installing %1$s", name);
- monitor.setDescription(desc);
-
- File destFolder = archive.getParentPackage().getInstallFolder(mUpdaterData.getOsSdkRoot());
-
- File unzipDestFolder = destFolder;
- File renamedDestFolder = null;
-
- try {
- // If this folder already exists, unzip in a temporary folder and then move/unlink.
- if (destFolder.exists()) {
- // Find a new temp folder that doesn't exist yet
- unzipDestFolder = findTempFolder(destFolder, "new"); //$NON-NLS-1$
-
- if (unzipDestFolder == null) {
- // this should not seriously happen.
- monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",
- destFolder.getPath());
- return false;
- }
-
- if (!unzipDestFolder.mkdirs()) {
- monitor.setResult("Failed to create temp directory %1$s",
- unzipDestFolder.getPath());
- return false;
- }
- }
-
- if (!unzipFolder(archiveFile, archive.getSize(), unzipDestFolder, desc, monitor)) {
- return false;
- }
-
- if (unzipDestFolder != destFolder) {
- // Swap the old folder by the new one.
- // Both original folders will be deleted in the finally clause below.
- renamedDestFolder = findTempFolder(destFolder, "old"); //$NON-NLS-1$
- if (renamedDestFolder == null) {
- // this should not seriously happen.
- monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",
- destFolder.getPath());
- return false;
- }
-
- if (!destFolder.renameTo(renamedDestFolder)) {
- monitor.setResult("Failed to rename directory %1$s to %2$s",
- destFolder.getPath(), renamedDestFolder.getPath());
- return false;
-
- }
- if (!unzipDestFolder.renameTo(destFolder)) {
- monitor.setResult("Failed to rename directory %1$s to %2$s",
- unzipDestFolder.getPath(), destFolder.getPath());
- return false;
- }
- }
-
- return true;
-
- } finally {
- // Cleanup if the unzip folder is still set.
- deleteFileOrFolder(renamedDestFolder);
- if (unzipDestFolder != destFolder) {
- deleteFileOrFolder(unzipDestFolder);
- }
- }
- }
-
- private boolean unzipFolder(File archiveFile,
- long compressedSize,
- File unzipDestFolder,
- String description,
- ITaskMonitor monitor) {
-
- description += " (%1$d%%)";
-
- FileInputStream fis = null;
- ZipInputStream zis = null;
- try {
- fis = new FileInputStream(archiveFile);
- zis = new ZipInputStream(fis);
-
- // To advance the percent and the progress bar, we don't know the number of
- // items left to unzip. However we know the size of the archive and the size of
- // each uncompressed item. The zip file format overhead is negligible so that's
- // a good approximation.
- long incStep = compressedSize / NUM_MONITOR_INC;
- long incTotal = 0;
- long incCurr = 0;
- int lastPercent = 0;
-
- byte[] buf = new byte[65536];
-
- ZipEntry entry;
- while ((entry = zis.getNextEntry()) != null) {
-
- String name = entry.getName();
-
- // ZipFile entries should have forward slashes, but not all Zip
- // implementations can be expected to do that.
- name = name.replace('\\', '/');
-
- File destFile = new File(unzipDestFolder, name);
-
- if (name.endsWith("/")) { //$NON-NLS-1$
- // Create directory if it doesn't exist yet. This allows us to create
- // empty directories.
- if (!destFile.isDirectory() && !destFile.mkdirs()) {
- monitor.setResult("Failed to create temp directory %1$s",
- destFile.getPath());
- return false;
- }
- continue;
- } else if (name.indexOf('/') != -1) {
- // Otherwise it's a file in a sub-directory.
- // Make sure the parent directory has been created.
- File parentDir = destFile.getParentFile();
- if (!parentDir.isDirectory()) {
- if (!parentDir.mkdirs()) {
- monitor.setResult("Failed to create temp directory %1$s",
- parentDir.getPath());
- return false;
- }
- }
- }
-
- FileOutputStream fos = null;
- try {
- fos = new FileOutputStream(destFile);
- int n;
- while ((n = zis.read(buf)) != -1) {
- if (n > 0) {
- fos.write(buf, 0, n);
- }
- }
- } finally {
- if (fos != null) {
- fos.close();
- }
- }
-
- // Increment progress bar to match. We update only between files.
- for(incTotal += entry.getCompressedSize(); incCurr < incTotal; incCurr += incStep) {
- monitor.incProgress(1);
- }
-
- int percent = (int) (100 * incTotal / compressedSize);
- if (percent != lastPercent) {
- monitor.setDescription(description, percent);
- lastPercent = percent;
- }
-
- if (monitor.isCancelRequested()) {
- return false;
- }
- }
-
- return true;
-
- } catch (IOException e) {
- monitor.setResult("Unzip failed: %1$s", e.getMessage());
-
- } finally {
- if (zis != null) {
- try {
- zis.close();
- } catch (IOException e) {
- // pass
- }
- }
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException e) {
- // pass
- }
- }
- }
-
- return false;
- }
-
- /**
- * Finds a temp folder which name is similar to the one of the ideal folder
- * and with a ".tmpN" appended.
- * <p/>
- * This operation is not atomic so there's no guarantee the folder can't get
- * created in between. This is however unlikely and the caller can assume the
- * returned folder does not exist yet.
- * <p/>
- * Returns null if no such folder can be found (e.g. if all candidates exist),
- * which is rather unlikely.
- */
- private File findTempFolder(File idealFolder, String suffix) {
- String basePath = idealFolder.getPath();
-
- for (int i = 1; i < 100; i++) {
- File folder = new File(String.format("%1$s.%2$s%3$02d", basePath, suffix, i)); //$NON-NLS-1$
- if (!folder.exists()) {
- return folder;
- }
- }
- return null;
- }
-
- /**
- * Deletes a file or a directory.
- * Directories are deleted recursively.
- * The argument can be null.
- */
- private void deleteFileOrFolder(File fileOrFolder) {
- if (fileOrFolder != null) {
- if (fileOrFolder.isDirectory()) {
- // Must delete content recursively first
- for (File item : fileOrFolder.listFiles()) {
- deleteFileOrFolder(item);
- }
- }
- if (!fileOrFolder.delete()) {
- fileOrFolder.deleteOnExit();
- }
- }
- }
-
// End of hiding from SWT Designer
//$hide<<$
}