Home | History | Annotate | Download | only in ddms
      1 /*
      2  * Copyright (C) 2011 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 package com.android.ide.eclipse.ddms;
     17 
     18 import com.android.ddmlib.AndroidDebugBridge;
     19 import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
     20 import com.android.ddmlib.IDevice;
     21 import com.android.ddmlib.Log.LogLevel;
     22 import com.android.ddmlib.logcat.LogCatMessage;
     23 import com.android.ddmuilib.logcat.ILogCatBufferChangeListener;
     24 import com.android.ddmuilib.logcat.LogCatReceiver;
     25 import com.android.ddmuilib.logcat.LogCatReceiverFactory;
     26 import com.android.ide.eclipse.ddms.views.LogCatView;
     27 
     28 import org.eclipse.jface.preference.IPreferenceStore;
     29 import org.eclipse.jface.util.IPropertyChangeListener;
     30 import org.eclipse.jface.util.PropertyChangeEvent;
     31 import org.eclipse.jface.window.Window;
     32 import org.eclipse.swt.widgets.Display;
     33 import org.eclipse.swt.widgets.Shell;
     34 import org.eclipse.ui.IViewPart;
     35 import org.eclipse.ui.IWorkbenchPage;
     36 import org.eclipse.ui.IWorkbenchWindow;
     37 import org.eclipse.ui.PartInitException;
     38 import org.eclipse.ui.PlatformUI;
     39 
     40 import java.util.HashMap;
     41 import java.util.List;
     42 import java.util.Map;
     43 import java.util.concurrent.atomic.AtomicBoolean;
     44 
     45 /**
     46  * LogCatMonitor helps in monitoring the logcat output from a set of devices.
     47  * It scans through the received logcat messages, and activates the logcat view
     48  * if any message is deemed important.
     49  */
     50 public class LogCatMonitor {
     51     public static final String AUTO_MONITOR_PREFKEY = "ddms.logcat.automonitor"; //$NON-NLS-1$
     52     public static final String AUTO_MONITOR_LOGLEVEL = "ddms.logcat.auotmonitor.level"; //$NON-NLS-1$
     53     private static final String AUTO_MONITOR_PROMPT_SHOWN = "ddms.logcat.automonitor.userprompt"; //$NON-NLS-1$
     54 
     55     private IPreferenceStore mPrefStore;
     56     private Map<String, DeviceData> mMonitoredDevices;
     57     private IDebuggerConnector[] mConnectors;
     58 
     59     private int mMinMessagePriority;
     60 
     61     /**
     62      * Flag that controls when the logcat stream is checked. This flag is set when the user
     63      * performs a launch, and is reset as soon as the logcat view is displayed.
     64      */
     65     final AtomicBoolean mMonitorEnabled = new AtomicBoolean(false);
     66 
     67     public LogCatMonitor(IDebuggerConnector[] debuggerConnectors, IPreferenceStore prefStore) {
     68         mConnectors = debuggerConnectors;
     69         mPrefStore = prefStore;
     70         mMinMessagePriority =
     71                 LogLevel.getByString(mPrefStore.getString(AUTO_MONITOR_LOGLEVEL)).getPriority();
     72 
     73         mMonitoredDevices = new HashMap<String, DeviceData>();
     74 
     75         AndroidDebugBridge.addDeviceChangeListener(new IDeviceChangeListener() {
     76             @Override
     77             public void deviceDisconnected(IDevice device) {
     78                 unmonitorDevice(device.getSerialNumber());
     79                 mMonitoredDevices.remove(device.getSerialNumber());
     80             }
     81 
     82             @Override
     83             public void deviceConnected(IDevice device) {
     84             }
     85 
     86             @Override
     87             public void deviceChanged(IDevice device, int changeMask) {
     88             }
     89         });
     90 
     91         mPrefStore.addPropertyChangeListener(new IPropertyChangeListener() {
     92             @Override
     93             public void propertyChange(PropertyChangeEvent event) {
     94                 if (AUTO_MONITOR_PREFKEY.equals(event.getProperty())
     95                         && event.getNewValue().equals(false)) {
     96                     unmonitorAllDevices();
     97                 } else if (AUTO_MONITOR_LOGLEVEL.equals(event.getProperty())) {
     98                     mMinMessagePriority =
     99                             LogLevel.getByString((String) event.getNewValue()).getPriority();
    100                 }
    101             }
    102         });
    103     }
    104 
    105     private void unmonitorAllDevices() {
    106         for (String device : mMonitoredDevices.keySet()) {
    107             unmonitorDevice(device);
    108         }
    109 
    110         mMonitoredDevices.clear();
    111     }
    112 
    113     private void unmonitorDevice(String deviceSerial) {
    114         DeviceData data = mMonitoredDevices.get(deviceSerial);
    115         if (data == null) {
    116             return;
    117         }
    118 
    119         data.receiver.removeMessageReceivedEventListener(data.bufferChangeListener);
    120     }
    121 
    122     public void monitorDevice(final IDevice device) {
    123         if (!mPrefStore.getBoolean(AUTO_MONITOR_PREFKEY)) {
    124             // do not monitor device if auto monitoring is off
    125             return;
    126         }
    127 
    128         mMonitorEnabled.set(true);
    129 
    130         if (mMonitoredDevices.keySet().contains(device.getSerialNumber())) {
    131             // the device is already monitored
    132             return;
    133         }
    134 
    135         LogCatReceiver r = LogCatReceiverFactory.INSTANCE.newReceiver(device, mPrefStore);
    136         ILogCatBufferChangeListener l = new ILogCatBufferChangeListener() {
    137             @Override
    138             public void bufferChanged(List<LogCatMessage> addedMessages,
    139                     List<LogCatMessage> deletedMessages) {
    140                 checkMessages(addedMessages, device);
    141             }
    142         };
    143         r.addMessageReceivedEventListener(l);
    144 
    145         mMonitoredDevices.put(device.getSerialNumber(), new DeviceData(r, l));
    146     }
    147 
    148     private void checkMessages(List<LogCatMessage> receivedMessages, IDevice device) {
    149         if (!mMonitorEnabled.get()) {
    150             return;
    151         }
    152 
    153         // check the received list of messages to see if any of them are
    154         // significant enough to be seen by the user. If so, activate the logcat view
    155         // to display those messages
    156         for (LogCatMessage m : receivedMessages) {
    157             if (isImportantMessage(m)) {
    158                 focusLogCatView(device, m.getAppName());
    159 
    160                 // now that logcat view is active, no need to check messages until the next
    161                 // time user launches an application.
    162                 mMonitorEnabled.set(false);
    163                 break;
    164             }
    165         }
    166     }
    167 
    168     /**
    169      * Check whether a message is "important". Currently, we assume that a message is important if
    170      * it is of severity level error or higher, and it belongs to an app currently in the workspace.
    171      */
    172     private boolean isImportantMessage(LogCatMessage m) {
    173         if (m.getLogLevel().getPriority() < mMinMessagePriority) {
    174             return false;
    175         }
    176 
    177         String app = m.getAppName();
    178         for (IDebuggerConnector c : mConnectors) {
    179             if (c.isWorkspaceApp(app)) {
    180                 return true;
    181             }
    182         }
    183 
    184         return false;
    185     }
    186 
    187     private void focusLogCatView(final IDevice device, final String appName) {
    188         Display.getDefault().asyncExec(new Runnable() {
    189             @Override
    190             public void run() {
    191                 IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
    192                 if (window == null) {
    193                     return;
    194                 }
    195 
    196                 IWorkbenchPage page = window.getActivePage();
    197                 if (page == null) {
    198                     return;
    199                 }
    200 
    201                 // if the logcat view is not visible, then prompt the user once to set
    202                 // logcat monitoring preferences
    203                 if (!isLogCatViewVisible(page)) {
    204                     boolean showLogCatView = promptUserOnce(page.getWorkbenchWindow().getShell());
    205                     if (!showLogCatView) {
    206                         return;
    207                     }
    208                 }
    209 
    210                 // display view
    211                 final LogCatView v = displayLogCatView(page);
    212                 if (v == null) {
    213                     return;
    214                 }
    215 
    216                 // select correct device
    217                 v.selectionChanged(device);
    218 
    219                 // select appropriate filter
    220                 v.selectTransientAppFilter(appName);
    221             }
    222 
    223             private boolean isLogCatViewVisible(IWorkbenchPage page) {
    224                 IViewPart view = page.findView(LogCatView.ID);
    225                 return view != null && page.isPartVisible(view);
    226             }
    227 
    228             private LogCatView displayLogCatView(IWorkbenchPage page) {
    229                 // if the view is already in the page, just bring it to the front
    230                 // without giving it focus.
    231                 IViewPart view = page.findView(LogCatView.ID);
    232                 if (view != null) {
    233                     page.bringToTop(view);
    234                     if (view instanceof LogCatView) {
    235                         return (LogCatView)view;
    236                     }
    237                 }
    238 
    239                 // if the view is not in the page, then create and show it.
    240                 try {
    241                     return (LogCatView) page.showView(LogCatView.ID);
    242                 } catch (PartInitException e) {
    243                     return null;
    244                 }
    245             }
    246 
    247             private boolean promptUserOnce(Shell shell) {
    248                 // see if this prompt was already displayed
    249                 boolean promptShown = mPrefStore.getBoolean(AUTO_MONITOR_PROMPT_SHOWN);
    250                 if (promptShown) {
    251                     return mPrefStore.getBoolean(AUTO_MONITOR_PREFKEY);
    252                 }
    253 
    254                 LogCatMonitorDialog dlg = new LogCatMonitorDialog(shell);
    255                 int r = dlg.open();
    256 
    257                 // save preference indicating that this dialog has been displayed once
    258                 mPrefStore.setValue(AUTO_MONITOR_PROMPT_SHOWN, true);
    259                 mPrefStore.setValue(AUTO_MONITOR_PREFKEY, dlg.shouldMonitor());
    260                 mPrefStore.setValue(AUTO_MONITOR_LOGLEVEL, dlg.getMinimumPriority());
    261 
    262                 return r == Window.OK && dlg.shouldMonitor();
    263             }
    264 
    265         });
    266     }
    267 
    268     private static class DeviceData {
    269         public final LogCatReceiver receiver;
    270         public final ILogCatBufferChangeListener bufferChangeListener;
    271 
    272         public DeviceData(LogCatReceiver r, ILogCatBufferChangeListener l) {
    273             receiver = r;
    274             bufferChangeListener = l;
    275         }
    276     }
    277 }
    278