blob: 4cf92c48a35c0ea532b04f8d65ee4d9fdf1877f9 [file] [log] [blame]
package com.android.networkstack.tethering.companionproxy.io;
import android.system.ErrnoException;
import android.system.StructPollfd;
import android.util.Log;
import java.io.IOException;
/**
* Base class for all AsyncFile implementations.
*
* @hide
*/
abstract class BaseAsyncFileImpl implements AsyncFile {
private final Listener mListener;
private final String mLogTag;
private volatile boolean mWantsClose;
private volatile boolean mWantsReadEvents;
private volatile boolean mWantsWriteEvents;
private boolean mHasNotifiedClosed;
private volatile boolean mEndOfFile;
BaseAsyncFileImpl(Listener listener, String logTag) {
mListener = listener;
mLogTag = logTag;
}
@Override
public final void close() {
mWantsClose = true;
wakeup();
}
final boolean wantsClose() {
return mWantsClose;
}
final void doClose() {
mWantsClose = true;
closeInternal();
if (!mHasNotifiedClosed) {
mHasNotifiedClosed = true;
try {
mListener.onClosed(this);
} catch (Throwable e) {
Log.w(mLogTag, logStr("Unexpected error while notifying close event"), e);
}
}
}
@Override
public final void enableReadEvents(boolean enable) {
if (enable != mWantsReadEvents) {
mWantsReadEvents = enable;
wakeup();
}
}
@Override
public final void enableWriteEvents(boolean enable) {
if (enable != mWantsWriteEvents) {
mWantsWriteEvents = enable;
wakeup();
}
}
protected final boolean wantsReadEvents() {
return mWantsReadEvents;
}
protected final boolean wantsWriteEvents() {
return mWantsWriteEvents;
}
protected final void callEventListener(boolean hasReadEvent, boolean hasWriteEvent) {
// Handle writes before reads, because read events may have side effects which
// affect the state of the sockets being polled, including deleting them entirely.
// Write events have no side effects, so they should be handled first.
if (hasWriteEvent && mWantsWriteEvents && !mWantsClose) {
try {
mListener.onWriteReady(this);
} catch (Throwable e) {
Log.w(mLogTag, logStr("Unexpected error notifying write event, closing file"), e);
close();
}
}
if (hasReadEvent && mWantsReadEvents && !mWantsClose) {
try {
mListener.onReadReady(this);
} catch (Throwable e) {
Log.w(mLogTag, logStr("Unexpected error notifying read event, closing file"), e);
close();
}
}
}
@Override
public final boolean reachedEndOfFile() {
return mEndOfFile;
}
@Override
public final int read(byte[] buffer, int pos, int len) throws IOException {
assertInThread();
int result = readInternal(buffer, pos, len);
if (result < 0) {
mEndOfFile = true;
}
return result;
}
@Override
public final int write(byte[] buffer, int pos, int len) throws IOException {
assertInThread();
return writeInternal(buffer, pos, len);
}
protected abstract void closeInternal();
protected abstract void applyEventRequests();
protected abstract StructPollfd getStructPollfd();
protected abstract void notifyEvents();
protected abstract int readInternal(byte[] buffer, int pos, int len) throws IOException;
protected abstract int writeInternal(byte[] buffer, int pos, int len) throws IOException;
protected abstract void wakeup();
protected abstract void assertInThread();
protected final String logStr(String message) {
return "[EventManager] " + message;
}
protected final void toString(StringBuilder sb) {
assertInThread();
sb.append(",eof=");
sb.append(mEndOfFile);
sb.append(",reqClose=");
sb.append(mWantsClose);
sb.append(",notifiedClosed=");
sb.append(mHasNotifiedClosed);
sb.append(",reqRead=");
sb.append(mWantsReadEvents);
sb.append(",reqWrite=");
sb.append(mWantsWriteEvents);
}
}