| /* |
| * Copyright (C) 2013 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.ddmlib.logcat; |
| |
| import com.android.annotations.NonNull; |
| import com.android.annotations.concurrency.GuardedBy; |
| import com.android.ddmlib.AdbCommandRejectedException; |
| import com.android.ddmlib.IDevice; |
| import com.android.ddmlib.IShellOutputReceiver; |
| import com.android.ddmlib.Log.LogLevel; |
| import com.android.ddmlib.MultiLineReceiver; |
| import com.android.ddmlib.ShellCommandUnresponsiveException; |
| import com.android.ddmlib.TimeoutException; |
| |
| import java.io.IOException; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| public class LogCatReceiverTask implements Runnable { |
| private static final String LOGCAT_COMMAND = "logcat -v long"; //$NON-NLS-1$ |
| private static final int DEVICE_POLL_INTERVAL_MSEC = 1000; |
| |
| private static final LogCatMessage sDeviceDisconnectedMsg = |
| errorMessage("Device disconnected: 1"); |
| private static final LogCatMessage sConnectionTimeoutMsg = |
| errorMessage("LogCat Connection timed out"); |
| private static final LogCatMessage sConnectionErrorMsg = |
| errorMessage("LogCat Connection error"); |
| |
| private final IDevice mDevice; |
| private final LogCatOutputReceiver mReceiver; |
| private final LogCatMessageParser mParser; |
| private final AtomicBoolean mCancelled; |
| |
| @GuardedBy("this") |
| private final Set<LogCatListener> mListeners = new HashSet<LogCatListener>(); |
| |
| public LogCatReceiverTask(@NonNull IDevice device) { |
| mDevice = device; |
| |
| mReceiver = new LogCatOutputReceiver(); |
| mParser = new LogCatMessageParser(); |
| mCancelled = new AtomicBoolean(); |
| } |
| |
| @Override |
| public void run() { |
| // wait while device comes online |
| while (!mDevice.isOnline()) { |
| try { |
| Thread.sleep(DEVICE_POLL_INTERVAL_MSEC); |
| } catch (InterruptedException e) { |
| return; |
| } |
| } |
| |
| try { |
| mDevice.executeShellCommand(LOGCAT_COMMAND, mReceiver, 0); |
| } catch (TimeoutException e) { |
| notifyListeners(Collections.singletonList(sConnectionTimeoutMsg)); |
| } catch (AdbCommandRejectedException ignored) { |
| // will not be thrown as long as the shell supports logcat |
| } catch (ShellCommandUnresponsiveException ignored) { |
| // this will not be thrown since the last argument is 0 |
| } catch (IOException e) { |
| notifyListeners(Collections.singletonList(sConnectionErrorMsg)); |
| } |
| |
| notifyListeners(Collections.singletonList(sDeviceDisconnectedMsg)); |
| } |
| |
| public void stop() { |
| mCancelled.set(true); |
| } |
| |
| private class LogCatOutputReceiver extends MultiLineReceiver { |
| public LogCatOutputReceiver() { |
| setTrimLine(false); |
| } |
| |
| /** Implements {@link IShellOutputReceiver#isCancelled() }. */ |
| @Override |
| public boolean isCancelled() { |
| return mCancelled.get(); |
| } |
| |
| @Override |
| public void processNewLines(String[] lines) { |
| if (!mCancelled.get()) { |
| processLogLines(lines); |
| } |
| } |
| |
| private void processLogLines(String[] lines) { |
| List<LogCatMessage> newMessages = mParser.processLogLines(lines, mDevice); |
| if (!newMessages.isEmpty()) { |
| notifyListeners(newMessages); |
| } |
| } |
| } |
| |
| public synchronized void addLogCatListener(LogCatListener l) { |
| mListeners.add(l); |
| } |
| |
| public synchronized void removeLogCatListener(LogCatListener l) { |
| mListeners.remove(l); |
| } |
| |
| private synchronized void notifyListeners(List<LogCatMessage> messages) { |
| for (LogCatListener l: mListeners) { |
| l.log(messages); |
| } |
| } |
| |
| private static LogCatMessage errorMessage(String msg) { |
| return new LogCatMessage(LogLevel.ERROR, "", "", "", "", "", msg); |
| } |
| } |