blob: b5fd36e72351f548fe86fd2a0898580d208aa2a1 [file] [log] [blame]
/*
* 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);
}
}