diff options
| author | Raphael <raphael@google.com> | 2009-08-16 13:07:30 -0700 |
|---|---|---|
| committer | Raphael <raphael@google.com> | 2009-08-17 12:10:30 -0700 |
| commit | 729fc9355d87bd72a99714e5fa4c2667c09bf255 (patch) | |
| tree | 7c057fdbbf0a79d11a58e9023068196f2f6e36a2 /tools | |
| parent | ecc1a3d0cf3af3dbbf4c190e1060fc1c0f8fb8a6 (diff) | |
| download | android_development-729fc9355d87bd72a99714e5fa4c2667c09bf255.tar.gz android_development-729fc9355d87bd72a99714e5fa4c2667c09bf255.tar.bz2 android_development-729fc9355d87bd72a99714e5fa4c2667c09bf255.zip | |
SDK Updater, restart ADB after updating tool/addon package.
There's a new setting for this too.
BUG 1922590
Diffstat (limited to 'tools')
6 files changed, 378 insertions, 16 deletions
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java index 8ab1364c8..c80005447 100755 --- a/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java +++ b/tools/sdkmanager/app/src/com/android/sdkmanager/internal/repository/SettingsPage.java @@ -48,6 +48,7 @@ public class SettingsPage extends Composite implements ISettingsPage { private Text mProxyServerText;
private Text mProxyPortText;
private Button mForceHttpCheck;
+ private Button mAskAdbRestartCheck;
private ModifyListener mSetApplyDirty = new ModifyListener() {
public void modifyText(ModifyEvent e) {
@@ -73,18 +74,26 @@ public class SettingsPage extends Composite implements ISettingsPage { mProxyServerLabel = new Label(mProxySettingsGroup, SWT.NONE);
mProxyServerLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
mProxyServerLabel.setText("HTTP Proxy Server");
+ String tooltip = "The DNS name or IP of the HTTP proxy server to use. " +
+ "When empty, no HTTP proxy is used.";
+ mProxyServerLabel.setToolTipText(tooltip);
mProxyServerText = new Text(mProxySettingsGroup, SWT.BORDER);
mProxyServerText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
mProxyServerText.addModifyListener(mSetApplyDirty);
+ mProxyServerText.setToolTipText(tooltip);
mProxyPortLabel = new Label(mProxySettingsGroup, SWT.NONE);
mProxyPortLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
mProxyPortLabel.setText("HTTP Proxy Port");
+ tooltip = "The port of the HTTP proxy server to use. " +
+ "When empty, the default for HTTP or HTTPS is used.";
+ mProxyPortLabel.setToolTipText(tooltip);
mProxyPortText = new Text(mProxySettingsGroup, SWT.BORDER);
mProxyPortText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
mProxyPortText.addModifyListener(mSetApplyDirty);
+ mProxyPortText.setToolTipText(tooltip);
mMiscGroup = new Group(this, SWT.NONE);
mMiscGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
@@ -92,7 +101,10 @@ public class SettingsPage extends Composite implements ISettingsPage { mMiscGroup.setLayout(new GridLayout(2, false));
mForceHttpCheck = new Button(mMiscGroup, SWT.CHECK);
+ mForceHttpCheck.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
mForceHttpCheck.setText("Force https://... sources to be fetched using http://...");
+ mForceHttpCheck.setToolTipText("If you are not able to connect to the official Android repository " +
+ "using HTTPS, enable this setting to force accessing it via HTTP.");
mForceHttpCheck.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
@@ -100,6 +112,18 @@ public class SettingsPage extends Composite implements ISettingsPage { }
});
+ mAskAdbRestartCheck = new Button(mMiscGroup, SWT.CHECK);
+ mAskAdbRestartCheck.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+ mAskAdbRestartCheck.setText("Ask before restarting ADB");
+ mAskAdbRestartCheck.setToolTipText("When checked, the user will be asked for permission " +
+ "to restart ADB after updating an addon-on package or a tool package.");
+ mAskAdbRestartCheck.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onForceHttpSelected(); //$hide$
+ }
+ });
+
mApplyButton = new Button(this, SWT.NONE);
mApplyButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
mApplyButton.setText("Save && Apply");
@@ -139,6 +163,7 @@ public class SettingsPage extends Composite implements ISettingsPage { mProxyServerText.setText(in_settings.getProperty(KEY_HTTP_PROXY_HOST, "")); //$NON-NLS-1$
mProxyPortText.setText( in_settings.getProperty(KEY_HTTP_PROXY_PORT, "")); //$NON-NLS-1$
mForceHttpCheck.setSelection(Boolean.parseBoolean(in_settings.getProperty(KEY_FORCE_HTTP)));
+ mAskAdbRestartCheck.setSelection(Boolean.parseBoolean(in_settings.getProperty(KEY_ASK_ADB_RESTART)));
// We loaded fresh settings so there's nothing dirty to apply
mApplyButton.setEnabled(false);
@@ -151,6 +176,8 @@ public class SettingsPage extends Composite implements ISettingsPage { out_settings.setProperty(KEY_HTTP_PROXY_PORT, mProxyPortText.getText());
out_settings.setProperty(KEY_FORCE_HTTP,
Boolean.toString(mForceHttpCheck.getSelection()));
+ out_settings.setProperty(KEY_ASK_ADB_RESTART,
+ Boolean.toString(mAskAdbRestartCheck.getSelection()));
}
/**
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AdbWrapper.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AdbWrapper.java new file mode 100755 index 000000000..cb553d119 --- /dev/null +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AdbWrapper.java @@ -0,0 +1,221 @@ +/*
+ * 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.android.sdkuilib.internal.repository;
+
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.repository.ITaskMonitor;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+/**
+ * A lightweight wrapper to start & stop ADB.
+ */
+public class AdbWrapper {
+
+ /*
+ * Note: we could bring ddmlib in SdkManager for that purpose, however this allows us to
+ * specialize the start/stop methods to our needs (e.g. a task monitor, etc.)
+ */
+
+ private final String mAdbOsLocation;
+ private final ITaskMonitor mMonitor;
+
+ /**
+ * Creates a new lightweight ADB wrapper.
+ *
+ * @param osSdkPath The root OS path of the SDK. Cannot be null.
+ * @param monitor A logger object. Cannot be null.
+ */
+ public AdbWrapper(String osSdkPath, ITaskMonitor monitor) {
+ mMonitor = monitor;
+
+ if (!osSdkPath.endsWith(File.separator)) {
+ osSdkPath += File.separator;
+ }
+ mAdbOsLocation = osSdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ADB;
+ }
+
+ private void display(String format, Object...args) {
+ mMonitor.setResult(format, args);
+ }
+
+ /**
+ * Starts the adb host side server.
+ * @return true if success
+ */
+ public synchronized boolean startAdb() {
+ if (mAdbOsLocation == null) {
+ display("Error: missing path to ADB."); //$NON-NLS-1$
+ return false;
+ }
+
+ Process proc;
+ int status = -1;
+
+ try {
+ String[] command = new String[2];
+ command[0] = mAdbOsLocation;
+ command[1] = "start-server"; //$NON-NLS-1$
+ proc = Runtime.getRuntime().exec(command);
+
+ ArrayList<String> errorOutput = new ArrayList<String>();
+ ArrayList<String> stdOutput = new ArrayList<String>();
+ status = grabProcessOutput(proc, errorOutput, stdOutput,
+ false /* waitForReaders */);
+
+ } catch (IOException ioe) {
+ display("Unable to run 'adb': %1$s.", ioe.getMessage()); //$NON-NLS-1$
+ // we'll return false;
+ } catch (InterruptedException ie) {
+ display("Unable to run 'adb': %1$s.", ie.getMessage()); //$NON-NLS-1$
+ // we'll return false;
+ }
+
+ if (status != 0) {
+ display("'adb start-server' failed."); //$NON-NLS-1$
+ return false;
+ }
+
+ display("'adb start-server' succeeded."); //$NON-NLS-1$
+
+ return true;
+ }
+
+ /**
+ * Stops the adb host side server.
+ * @return true if success
+ */
+ public synchronized boolean stopAdb() {
+ if (mAdbOsLocation == null) {
+ display("Error: missing path to ADB."); //$NON-NLS-1$
+ return false;
+ }
+
+ Process proc;
+ int status = -1;
+
+ try {
+ String[] command = new String[2];
+ command[0] = mAdbOsLocation;
+ command[1] = "kill-server"; //$NON-NLS-1$
+ proc = Runtime.getRuntime().exec(command);
+ status = proc.waitFor();
+ }
+ catch (IOException ioe) {
+ // we'll return false;
+ }
+ catch (InterruptedException ie) {
+ // we'll return false;
+ }
+
+ if (status != 0) {
+ display("'adb kill-server' failed -- run manually if necessary."); //$NON-NLS-1$
+ return false;
+ }
+
+ display("'adb kill-server' succeeded."); //$NON-NLS-1$
+ return true;
+ }
+
+ /**
+ * Get the stderr/stdout outputs of a process and return when the process is done.
+ * Both <b>must</b> be read or the process will block on windows.
+ * @param process The process to get the ouput from
+ * @param errorOutput The array to store the stderr output. cannot be null.
+ * @param stdOutput The array to store the stdout output. cannot be null.
+ * @param waitforReaders if true, this will wait for the reader threads.
+ * @return the process return code.
+ * @throws InterruptedException
+ */
+ private int grabProcessOutput(final Process process, final ArrayList<String> errorOutput,
+ final ArrayList<String> stdOutput, boolean waitforReaders)
+ throws InterruptedException {
+ assert errorOutput != null;
+ assert stdOutput != null;
+ // read the lines as they come. if null is returned, it's
+ // because the process finished
+ Thread t1 = new Thread("") { //$NON-NLS-1$
+ @Override
+ public void run() {
+ // create a buffer to read the stderr output
+ InputStreamReader is = new InputStreamReader(process.getErrorStream());
+ BufferedReader errReader = new BufferedReader(is);
+
+ try {
+ while (true) {
+ String line = errReader.readLine();
+ if (line != null) {
+ display("ADB Error: %1$s", line);
+ errorOutput.add(line);
+ } else {
+ break;
+ }
+ }
+ } catch (IOException e) {
+ // do nothing.
+ }
+ }
+ };
+
+ Thread t2 = new Thread("") { //$NON-NLS-1$
+ @Override
+ public void run() {
+ InputStreamReader is = new InputStreamReader(process.getInputStream());
+ BufferedReader outReader = new BufferedReader(is);
+
+ try {
+ while (true) {
+ String line = outReader.readLine();
+ if (line != null) {
+ display("ADB: %1$s", line);
+ stdOutput.add(line);
+ } else {
+ break;
+ }
+ }
+ } catch (IOException e) {
+ // do nothing.
+ }
+ }
+ };
+
+ t1.start();
+ t2.start();
+
+ // it looks like on windows process#waitFor() can return
+ // before the thread have filled the arrays, so we wait for both threads and the
+ // process itself.
+ if (waitforReaders) {
+ try {
+ t1.join();
+ } catch (InterruptedException e) {
+ }
+ try {
+ t2.join();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ // get the return code from the process
+ return process.waitFor();
+ }
+
+}
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java index 78b04f333..0d7179ce6 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java @@ -24,14 +24,34 @@ import java.util.Properties; */
public interface ISettingsPage {
- /** Java system setting picked up by {@link URL} for http proxy port. Type: String. */
+ /**
+ * Java system setting picked up by {@link URL} for http proxy port.
+ * Type: String.
+ */
public static final String KEY_HTTP_PROXY_PORT = "http.proxyPort"; //$NON-NLS-1$
- /** Java system setting picked up by {@link URL} for http proxy host. Type: String. */
+ /**
+ * Java system setting picked up by {@link URL} for http proxy host.
+ * Type: String.
+ */
public static final String KEY_HTTP_PROXY_HOST = "http.proxyHost"; //$NON-NLS-1$
- /** Setting to force using http:// instead of https:// connections. Type: Boolean. */
+ /**
+ * Setting to force using http:// instead of https:// connections.
+ * Type: Boolean.
+ * Default: False.
+ */
public static final String KEY_FORCE_HTTP = "sdkman.force.http"; //$NON-NLS-1$
- /** Setting to display only packages that are new or updates. Type: Boolean. */
+ /**
+ * Setting to display only packages that are new or updates.
+ * Type: Boolean.
+ * Default: True.
+ */
public static final String KEY_SHOW_UPDATE_ONLY = "sdkman.show.update.only"; //$NON-NLS-1$
+ /**
+ * Setting to ask for permission before restarting ADB.
+ * Type: Boolean.
+ * Default: True.
+ */
+ public static final String KEY_ASK_ADB_RESTART = "sdkman.ask.adb.restart"; //$NON-NLS-1$
/** Loads settings from the given {@link Properties} container and update the page UI. */
public abstract void loadSettings(Properties in_settings);
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java index 8f5c4d2b5..1898f666c 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java @@ -38,13 +38,11 @@ import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Tree;
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java index b6363d304..a4b1a05de 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java @@ -26,7 +26,11 @@ import java.io.IOException; import java.util.Properties;
/**
- *
+ * Controller class to get settings values. Settings are kept in-memory.
+ * Users of this class must first load the settings before changing them and save
+ * them when modified.
+ * <p/>
+ * Settings are enumerated by constants in {@link ISettingsPage}.
*/
public class SettingsController {
@@ -41,10 +45,30 @@ public class SettingsController { //--- Access to settings ------------
+ /**
+ * Returns the value of the ISettingsPage#KEY_FORCE_HTTP setting.
+ * @see ISettingsPage#KEY_FORCE_HTTP
+ */
public boolean getForceHttp() {
return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP));
}
+ /**
+ * Returns the value of the ISettingsPage#KEY_ASK_ADB_RESTART setting.
+ * @see ISettingsPage#KEY_ASK_ADB_RESTART
+ */
+ public boolean getAskBeforeAdbRestart() {
+ String value = mProperties.getProperty(ISettingsPage.KEY_ASK_ADB_RESTART);
+ if (value == null) {
+ return true;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ /**
+ * Returns the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting.
+ * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY
+ */
public boolean getShowUpdateOnly() {
String value = mProperties.getProperty(ISettingsPage.KEY_SHOW_UPDATE_ONLY);
if (value == null) {
@@ -53,8 +77,20 @@ public class SettingsController { return Boolean.parseBoolean(value);
}
+ /**
+ * Sets the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting.
+ * @param enabled True if only compatible update items should be shown.
+ * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY
+ */
public void setShowUpdateOnly(boolean enabled) {
- mProperties.setProperty(ISettingsPage.KEY_SHOW_UPDATE_ONLY, Boolean.toString(enabled));
+ setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled);
+ }
+
+ /**
+ * Internal helper to set a boolean setting.
+ */
+ private void setSetting(String key, boolean value) {
+ mProperties.setProperty(key, Boolean.toString(value));
}
//--- Controller methods -------------
@@ -89,6 +125,10 @@ public class SettingsController { fis = new FileInputStream(f);
mProperties.load(fis);
+
+ // Properly reformat some settings to enforce their default value when missing.
+ setShowUpdateOnly(getShowUpdateOnly());
+ setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, getAskBeforeAdbRestart());
}
} catch (AndroidLocationException e) {
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java index a605e3d73..bec00f8c4 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java @@ -34,6 +34,8 @@ import com.android.sdklib.internal.repository.Package.UpdateInfo; import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import java.io.ByteArrayOutputStream;
@@ -294,14 +296,15 @@ class UpdaterData { break;
}
- if (archive.getParentPackage() instanceof AddonPackage) {
- installedAddon = true;
- } else if (archive.getParentPackage() instanceof ToolPackage) {
- installedTools = true;
- }
-
if (archive.install(mOsSdkRoot, forceHttp, mSdkManager, monitor)) {
numInstalled++;
+
+ // Check if we successfully installed a tool or add-on package.
+ if (archive.getParentPackage() instanceof AddonPackage) {
+ installedAddon = true;
+ } else if (archive.getParentPackage() instanceof ToolPackage) {
+ installedTools = true;
+ }
}
} catch (Throwable t) {
@@ -334,8 +337,10 @@ class UpdaterData { // Update the USB vendor ids for adb
try {
mSdkManager.updateAdb();
+ monitor.setResult("Updated ADB to support the USB devices declared in the SDK add-ons.");
} catch (Exception e) {
mSdkLog.error(e, "Update ADB failed");
+ monitor.setResult("failed to update adb to support the USB devices declared in the SDK add-ons.");
}
}
@@ -346,8 +351,11 @@ class UpdaterData { // before updating the tools folder, as adb.exe is (surprisingly) not
// locked.
- // TODO either bring in ddmlib and use its existing methods to stop adb
- // or use a shell exec to tools/adb.
+ askForAdbRestart(monitor);
+ }
+
+ if (installedTools) {
+ notifyToolsNeedsToBeRestarted();
}
if (numInstalled == 0) {
@@ -365,6 +373,54 @@ class UpdaterData { }
/**
+ * Attemps to restart ADB.
+ *
+ * If the "ask before restart" setting is set (the default), prompt the user whether
+ * now is a good time to restart ADB.
+ * @param monitor
+ */
+ private void askForAdbRestart(ITaskMonitor monitor) {
+ final boolean[] canRestart = new boolean[] { true };
+
+ if (getSettingsController().getAskBeforeAdbRestart()) {
+ // need to ask for permission first
+ Display display = mWindowShell.getDisplay();
+
+ display.syncExec(new Runnable() {
+ public void run() {
+ canRestart[0] = MessageDialog.openQuestion(mWindowShell,
+ "ADB Restart",
+ "A package that depends on ADB has been updated. It is recommended " +
+ "to restart ADB. Is it OK to do it now? If not, you can restart it " +
+ "manually later.");
+ }
+ });
+ }
+
+ if (canRestart[0]) {
+ AdbWrapper adb = new AdbWrapper(getOsSdkRoot(), monitor);
+ adb.stopAdb();
+ adb.startAdb();
+ }
+ }
+
+ private void notifyToolsNeedsToBeRestarted() {
+ Display display = mWindowShell.getDisplay();
+
+ display.syncExec(new Runnable() {
+ public void run() {
+ MessageDialog.openInformation(mWindowShell,
+ "Android Tools Updated",
+ "The Android SDK tool that you are currently using has been updated. " +
+ "It is recommended that you now close the Android SDK window and re-open it. " +
+ "If you started this window from Eclipse, please check if the Android " +
+ "plug-in needs to be updated.");
+ }
+ });
+ }
+
+
+ /**
* Tries to update all the *existing* local packages.
* This first refreshes all sources, then compares the available remote packages with
* the current local ones and suggest updates to be done to the user. Finally all
|
