mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-51833 adb operations should be done in non-ui thread with timeout
This commit is contained in:
@@ -122,6 +122,7 @@ public class AndroidFacet extends Facet<AndroidFacetConfiguration> {
|
||||
mySystemResourceManager = null;
|
||||
}
|
||||
|
||||
// can be invoked only from dispatch thread!
|
||||
@Nullable
|
||||
public AndroidDebugBridge getDebugBridge() {
|
||||
AndroidPlatform platform = getConfiguration().getAndroidPlatform();
|
||||
|
||||
@@ -192,6 +192,16 @@ public abstract class AndroidRunConfigurationBase extends ModuleBasedConfigurati
|
||||
|
||||
Project project = env.getProject();
|
||||
|
||||
AndroidFacetConfiguration configuration = facet.getConfiguration();
|
||||
AndroidPlatform platform = configuration.getAndroidPlatform();
|
||||
if (platform == null) {
|
||||
Messages.showErrorDialog(project, AndroidBundle.message("specify.platform.error"), CommonBundle.getErrorTitle());
|
||||
ModulesConfigurator.showFacetSettingsDialog(facet, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (platform.getSdk().getDebugBridge(getProject()) == null) return null;
|
||||
|
||||
boolean debug = DefaultDebugExecutor.EXECUTOR_ID.equals(executor.getId());
|
||||
if (debug) {
|
||||
if (!activateDdmsIfNeccessary(facet)) {
|
||||
@@ -204,47 +214,37 @@ public abstract class AndroidRunConfigurationBase extends ModuleBasedConfigurati
|
||||
}
|
||||
}
|
||||
|
||||
AndroidFacetConfiguration configuration = facet.getConfiguration();
|
||||
AndroidPlatform platform = configuration.getAndroidPlatform();
|
||||
if (platform == null) {
|
||||
Messages.showErrorDialog(project, AndroidBundle.message("specify.platform.error"), CommonBundle.getErrorTitle());
|
||||
ModulesConfigurator.showFacetSettingsDialog(facet, null);
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
String aPackage = getPackageName(facet);
|
||||
if (aPackage == null) return null;
|
||||
String aPackage = getPackageName(facet);
|
||||
if (aPackage == null) return null;
|
||||
|
||||
Map<AndroidFacet, String> depModule2PackageName = new HashMap<AndroidFacet, String>();
|
||||
if (!fillRuntimeAndTestDependencies(module, depModule2PackageName)) return null;
|
||||
Map<AndroidFacet, String> depModule2PackageName = new HashMap<AndroidFacet, String>();
|
||||
if (!fillRuntimeAndTestDependencies(module, depModule2PackageName)) return null;
|
||||
|
||||
if (platform.getSdk().getDebugBridge(project) == null) return null;
|
||||
IDevice[] targetDevices = new IDevice[0];
|
||||
if (CHOOSE_DEVICE_MANUALLY) {
|
||||
IDevice[] devices = chooseDevicesManually(facet);
|
||||
if (devices.length > 0) {
|
||||
if (debug && containsRealDevice(devices)) {
|
||||
if (!checkDebuggableOption(facet)) {
|
||||
return null;
|
||||
}
|
||||
IDevice[] targetDevices = new IDevice[0];
|
||||
if (CHOOSE_DEVICE_MANUALLY) {
|
||||
IDevice[] devices = chooseDevicesManually(facet);
|
||||
if (devices.length > 0) {
|
||||
if (debug && containsRealDevice(devices)) {
|
||||
if (!checkDebuggableOption(facet)) {
|
||||
return null;
|
||||
}
|
||||
targetDevices = devices;
|
||||
PropertiesComponent.getInstance(getProject()).setValue(ANDROID_TARGET_DEVICES_PROPERTY, toString(targetDevices));
|
||||
}
|
||||
if (targetDevices.length == 0) return null;
|
||||
targetDevices = devices;
|
||||
PropertiesComponent.getInstance(getProject()).setValue(ANDROID_TARGET_DEVICES_PROPERTY, toString(targetDevices));
|
||||
}
|
||||
AndroidApplicationLauncher applicationLauncher = getApplicationLauncher(facet);
|
||||
if (applicationLauncher != null) {
|
||||
return new AndroidRunningState(env, facet, targetDevices, PREFERRED_AVD.length() > 0 ? PREFERRED_AVD : null,
|
||||
computeCommandLine(), aPackage, applicationLauncher, depModule2PackageName) {
|
||||
if (targetDevices.length == 0) return null;
|
||||
}
|
||||
AndroidApplicationLauncher applicationLauncher = getApplicationLauncher(facet);
|
||||
if (applicationLauncher != null) {
|
||||
return new AndroidRunningState(env, facet, targetDevices, PREFERRED_AVD.length() > 0 ? PREFERRED_AVD : null,
|
||||
computeCommandLine(), aPackage, applicationLauncher, depModule2PackageName) {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected ConsoleView attachConsole() throws ExecutionException {
|
||||
return AndroidRunConfigurationBase.this.attachConsole(this, executor);
|
||||
}
|
||||
};
|
||||
}
|
||||
@NotNull
|
||||
@Override
|
||||
protected ConsoleView attachConsole() throws ExecutionException {
|
||||
return AndroidRunConfigurationBase.this.attachConsole(this, executor);
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -296,16 +296,22 @@ public abstract class AndroidRunConfigurationBase extends ModuleBasedConfigurati
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean isDdmsCorrupted(@NotNull AndroidFacet facet) {
|
||||
// can be invoked only from dispatch thread!
|
||||
private static boolean isDdmsCorrupted(@NotNull AndroidFacet facet) {
|
||||
AndroidDebugBridge bridge = facet.getDebugBridge();
|
||||
if (bridge != null) {
|
||||
IDevice[] devices = bridge.getDevices();
|
||||
if (devices.length > 0) {
|
||||
Client[] clients = devices[0].getClients();
|
||||
if (clients.length > 0) {
|
||||
ClientData clientData = clients[0].getClientData();
|
||||
return clientData == null || clientData.getVmIdentifier() == null;
|
||||
}
|
||||
return isDdmsCorrupted(bridge);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean isDdmsCorrupted(@NotNull AndroidDebugBridge bridge) {
|
||||
IDevice[] devices = bridge.getDevices();
|
||||
if (devices.length > 0) {
|
||||
Client[] clients = devices[0].getClients();
|
||||
if (clients.length > 0) {
|
||||
ClientData clientData = clients[0].getClientData();
|
||||
return clientData == null || clientData.getVmIdentifier() == null;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -254,7 +254,7 @@ public abstract class AndroidRunningState implements RunProfileState, AndroidDeb
|
||||
|
||||
@Nullable
|
||||
private IDevice chooseDeviceAutomaticaly() {
|
||||
final AndroidDebugBridge bridge = myFacet.getDebugBridge();
|
||||
final AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
|
||||
if (bridge == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -551,7 +551,8 @@ public abstract class AndroidRunningState implements RunProfileState, AndroidDeb
|
||||
}
|
||||
|
||||
private boolean checkDdms() {
|
||||
if (myDebugMode && AndroidRunConfigurationBase.isDdmsCorrupted(myFacet)) {
|
||||
AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
|
||||
if (myDebugMode && bridge != null && AndroidRunConfigurationBase.isDdmsCorrupted(bridge)) {
|
||||
message("Debug info is not available. Please close other application using ADB: DDMS, Eclipse", STDERR);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ import com.android.sdklib.ISdkLog;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
import com.android.sdklib.SdkManager;
|
||||
import com.intellij.CommonBundle;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
@@ -48,6 +52,8 @@ import static org.jetbrains.android.util.AndroidUtils.ADB;
|
||||
public abstract class AndroidSdk {
|
||||
private static volatile boolean myDdmLibInitialized = false;
|
||||
|
||||
private static volatile boolean myAdbCrashed = false;
|
||||
|
||||
private static final Object myDdmsLock = new Object();
|
||||
|
||||
@NotNull
|
||||
@@ -138,7 +144,77 @@ public abstract class AndroidSdk {
|
||||
return getLocation().hashCode();
|
||||
}
|
||||
|
||||
public void initializeDdmlib() {
|
||||
private boolean initializeDdmlib(@NotNull Project project) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
|
||||
while (true) {
|
||||
final MyInitializeDdmlibTask task = new MyInitializeDdmlibTask(project);
|
||||
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doInitializeDdmlib();
|
||||
task.finish();
|
||||
}
|
||||
});
|
||||
|
||||
t.start();
|
||||
|
||||
boolean retryWas = false;
|
||||
|
||||
while (!task.isFinished()) {
|
||||
ProgressManager.getInstance().run(task);
|
||||
|
||||
boolean finished = task.isFinished();
|
||||
|
||||
myAdbCrashed = !finished;
|
||||
|
||||
if (task.isCanceled()) {
|
||||
forceInterrupt(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!finished) {
|
||||
int result = Messages
|
||||
.showDialog(project, "ADB not responding. Please, kill \"" + SdkConstants.FN_ADB + "\" process manually and click 'Retry'",
|
||||
CommonBundle.getErrorTitle(), new String[]{"&Retry", "&Cancel"}, 0, Messages.getErrorIcon());
|
||||
|
||||
if (result == 1) {
|
||||
forceInterrupt(t);
|
||||
return false;
|
||||
}
|
||||
retryWas = true;
|
||||
}
|
||||
}
|
||||
|
||||
// task finished, but if we had problems, ddmlib can be still initialized incorrectly, so we invoke initialize once again
|
||||
if (!retryWas) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"BusyWait"})
|
||||
private static void forceInterrupt(Thread thread) {
|
||||
/*
|
||||
ddmlib has incorrect handling of InterruptedException, so we need to invoke it several times,
|
||||
because there are three blocking invokation in succession
|
||||
*/
|
||||
|
||||
for (int i = 0; i < 6 && thread.isAlive(); i++) {
|
||||
thread.interrupt();
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doInitializeDdmlib() {
|
||||
synchronized (myDdmsLock) {
|
||||
String adbPath = getAdbPath();
|
||||
if (!myDdmLibInitialized) {
|
||||
@@ -148,7 +224,7 @@ public abstract class AndroidSdk {
|
||||
AndroidDebugBridge.createBridge(adbPath, true);
|
||||
}
|
||||
else {
|
||||
AndroidDebugBridge.createBridge(adbPath, false);
|
||||
AndroidDebugBridge.createBridge(adbPath, myAdbCrashed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,8 +246,10 @@ public abstract class AndroidSdk {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AndroidDebugBridge getDebugBridge(Project project) {
|
||||
initializeDdmlib();
|
||||
public AndroidDebugBridge getDebugBridge(@NotNull Project project) {
|
||||
if (!initializeDdmlib(project)) {
|
||||
return null;
|
||||
}
|
||||
return AndroidDebugBridge.getBridge();
|
||||
}
|
||||
|
||||
@@ -188,4 +266,64 @@ public abstract class AndroidSdk {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class MyInitializeDdmlibTask extends Task.Modal {
|
||||
private final Object myLock = new Object();
|
||||
private volatile boolean myFinished;
|
||||
private volatile boolean myCanceled;
|
||||
|
||||
public MyInitializeDdmlibTask(Project project) {
|
||||
super(project, "Connecting to ADB process", true);
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
synchronized (myLock) {
|
||||
return myFinished;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
synchronized (myLock) {
|
||||
return myCanceled;
|
||||
}
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
synchronized (myLock) {
|
||||
myFinished = true;
|
||||
myLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
synchronized (myLock) {
|
||||
myCanceled = true;
|
||||
myLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
indicator.setIndeterminate(true);
|
||||
synchronized (myLock) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
final long timeout = 7000;
|
||||
|
||||
while (!myFinished && !myCanceled) {
|
||||
long wastedTime = System.currentTimeMillis() - startTime;
|
||||
if (wastedTime >= timeout) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
myLock.wait(timeout - wastedTime);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user