FileDocCategorySizeDatePackage
EventLogPanel.javaAPI DocAndroid 1.5 API31382Wed May 06 22:41:08 BST 2009com.android.ddmuilib.log.event

EventLogPanel

public class EventLogPanel extends com.android.ddmuilib.TablePanel implements com.android.ddmuilib.log.event.EventDisplay.ILogColumnListener, com.android.ddmlib.log.LogReceiver.ILogListener
Event log viewer

Fields Summary
private static final String
TAG_FILE_EXT
private static final String
PREFS_EVENT_DISPLAY
private static final String
EVENT_DISPLAY_STORAGE_SEPARATOR
static final String
PREFS_DISPLAY_WIDTH
static final String
PREFS_DISPLAY_HEIGHT
private static final int
DEFAULT_DISPLAY_WIDTH
private static final int
DEFAULT_DISPLAY_HEIGHT
private com.android.ddmuilib.IImageLoader
mImageLoader
private com.android.ddmlib.Device
mCurrentLoggedDevice
private String
mCurrentLogFile
private com.android.ddmlib.log.LogReceiver
mCurrentLogReceiver
private com.android.ddmlib.log.EventLogParser
mCurrentEventLogParser
private Object
mLock
private final ArrayList
mEvents
list of all the events.
private final ArrayList
mNewEvents
list of all the new events, that have yet to be displayed by the ui
private boolean
mPendingDisplay
indicates a pending ui thread display
private final ArrayList
mEventDisplays
list of all the custom event displays
private final NumberFormat
mFormatter
private org.eclipse.swt.widgets.Composite
mParent
private org.eclipse.swt.custom.ScrolledComposite
mBottomParentPanel
private org.eclipse.swt.widgets.Composite
mBottomPanel
private com.android.ddmuilib.actions.ICommonAction
mOptionsAction
private com.android.ddmuilib.actions.ICommonAction
mClearAction
private com.android.ddmuilib.actions.ICommonAction
mSaveAction
private com.android.ddmuilib.actions.ICommonAction
mLoadAction
private com.android.ddmuilib.actions.ICommonAction
mImportAction
private File
mTempFile
file containing the current log raw data.
Constructors Summary
public EventLogPanel(com.android.ddmuilib.IImageLoader imageLoader)


       
        super();
        mImageLoader = imageLoader;
        mFormatter.setGroupingUsed(true);
    
Methods Summary
public voidclearLog()
Clears the log.

This must be called from the UI thread

        try {
            synchronized (mLock) {
                mEvents.clear();
                mNewEvents.clear();
                mPendingDisplay = false;
                for (EventDisplay eventDisplay : mEventDisplays) {
                    eventDisplay.resetUI();
                }
            }
        } catch (SWTException e) {
            Log.e("EventLog", e); //$NON-NLS-1$
        }
    
public voidclientChanged(com.android.ddmlib.Client client, int changeMask)

        // pass
    
public voidclientSelected()

        // pass
    
public voidcolumnResized(int index, org.eclipse.swt.widgets.TableColumn sourceColumn)

        for (EventDisplay eventDisplay : mEventDisplays) {
            eventDisplay.resizeColumn(index, sourceColumn);
        }
    
protected org.eclipse.swt.widgets.ControlcreateControl(org.eclipse.swt.widgets.Composite parent)

        mParent = parent;
        mParent.addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                synchronized (mLock) {
                    if (mCurrentLogReceiver != null) {
                        mCurrentLogReceiver.cancel();
                        mCurrentLogReceiver = null;
                        mCurrentEventLogParser = null;
                        mCurrentLoggedDevice = null;
                        mEventDisplays.clear();
                        mEvents.clear();
                    }
                }
            }
        });

        final IPreferenceStore store = DdmUiPreferences.getStore();

        // init some store stuff
        store.setDefault(PREFS_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH);
        store.setDefault(PREFS_DISPLAY_HEIGHT, DEFAULT_DISPLAY_HEIGHT);
        
        mBottomParentPanel = new ScrolledComposite(parent, SWT.V_SCROLL);
        mBottomParentPanel.setLayoutData(new GridData(GridData.FILL_BOTH));
        mBottomParentPanel.setExpandHorizontal(true);
        mBottomParentPanel.setExpandVertical(true);

        mBottomParentPanel.addControlListener(new ControlAdapter() {
            @Override
            public void controlResized(ControlEvent e) {
                if (mBottomPanel != null) {
                    Rectangle r = mBottomParentPanel.getClientArea();
                    mBottomParentPanel.setMinSize(mBottomPanel.computeSize(r.width,
                        SWT.DEFAULT));
                }
            }
        });

        prepareDisplayUi();

        // load the EventDisplay from storage.
        loadEventDisplays();

        // create the ui
        createDisplayUi();
        
        return mBottomParentPanel;
    
private voidcreateDisplayUi()

        RowLayout rowLayout = new RowLayout();
        rowLayout.wrap = true;
        rowLayout.pack = false;
        rowLayout.justify = true;
        rowLayout.fill = true;
        rowLayout.type = SWT.HORIZONTAL;
        mBottomPanel.setLayout(rowLayout);
        
        IPreferenceStore store = DdmUiPreferences.getStore();
        int displayWidth = store.getInt(PREFS_DISPLAY_WIDTH);
        int displayHeight = store.getInt(PREFS_DISPLAY_HEIGHT);
        
        for (EventDisplay eventDisplay : mEventDisplays) {
            Control c = eventDisplay.createComposite(mBottomPanel, mCurrentEventLogParser, this);
            if (c != null) {
                RowData rd = new RowData();
                rd.height = displayHeight;
                rd.width = displayWidth;
                c.setLayoutData(rd);
            }
            
            Table table = eventDisplay.getTable();
            if (table != null) {
                addTableToFocusListener(table);
            }
        }

        mBottomPanel.layout();
        mBottomParentPanel.setMinSize(mBottomPanel.computeSize(SWT.DEFAULT, SWT.DEFAULT));
        mBottomParentPanel.layout();
    
public voiddeviceSelected()

        startEventLog(getCurrentDevice());
    
private voiddisplayNewEvents()

        // never display more than 1,000 events in this loop. We can't do too much in the UI thread.
        int count = 0;

        // prepare the displays
        for (EventDisplay eventDisplay : mEventDisplays) {
            eventDisplay.startMultiEventDisplay();
        }
        
        // display the new events
        EventContainer event = null;
        boolean need_to_reloop = false;
        do {
            // get the next event to display.
            synchronized (mNewEvents) {
                if (mNewEvents.size() > 0) {
                    if (count > 200) {
                        // there are still events to be displayed, but we don't want to hog the
                        // UI thread for too long, so we stop this runnable, but launch a new
                        // one to keep going.
                        need_to_reloop = true;
                        event = null;
                    } else {
                        event = mNewEvents.remove(0);
                        count++;
                    }
                } else {
                    // we're done.
                    event = null;
                    mPendingDisplay = false;
                }
            }

            if (event != null) {
                // notify the event display
                for (EventDisplay eventDisplay : mEventDisplays) {
                    eventDisplay.newEvent(event, mCurrentEventLogParser);
                }
            }
        } while (event != null);

        // we're done displaying events.
        for (EventDisplay eventDisplay : mEventDisplays) {
            eventDisplay.endMultiEventDisplay();
        }
        
        // if needed, ask the UI thread to re-run this method.
        if (need_to_reloop) {
            scheduleUIEventHandler();
        }
    
private voidhandleNewEvent(com.android.ddmlib.log.EventContainer event)

        // add the event to the generic list
        mEvents.add(event);
        
        // add to the list of events that needs to be displayed, and trigger a
        // new display if needed.
        boolean start_event = false;
        synchronized (mNewEvents) {
            mNewEvents.add(event);
            
            if (mPendingDisplay == false) {
                mPendingDisplay = true;
                start_event = true;
            }
        }
        
        if (start_event == false) {
            // we're done
            return;
        }

        scheduleUIEventHandler();
    
public voidimportBugReport(java.lang.String filePath)

        try {
            BugReportImporter importer = new BugReportImporter(filePath);
            
            String[] tags = importer.getTags();
            String[] log = importer.getLog();
            
            startEventLogFromContent(tags, log);
            
        } catch (FileNotFoundException e) {
            Log.logAndDisplay(LogLevel.ERROR, "Import",
                    "Unable to import bug report: " + e.getMessage());
        }
    
private voidloadEventDisplays()
Loads the {@link EventDisplay}s from the preference store.

        IPreferenceStore store = DdmUiPreferences.getStore();
        String storage = store.getString(PREFS_EVENT_DISPLAY);
        
        if (storage.length() > 0) {
            String[] values = storage.split(Pattern.quote(EVENT_DISPLAY_STORAGE_SEPARATOR));
            
            for (String value : values) {
                EventDisplay eventDisplay = EventDisplay.load(value);
                if (eventDisplay != null) {
                    mEventDisplays.add(eventDisplay);
                }
            }
        }
    
public voidloadLog(java.lang.String filePath)
Loads a binary event log (if has associated .tag file) or otherwise loads a textual event log.

param
filePath Event log path (and base of potential tag file)

        if ((new File(filePath + TAG_FILE_EXT)).exists()) {
            startEventLogFromFiles(filePath);
        } else {
            try {
                EventLogImporter importer = new EventLogImporter(filePath);
                String[] tags = importer.getTags();
                String[] log = importer.getLog();
                startEventLogFromContent(tags, log);
            } catch (FileNotFoundException e) {
                // If this fails, display the error message from startEventLogFromFiles,
                // and pretend we never tried EventLogImporter
                Log.logAndDisplay(Log.LogLevel.ERROR, "EventLog",
                        String.format("Failure to read %1$s", filePath + TAG_FILE_EXT));
            }

        }
    
public voidnewData(byte[] data, int offset, int length)
Processes raw data coming from the log service.

see
LogReceiver.ILogListener#newData(byte[], int, int)

        if (mTempFile != null) {
            try {
                FileOutputStream fos = new FileOutputStream(mTempFile, true /* append */);
                fos.write(data, offset, length);
                fos.close();
            } catch (FileNotFoundException e) {
            } catch (IOException e) {
            }
        }
    
public voidnewEntry(com.android.ddmlib.log.LogReceiver.LogEntry entry)
Processes a new {@link LogEntry} by parsing it with {@link EventLogParser} and displaying it.

param
entry The new log entry
see
LogReceiver.ILogListener#newEntry(LogEntry)

        synchronized (mLock) {
            if (mCurrentEventLogParser != null) {
                EventContainer event = mCurrentEventLogParser.parse(entry);
                if (event != null) {
                    handleNewEvent(event);
                }
            }
        }
    
public voidopenOptionPanel()
Opens the option panel.

This must be called from the UI thread

        try {
            EventDisplayOptions dialog = new EventDisplayOptions(mImageLoader, mParent.getShell());
            if (dialog.open(mCurrentEventLogParser, mEventDisplays, mEvents)) {
                synchronized (mLock) {
                    // get the new EventDisplay list
                    mEventDisplays.clear();
                    mEventDisplays.addAll(dialog.getEventDisplays());
                    
                    // since the list of EventDisplay changed, we store it.
                    saveEventDisplays();
                    
                    rebuildUi();
                }
            }
        } catch (SWTException e) {
            Log.e("EventLog", e); //$NON-NLS-1$
        }
    
protected voidpostCreation()

        // pass
    
private voidprepareDisplayUi()

        mBottomPanel = new Composite(mBottomParentPanel, SWT.NONE);
        mBottomParentPanel.setContent(mBottomPanel);
    
private voidrebuildUi()
Rebuild the display ui.

        synchronized (mLock) {
            // we need to rebuild the ui. First we get rid of it.
            mBottomPanel.dispose();
            mBottomPanel = null;
            
            prepareDisplayUi();
            createDisplayUi();
            
            // and fill it
            
            boolean start_event = false;
            synchronized (mNewEvents) {
                mNewEvents.addAll(0, mEvents);
                
                if (mPendingDisplay == false) {
                    mPendingDisplay = true;
                    start_event = true;
                }
            }
            
            if (start_event) {
                scheduleUIEventHandler();
            }
            
            Rectangle r = mBottomParentPanel.getClientArea();
            mBottomParentPanel.setMinSize(mBottomPanel.computeSize(r.width,
                SWT.DEFAULT));
        }
    
private voidresetUI(boolean inUiThread)

        mEvents.clear();

        // the ui is static we just empty it.
        if (inUiThread) {
            resetUiFromUiThread();
        } else {
            try {
                Display d = mBottomParentPanel.getDisplay();

                // run sync as we need to update right now.
                d.syncExec(new Runnable() {
                    public void run() {
                        if (mBottomParentPanel.isDisposed() == false) {
                            resetUiFromUiThread();
                        }
                    }
                });
            } catch (SWTException e) {
                // display is disposed, we're quitting. Do nothing.
            }
        }
    
private voidresetUiFromUiThread()

        synchronized(mLock) {
            for (EventDisplay eventDisplay : mEventDisplays) {
                eventDisplay.resetUI();
            }
        }
        mOptionsAction.setEnabled(false);
        mClearAction.setEnabled(false);
        mSaveAction.setEnabled(false);
    
private voidrunLocalEventLogService(java.lang.String fileName, com.android.ddmlib.log.LogReceiver logReceiver)
Runs an event log service out of a local file.

param
fileName the full file name of the local file containing the event log.
param
logReceiver the receiver that will handle the log
throws
IOException

        byte[] buffer = new byte[256];
        
        FileInputStream fis = new FileInputStream(fileName);
        
        int count;
        while ((count = fis.read(buffer)) != -1) {
            logReceiver.parseNewData(buffer, 0, count);
        }
    
private voidrunLocalEventLogService(java.lang.String[] log, com.android.ddmlib.log.LogReceiver currentLogReceiver)

        synchronized (mLock) {
            for (String line : log) {
                EventContainer event = mCurrentEventLogParser.parse(line);
                if (event != null) {
                    handleNewEvent(event);
                }
            }
        }
    
private voidsaveEventDisplays()
Saves the {@link EventDisplay}s into the {@link DdmUiPreferences} store.

        IPreferenceStore store = DdmUiPreferences.getStore();
        
        boolean first = true;
        StringBuilder sb = new StringBuilder();
        
        for (EventDisplay eventDisplay : mEventDisplays) {
            String storage = eventDisplay.getStorageString();
            if (storage != null) {
                if (first == false) {
                    sb.append(EVENT_DISPLAY_STORAGE_SEPARATOR);
                } else {
                    first = false;
                }
                
                sb.append(storage);
            }
        }

        store.setValue(PREFS_EVENT_DISPLAY, sb.toString());
    
public voidsaveLog(java.lang.String filePath)
Saves the content of the event log into a file. The log is saved in the same binary format than on the device.

param
filePath
throws
IOException

        if (mCurrentLoggedDevice != null && mCurrentEventLogParser != null) {
            File destFile = new File(filePath);
            destFile.createNewFile();
            FileInputStream fis = new FileInputStream(mTempFile);
            FileOutputStream fos = new FileOutputStream(destFile);
            byte[] buffer = new byte[1024];
            
            int count;
            
            while ((count = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, count);
            }
            
            fos.close();
            fis.close();
            
            // now we save the tag file
            filePath = filePath + TAG_FILE_EXT;
            mCurrentEventLogParser.saveTags(filePath);
        }
    
private voidscheduleUIEventHandler()
Schedules the UI thread to execute a {@link Runnable} calling {@link #displayNewEvents()}.

        try  {
            Display d = mBottomParentPanel.getDisplay();
            d.asyncExec(new Runnable() {
                public void run() {
                    if (mBottomParentPanel.isDisposed() == false) {
                        if (mCurrentEventLogParser != null) {
                            displayNewEvents();
                        }
                    }
                }
            });
        } catch (SWTException e) {
            // if the ui is disposed, do nothing 
        }
    
public voidsetActions(com.android.ddmuilib.actions.ICommonAction optionsAction, com.android.ddmuilib.actions.ICommonAction clearAction, com.android.ddmuilib.actions.ICommonAction saveAction, com.android.ddmuilib.actions.ICommonAction loadAction, com.android.ddmuilib.actions.ICommonAction importAction)
Sets the external actions.

This method sets up the {@link ICommonAction} objects to execute the proper code when triggered by using {@link ICommonAction#setRunnable(Runnable)}.

It will also make sure they are enabled only when possible.

param
optionsAction
param
clearAction
param
saveAction
param
loadAction
param
importAction

        mOptionsAction = optionsAction;
        mOptionsAction.setRunnable(new Runnable() {
            public void run() {
                openOptionPanel();
            }
        });

        mClearAction = clearAction;
        mClearAction.setRunnable(new Runnable() {
            public void run() {
                clearLog();
            }
        });

        mSaveAction = saveAction;
        mSaveAction.setRunnable(new Runnable() {
            public void run() {
                try {
                    FileDialog fileDialog = new FileDialog(mParent.getShell(), SWT.SAVE);

                    fileDialog.setText("Save Event Log");
                    fileDialog.setFileName("event.log");

                    String fileName = fileDialog.open();
                    if (fileName != null) {
                        saveLog(fileName);
                    }
                } catch (IOException e1) {
                }
            }
        });

        mLoadAction = loadAction;
        mLoadAction.setRunnable(new Runnable() {
            public void run() {
                FileDialog fileDialog = new FileDialog(mParent.getShell(), SWT.OPEN);

                fileDialog.setText("Load Event Log");

                String fileName = fileDialog.open();
                if (fileName != null) {
                    loadLog(fileName);
                }
            }
        });

        mImportAction = importAction;
        mImportAction.setRunnable(new Runnable() {
            public void run() {
                FileDialog fileDialog = new FileDialog(mParent.getShell(), SWT.OPEN);

                fileDialog.setText("Import Bug Report");

                String fileName = fileDialog.open();
                if (fileName != null) {
                    importBugReport(fileName);
                }
            }
        });

        mOptionsAction.setEnabled(false);
        mClearAction.setEnabled(false);
        mSaveAction.setEnabled(false);
    
public voidsetFocus()

        mBottomParentPanel.setFocus();
    
private voidstartEventLog(com.android.ddmlib.Device device)
Starts a new logcat and set mCurrentLogCat as the current receiver.

param
device the device to connect logcat to.

        if (device == mCurrentLoggedDevice) {
            return;
        }

        // if we have a logcat already running
        if (mCurrentLogReceiver != null) {
            stopEventLog(false);
        }
        mCurrentLoggedDevice = null;
        mCurrentLogFile = null;

        if (device != null) {
            // create a new output receiver
            mCurrentLogReceiver = new LogReceiver(this);

            // start the logcat in a different thread
            new Thread("EventLog")  { //$NON-NLS-1$
                @Override
                public void run() {
                    while (device.isOnline() == false &&
                            mCurrentLogReceiver != null &&
                            mCurrentLogReceiver.isCancelled() == false) {
                        try {
                            sleep(2000);
                        } catch (InterruptedException e) {
                            return;
                        }
                    }

                    if (mCurrentLogReceiver == null || mCurrentLogReceiver.isCancelled()) {
                        // logcat was stopped/cancelled before the device became ready.
                        return;
                    }

                    try {
                        mCurrentLoggedDevice = device;
                        synchronized (mLock) {
                            mCurrentEventLogParser = new EventLogParser();
                            mCurrentEventLogParser.init(device);
                        }
                        
                        // update the event display with the new parser.
                        updateEventDisplays();
                        
                        // prepare the temp file that will contain the raw data
                        mTempFile = File.createTempFile("android-event-", ".log");

                        device.runEventLogService(mCurrentLogReceiver);
                    } catch (Exception e) {
                        Log.e("EventLog", e);
                    } finally {
                    }
                }
            }.start();
        }
    
private voidstartEventLogFromContent(java.lang.String[] tags, java.lang.String[] log)

        // if we have a logcat already running
        if (mCurrentLogReceiver != null) {
            stopEventLog(false);
        }
        mCurrentLoggedDevice = null;
        mCurrentLogFile = null;

        // create a new output receiver
        mCurrentLogReceiver = new LogReceiver(this);
        
        mSaveAction.setEnabled(false);

        // start the logcat in a different thread
        new Thread("EventLog")  { //$NON-NLS-1$
            @Override
            public void run() {
                try {
                    synchronized (mLock) {
                        mCurrentEventLogParser = new EventLogParser();
                        if (mCurrentEventLogParser.init(tags) == false) {
                            mCurrentEventLogParser = null;
                            return;
                        }
                    }
                    
                    // update the event display with the new parser.
                    updateEventDisplays();
                    
                    runLocalEventLogService(log, mCurrentLogReceiver);
                } catch (Exception e) {
                    Log.e("EventLog", e);
                } finally {
                }
            }
        }.start();
    
private voidstartEventLogFromFiles(java.lang.String fileName)

        // if we have a logcat already running
        if (mCurrentLogReceiver != null) {
            stopEventLog(false);
        }
        mCurrentLoggedDevice = null;
        mCurrentLogFile = null;

        // create a new output receiver
        mCurrentLogReceiver = new LogReceiver(this);
        
        mSaveAction.setEnabled(false);

        // start the logcat in a different thread
        new Thread("EventLog")  { //$NON-NLS-1$
            @Override
            public void run() {
                try {
                    mCurrentLogFile = fileName;
                    synchronized (mLock) {
                        mCurrentEventLogParser = new EventLogParser();
                        if (mCurrentEventLogParser.init(fileName + TAG_FILE_EXT) == false) {
                            mCurrentEventLogParser = null;
                            Log.logAndDisplay(LogLevel.ERROR, "EventLog",
                                    String.format("Failure to read %1$s", fileName + TAG_FILE_EXT));
                            return;
                        }
                    }
                    
                    // update the event display with the new parser.
                    updateEventDisplays();
                    
                    runLocalEventLogService(fileName, mCurrentLogReceiver);
                } catch (Exception e) {
                    Log.e("EventLog", e);
                } finally {
                }
            }
        }.start();
    
public voidstopEventLog(boolean inUiThread)

        if (mCurrentLogReceiver != null) {
            mCurrentLogReceiver.cancel();

            // when the thread finishes, no one will reference that object
            // and it'll be destroyed
            synchronized (mLock) {
                mCurrentLogReceiver = null;
                mCurrentEventLogParser = null;

                mCurrentLoggedDevice = null;
                mEvents.clear();
                mNewEvents.clear();
                mPendingDisplay = false;
            }

            resetUI(inUiThread);
        }
        
        if (mTempFile != null) {
            mTempFile.delete();
            mTempFile = null;
        }
    
private voidupdateEventDisplays()
Updates the {@link EventDisplay} with the new {@link EventLogParser}.

This will run asynchronously in the UI thread.

        try {
            Display d = mBottomParentPanel.getDisplay();

            d.asyncExec(new Runnable() {
                public void run() {
                    if (mBottomParentPanel.isDisposed() == false) {
                        for (EventDisplay eventDisplay : mEventDisplays) {
                            eventDisplay.setNewLogParser(mCurrentEventLogParser);
                        }
                        
                        mOptionsAction.setEnabled(true);
                        mClearAction.setEnabled(true);
                        if (mCurrentLogFile == null) {
                            mSaveAction.setEnabled(true);
                        } else {
                            mSaveAction.setEnabled(false);
                        }
                    }
                }
            });
        } catch (SWTException e) {
            // display is disposed: do nothing.
        }