blob: e99a637be911737685d048f835c34aba422d5a13 [file] [log] [blame]
/*
* Copyright (C) 2011 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.ide.eclipse.ddms;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.Log.LogLevel;
import com.android.ddmlib.logcat.LogCatMessage;
import com.android.ddmuilib.logcat.ILogCatBufferChangeListener;
import com.android.ddmuilib.logcat.LogCatReceiver;
import com.android.ddmuilib.logcat.LogCatReceiverFactory;
import com.android.ide.eclipse.ddms.views.LogCatView;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* LogCatMonitor helps in monitoring the logcat output from a set of devices.
* It scans through the received logcat messages, and activates the logcat view
* if any message is deemed important.
*/
public class LogCatMonitor {
public static final String AUTO_MONITOR_PREFKEY = "ddms.logcat.automonitor"; //$NON-NLS-1$
public static final String AUTO_MONITOR_LOGLEVEL = "ddms.logcat.auotmonitor.level"; //$NON-NLS-1$
private static final String AUTO_MONITOR_PROMPT_SHOWN = "ddms.logcat.automonitor.userprompt"; //$NON-NLS-1$
private IPreferenceStore mPrefStore;
private Map<String, DeviceData> mMonitoredDevices;
private IDebuggerConnector[] mConnectors;
private int mMinMessagePriority;
/**
* Flag that controls when the logcat stream is checked. This flag is set when the user
* performs a launch, and is reset as soon as the logcat view is displayed.
*/
final AtomicBoolean mMonitorEnabled = new AtomicBoolean(false);
public LogCatMonitor(IDebuggerConnector[] debuggerConnectors, IPreferenceStore prefStore) {
mConnectors = debuggerConnectors;
mPrefStore = prefStore;
mMinMessagePriority =
LogLevel.getByString(mPrefStore.getString(AUTO_MONITOR_LOGLEVEL)).getPriority();
mMonitoredDevices = new HashMap<String, DeviceData>();
AndroidDebugBridge.addDeviceChangeListener(new IDeviceChangeListener() {
@Override
public void deviceDisconnected(IDevice device) {
unmonitorDevice(device.getSerialNumber());
mMonitoredDevices.remove(device.getSerialNumber());
}
@Override
public void deviceConnected(IDevice device) {
}
@Override
public void deviceChanged(IDevice device, int changeMask) {
}
});
mPrefStore.addPropertyChangeListener(new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
if (AUTO_MONITOR_PREFKEY.equals(event.getProperty())
&& event.getNewValue().equals(false)) {
unmonitorAllDevices();
} else if (AUTO_MONITOR_LOGLEVEL.equals(event.getProperty())) {
mMinMessagePriority =
LogLevel.getByString((String) event.getNewValue()).getPriority();
}
}
});
}
private void unmonitorAllDevices() {
for (String device : mMonitoredDevices.keySet()) {
unmonitorDevice(device);
}
mMonitoredDevices.clear();
}
private void unmonitorDevice(String deviceSerial) {
DeviceData data = mMonitoredDevices.get(deviceSerial);
if (data == null) {
return;
}
data.receiver.removeMessageReceivedEventListener(data.bufferChangeListener);
}
public void monitorDevice(final IDevice device) {
if (!mPrefStore.getBoolean(AUTO_MONITOR_PREFKEY)) {
// do not monitor device if auto monitoring is off
return;
}
mMonitorEnabled.set(true);
if (mMonitoredDevices.keySet().contains(device.getSerialNumber())) {
// the device is already monitored
return;
}
LogCatReceiver r = LogCatReceiverFactory.INSTANCE.newReceiver(device, mPrefStore);
ILogCatBufferChangeListener l = new ILogCatBufferChangeListener() {
@Override
public void bufferChanged(List<LogCatMessage> addedMessages,
List<LogCatMessage> deletedMessages) {
checkMessages(addedMessages, device);
}
};
r.addMessageReceivedEventListener(l);
mMonitoredDevices.put(device.getSerialNumber(), new DeviceData(r, l));
}
private void checkMessages(List<LogCatMessage> receivedMessages, IDevice device) {
if (!mMonitorEnabled.get()) {
return;
}
// check the received list of messages to see if any of them are
// significant enough to be seen by the user. If so, activate the logcat view
// to display those messages
for (LogCatMessage m : receivedMessages) {
if (isImportantMessage(m)) {
focusLogCatView(device, m.getAppName());
// now that logcat view is active, no need to check messages until the next
// time user launches an application.
mMonitorEnabled.set(false);
break;
}
}
}
/**
* Check whether a message is "important". Currently, we assume that a message is important if
* it is of severity level error or higher, and it belongs to an app currently in the workspace.
*/
private boolean isImportantMessage(LogCatMessage m) {
if (m.getLogLevel().getPriority() < mMinMessagePriority) {
return false;
}
String app = m.getAppName();
for (IDebuggerConnector c : mConnectors) {
if (c.isWorkspaceApp(app)) {
return true;
}
}
return false;
}
private void focusLogCatView(final IDevice device, final String appName) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window == null) {
return;
}
IWorkbenchPage page = window.getActivePage();
if (page == null) {
return;
}
// if the logcat view is not visible, then prompt the user once to set
// logcat monitoring preferences
if (!isLogCatViewVisible(page)) {
boolean showLogCatView = promptUserOnce(page.getWorkbenchWindow().getShell());
if (!showLogCatView) {
return;
}
}
// display view
final LogCatView v = displayLogCatView(page);
if (v == null) {
return;
}
// select correct device
v.selectionChanged(device);
// select appropriate filter
v.selectTransientAppFilter(appName);
}
private boolean isLogCatViewVisible(IWorkbenchPage page) {
IViewPart view = page.findView(LogCatView.ID);
return view != null && page.isPartVisible(view);
}
private LogCatView displayLogCatView(IWorkbenchPage page) {
// if the view is already in the page, just bring it to the front
// without giving it focus.
IViewPart view = page.findView(LogCatView.ID);
if (view != null) {
page.bringToTop(view);
if (view instanceof LogCatView) {
return (LogCatView)view;
}
}
// if the view is not in the page, then create and show it.
try {
return (LogCatView) page.showView(LogCatView.ID);
} catch (PartInitException e) {
return null;
}
}
private boolean promptUserOnce(Shell shell) {
// see if this prompt was already displayed
boolean promptShown = mPrefStore.getBoolean(AUTO_MONITOR_PROMPT_SHOWN);
if (promptShown) {
return mPrefStore.getBoolean(AUTO_MONITOR_PREFKEY);
}
LogCatMonitorDialog dlg = new LogCatMonitorDialog(shell);
int r = dlg.open();
// save preference indicating that this dialog has been displayed once
mPrefStore.setValue(AUTO_MONITOR_PROMPT_SHOWN, true);
mPrefStore.setValue(AUTO_MONITOR_PREFKEY, dlg.shouldMonitor());
mPrefStore.setValue(AUTO_MONITOR_LOGLEVEL, dlg.getMinimumPriority());
return r == Window.OK && dlg.shouldMonitor();
}
});
}
private static class DeviceData {
public final LogCatReceiver receiver;
public final ILogCatBufferChangeListener bufferChangeListener;
public DeviceData(LogCatReceiver r, ILogCatBufferChangeListener l) {
receiver = r;
bufferChangeListener = l;
}
}
}