com.android.ddmlib.log.LogReceiver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ddmlib Show documentation
Show all versions of ddmlib Show documentation
Library providing APIs to talk to Android devices
/*
* Copyright (C) 2008 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.log;
import com.android.ddmlib.utils.ArrayHelper;
import java.security.InvalidParameterException;
/**
* Receiver able to provide low level parsing for device-side log services.
*/
public final class LogReceiver {
private static final int ENTRY_HEADER_SIZE = 20; // 2*2 + 4*4; see LogEntry.
/**
* Represents a log entry and its raw data.
*/
public static final class LogEntry {
/*
* See //device/include/utils/logger.h
*/
/** 16bit unsigned: length of the payload. */
public int len; /* This is normally followed by a 16 bit padding */
/** pid of the process that generated this {@link LogEntry} */
public int pid;
/** tid of the process that generated this {@link LogEntry} */
public int tid;
/** Seconds since epoch. */
public int sec;
/** nanoseconds. */
public int nsec;
/** The entry's raw data. */
public byte[] data;
}
/**
* Classes which implement this interface provide a method that deals
* with {@link LogEntry} objects coming from log service through a {@link LogReceiver}.
* This interface provides two methods.
*
* - {@link #newEntry(com.android.ddmlib.log.LogReceiver.LogEntry)} provides a
* first level of parsing, extracting {@link LogEntry} objects out of the log service output.
* - {@link #newData(byte[], int, int)} provides a way to receive the raw information
* coming directly from the log service.
*
*/
public interface ILogListener {
/**
* Sent when a new {@link LogEntry} has been parsed by the {@link LogReceiver}.
* @param entry the new log entry.
*/
public void newEntry(LogEntry entry);
/**
* Sent when new raw data is coming from the log service.
* @param data the raw data buffer.
* @param offset the offset into the buffer signaling the beginning of the new data.
* @param length the length of the new data.
*/
public void newData(byte[] data, int offset, int length);
}
/** Current {@link LogEntry} being read, before sending it to the listener. */
private LogEntry mCurrentEntry;
/** Temp buffer to store partial entry headers. */
private byte[] mEntryHeaderBuffer = new byte[ENTRY_HEADER_SIZE];
/** Offset in the partial header buffer */
private int mEntryHeaderOffset = 0;
/** Offset in the partial entry data */
private int mEntryDataOffset = 0;
/** Listener waiting for receive fully read {@link LogEntry} objects */
private ILogListener mListener;
private boolean mIsCancelled = false;
/**
* Creates a {@link LogReceiver} with an {@link ILogListener}.
*
* The {@link ILogListener} will receive new log entries as they are parsed, in the form
* of {@link LogEntry} objects.
* @param listener the listener to receive new log entries.
*/
public LogReceiver(ILogListener listener) {
mListener = listener;
}
/**
* Parses new data coming from the log service.
* @param data the data buffer
* @param offset the offset into the buffer signaling the beginning of the new data.
* @param length the length of the new data.
*/
public void parseNewData(byte[] data, int offset, int length) {
// notify the listener of new raw data
if (mListener != null) {
mListener.newData(data, offset, length);
}
// loop while there is still data to be read and the receiver has not be cancelled.
while (length > 0 && !mIsCancelled) {
// first check if we have no current entry.
if (mCurrentEntry == null) {
if (mEntryHeaderOffset + length < ENTRY_HEADER_SIZE) {
// if we don't have enough data to finish the header, save
// the data we have and return
System.arraycopy(data, offset, mEntryHeaderBuffer, mEntryHeaderOffset, length);
mEntryHeaderOffset += length;
return;
} else {
// we have enough to fill the header, let's do it.
// did we store some part at the beginning of the header?
if (mEntryHeaderOffset != 0) {
// copy the rest of the entry header into the header buffer
int size = ENTRY_HEADER_SIZE - mEntryHeaderOffset;
System.arraycopy(data, offset, mEntryHeaderBuffer, mEntryHeaderOffset,
size);
// create the entry from the header buffer
mCurrentEntry = createEntry(mEntryHeaderBuffer, 0);
// since we used the whole entry header buffer, we reset the offset
mEntryHeaderOffset = 0;
// adjust current offset and remaining length to the beginning
// of the entry data
offset += size;
length -= size;
} else {
// create the entry directly from the data array
mCurrentEntry = createEntry(data, offset);
// adjust current offset and remaining length to the beginning
// of the entry data
offset += ENTRY_HEADER_SIZE;
length -= ENTRY_HEADER_SIZE;
}
}
}
// at this point, we have an entry, and offset/length have been updated to skip
// the entry header.
// if we have enough data for this entry or more, we'll need to end this entry
if (length >= mCurrentEntry.len - mEntryDataOffset) {
// compute and save the size of the data that we have to read for this entry,
// based on how much we may already have read.
int dataSize = mCurrentEntry.len - mEntryDataOffset;
// we only read what we need, and put it in the entry buffer.
System.arraycopy(data, offset, mCurrentEntry.data, mEntryDataOffset, dataSize);
// notify the listener of a new entry
if (mListener != null) {
mListener.newEntry(mCurrentEntry);
}
// reset some flags: we have read 0 data of the current entry.
// and we have no current entry being read.
mEntryDataOffset = 0;
mCurrentEntry = null;
// and update the data buffer info to the end of the current entry / start
// of the next one.
offset += dataSize;
length -= dataSize;
} else {
// we don't have enough data to fill this entry, so we store what we have
// in the entry itself.
System.arraycopy(data, offset, mCurrentEntry.data, mEntryDataOffset, length);
// save the amount read for the data.
mEntryDataOffset += length;
return;
}
}
}
/**
* Returns whether this receiver is canceling the remote service.
*/
public boolean isCancelled() {
return mIsCancelled;
}
/**
* Cancels the current remote service.
*/
public void cancel() {
mIsCancelled = true;
}
/**
* Creates a {@link LogEntry} from the array of bytes. This expects the data buffer size
* to be at least offset + {@link #ENTRY_HEADER_SIZE}
.
* @param data the data buffer the entry is read from.
* @param offset the offset of the first byte from the buffer representing the entry.
* @return a new {@link LogEntry} or null
if some error happened.
*/
private LogEntry createEntry(byte[] data, int offset) {
if (data.length < offset + ENTRY_HEADER_SIZE) {
throw new InvalidParameterException(
"Buffer not big enough to hold full LoggerEntry header");
}
// create the new entry and fill it.
LogEntry entry = new LogEntry();
entry.len = ArrayHelper.swapU16bitFromArray(data, offset);
// we've read only 16 bits, but since there's also a 16 bit padding,
// we can skip right over both.
offset += 4;
entry.pid = ArrayHelper.swap32bitFromArray(data, offset);
offset += 4;
entry.tid = ArrayHelper.swap32bitFromArray(data, offset);
offset += 4;
entry.sec = ArrayHelper.swap32bitFromArray(data, offset);
offset += 4;
entry.nsec = ArrayHelper.swap32bitFromArray(data, offset);
offset += 4;
// allocate the data
entry.data = new byte[entry.len];
return entry;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy