Merge "[KA02.5] Use binder thread and executor to invoke callback"

This commit is contained in:
Junyu Lai
2019-03-15 07:35:27 +00:00
committed by Gerrit Code Review
11 changed files with 285 additions and 227 deletions

View File

@@ -38,7 +38,6 @@ import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.INetworkActivityListener;
import android.os.INetworkManagementService;
@@ -75,6 +74,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
/**
* Class that answers queries about the state of network connectivity. It also
@@ -1813,23 +1815,26 @@ public class ConnectivityManager {
public static final int MIN_INTERVAL = 10;
private final Network mNetwork;
private final PacketKeepaliveCallback mCallback;
private final Looper mLooper;
private final Messenger mMessenger;
private final ISocketKeepaliveCallback mCallback;
private final ExecutorService mExecutor;
private volatile Integer mSlot;
void stopLooper() {
mLooper.quit();
}
@UnsupportedAppUsage
public void stop() {
try {
mService.stopKeepalive(mNetwork, mSlot);
} catch (RemoteException e) {
Log.e(TAG, "Error stopping packet keepalive: ", e);
stopLooper();
mExecutor.execute(() -> {
try {
if (mSlot != null) {
mService.stopKeepalive(mNetwork, mSlot);
}
} catch (RemoteException e) {
Log.e(TAG, "Error stopping packet keepalive: ", e);
throw e.rethrowFromSystemServer();
}
});
} catch (RejectedExecutionException e) {
// The internal executor has already stopped due to previous event.
}
}
@@ -1837,40 +1842,43 @@ public class ConnectivityManager {
Preconditions.checkNotNull(network, "network cannot be null");
Preconditions.checkNotNull(callback, "callback cannot be null");
mNetwork = network;
mCallback = callback;
HandlerThread thread = new HandlerThread(TAG);
thread.start();
mLooper = thread.getLooper();
mMessenger = new Messenger(new Handler(mLooper) {
mExecutor = Executors.newSingleThreadExecutor();
mCallback = new ISocketKeepaliveCallback.Stub() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case NetworkAgent.EVENT_SOCKET_KEEPALIVE:
int error = message.arg2;
try {
if (error == SUCCESS) {
if (mSlot == null) {
mSlot = message.arg1;
mCallback.onStarted();
} else {
mSlot = null;
stopLooper();
mCallback.onStopped();
}
} else {
stopLooper();
mCallback.onError(error);
}
} catch (Exception e) {
Log.e(TAG, "Exception in keepalive callback(" + error + ")", e);
}
break;
default:
Log.e(TAG, "Unhandled message " + Integer.toHexString(message.what));
break;
}
public void onStarted(int slot) {
Binder.withCleanCallingIdentity(() ->
mExecutor.execute(() -> {
mSlot = slot;
callback.onStarted();
}));
}
});
@Override
public void onStopped() {
Binder.withCleanCallingIdentity(() ->
mExecutor.execute(() -> {
mSlot = null;
callback.onStopped();
}));
mExecutor.shutdown();
}
@Override
public void onError(int error) {
Binder.withCleanCallingIdentity(() ->
mExecutor.execute(() -> {
mSlot = null;
callback.onError(error);
}));
mExecutor.shutdown();
}
@Override
public void onDataReceived() {
// PacketKeepalive is only used for Nat-T keepalive and as such does not invoke
// this callback when data is received.
}
};
}
}
@@ -1887,12 +1895,11 @@ public class ConnectivityManager {
InetAddress srcAddr, int srcPort, InetAddress dstAddr) {
final PacketKeepalive k = new PacketKeepalive(network, callback);
try {
mService.startNattKeepalive(network, intervalSeconds, k.mMessenger, new Binder(),
mService.startNattKeepalive(network, intervalSeconds, k.mCallback,
srcAddr.getHostAddress(), srcPort, dstAddr.getHostAddress());
} catch (RemoteException e) {
Log.e(TAG, "Error starting packet keepalive: ", e);
k.stopLooper();
return null;
throw e.rethrowFromSystemServer();
}
return k;
}

View File

@@ -27,6 +27,7 @@ import android.net.NetworkMisc;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.ISocketKeepaliveCallback;
import android.net.ProxyInfo;
import android.os.Bundle;
import android.os.IBinder;
@@ -194,15 +195,15 @@ interface IConnectivityManager
void factoryReset();
void startNattKeepalive(in Network network, int intervalSeconds, in Messenger messenger,
in IBinder binder, String srcAddr, int srcPort, String dstAddr);
void startNattKeepalive(in Network network, int intervalSeconds,
in ISocketKeepaliveCallback cb, String srcAddr, int srcPort, String dstAddr);
void startNattKeepaliveWithFd(in Network network, in FileDescriptor fd, int resourceId,
int intervalSeconds, in Messenger messenger, in IBinder binder, String srcAddr,
int intervalSeconds, in ISocketKeepaliveCallback cb, String srcAddr,
String dstAddr);
void startTcpKeepalive(in Network network, in FileDescriptor fd, int intervalSeconds,
in Messenger messenger, in IBinder binder);
in ISocketKeepaliveCallback cb);
void stopKeepalive(in Network network, int slot);

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2019, 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 android.net;
/**
* Callback to provide status changes of keepalive offload.
*
* @hide
*/
oneway interface ISocketKeepaliveCallback
{
/** The keepalive was successfully started. */
void onStarted(int slot);
/** The keepalive was successfully stopped. */
void onStopped();
/** The keepalive was stopped because of an error. */
void onError(int error);
/** The keepalive on a TCP socket was stopped because the socket received data. */
void onDataReceived();
}

View File

@@ -17,7 +17,6 @@
package android.net;
import android.annotation.NonNull;
import android.os.Binder;
import android.os.RemoteException;
import android.util.Log;
@@ -52,24 +51,30 @@ public final class NattSocketKeepalive extends SocketKeepalive {
@Override
void startImpl(int intervalSec) {
try {
mService.startNattKeepaliveWithFd(mNetwork, mFd, mResourceId, intervalSec, mMessenger,
new Binder(), mSource.getHostAddress(), mDestination.getHostAddress());
} catch (RemoteException e) {
Log.e(TAG, "Error starting packet keepalive: ", e);
stopLooper();
}
mExecutor.execute(() -> {
try {
mService.startNattKeepaliveWithFd(mNetwork, mFd, mResourceId, intervalSec,
mCallback,
mSource.getHostAddress(), mDestination.getHostAddress());
} catch (RemoteException e) {
Log.e(TAG, "Error starting socket keepalive: ", e);
throw e.rethrowFromSystemServer();
}
});
}
@Override
void stopImpl() {
try {
if (mSlot != null) {
mService.stopKeepalive(mNetwork, mSlot);
mExecutor.execute(() -> {
try {
if (mSlot != null) {
mService.stopKeepalive(mNetwork, mSlot);
}
} catch (RemoteException e) {
Log.e(TAG, "Error stopping socket keepalive: ", e);
throw e.rethrowFromSystemServer();
}
} catch (RemoteException e) {
Log.e(TAG, "Error stopping packet keepalive: ", e);
stopLooper();
}
});
}
}

View File

@@ -20,13 +20,8 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.Process;
import android.util.Log;
import android.os.Binder;
import android.os.RemoteException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -152,10 +147,9 @@ public abstract class SocketKeepalive implements AutoCloseable {
@NonNull final IConnectivityManager mService;
@NonNull final Network mNetwork;
@NonNull private final Executor mExecutor;
@NonNull private final SocketKeepalive.Callback mCallback;
@NonNull private final Looper mLooper;
@NonNull final Messenger mMessenger;
@NonNull final Executor mExecutor;
@NonNull final ISocketKeepaliveCallback mCallback;
// TODO: remove slot since mCallback could be used to identify which keepalive to stop.
@Nullable Integer mSlot;
SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
@@ -163,53 +157,53 @@ public abstract class SocketKeepalive implements AutoCloseable {
mService = service;
mNetwork = network;
mExecutor = executor;
mCallback = callback;
// TODO: 1. Use other thread modeling instead of create one thread for every instance to
// reduce the memory cost.
// 2. support restart.
// 3. Fix race condition which caused by rapidly start and stop.
HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND
+ Process.THREAD_PRIORITY_LESS_FAVORABLE);
thread.start();
mLooper = thread.getLooper();
mMessenger = new Messenger(new Handler(mLooper) {
mCallback = new ISocketKeepaliveCallback.Stub() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case NetworkAgent.EVENT_SOCKET_KEEPALIVE:
final int status = message.arg2;
try {
if (status == SUCCESS) {
if (mSlot == null) {
mSlot = message.arg1;
mExecutor.execute(() -> mCallback.onStarted());
} else {
mSlot = null;
stopLooper();
mExecutor.execute(() -> mCallback.onStopped());
}
} else if (status == DATA_RECEIVED) {
stopLooper();
mExecutor.execute(() -> mCallback.onDataReceived());
} else {
stopLooper();
mExecutor.execute(() -> mCallback.onError(status));
}
} catch (Exception e) {
Log.e(TAG, "Exception in keepalive callback(" + status + ")", e);
}
break;
default:
Log.e(TAG, "Unhandled message " + Integer.toHexString(message.what));
break;
}
public void onStarted(int slot) {
Binder.withCleanCallingIdentity(() ->
mExecutor.execute(() -> {
mSlot = slot;
callback.onStarted();
}));
}
});
@Override
public void onStopped() {
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> {
mSlot = null;
callback.onStopped();
}));
}
@Override
public void onError(int error) {
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> {
mSlot = null;
callback.onError(error);
}));
}
@Override
public void onDataReceived() {
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> {
mSlot = null;
callback.onDataReceived();
}));
}
};
}
/**
* Request that keepalive be started with the given {@code intervalSec}. See
* {@link SocketKeepalive}.
* {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an exception
* when invoking start or stop of the {@link SocketKeepalive}, a {@link RemoteException} will be
* thrown into the {@code executor}. This is typically not important to catch because the remote
* party is the system, so if it is not in shape to communicate through binder the system is
* probably going down anyway. If the caller cares regardless, it can use a custom
* {@link Executor} to catch the {@link RemoteException}.
*
* @param intervalSec The target interval in seconds between keepalive packet transmissions.
* The interval should be between 10 seconds and 3600 seconds, otherwise
@@ -222,12 +216,6 @@ public abstract class SocketKeepalive implements AutoCloseable {
abstract void startImpl(int intervalSec);
/** @hide */
protected void stopLooper() {
// TODO: remove this after changing thread modeling.
mLooper.quit();
}
/**
* Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
* before using the object. See {@link SocketKeepalive}.
@@ -245,7 +233,6 @@ public abstract class SocketKeepalive implements AutoCloseable {
@Override
public final void close() {
stop();
stopLooper();
}
/**
@@ -259,7 +246,8 @@ public abstract class SocketKeepalive implements AutoCloseable {
public void onStopped() {}
/** An error occurred. */
public void onError(@ErrorCode int error) {}
/** The keepalive on a TCP socket was stopped because the socket received data. */
/** The keepalive on a TCP socket was stopped because the socket received data. This is
* never called for UDP sockets. */
public void onDataReceived() {}
}
}

View File

@@ -17,7 +17,6 @@
package android.net;
import android.annotation.NonNull;
import android.os.Binder;
import android.os.RemoteException;
import android.util.Log;
@@ -56,24 +55,28 @@ final class TcpSocketKeepalive extends SocketKeepalive {
*/
@Override
void startImpl(int intervalSec) {
try {
final FileDescriptor fd = mSocket.getFileDescriptor$();
mService.startTcpKeepalive(mNetwork, fd, intervalSec, mMessenger, new Binder());
} catch (RemoteException e) {
Log.e(TAG, "Error starting packet keepalive: ", e);
stopLooper();
}
mExecutor.execute(() -> {
try {
final FileDescriptor fd = mSocket.getFileDescriptor$();
mService.startTcpKeepalive(mNetwork, fd, intervalSec, mCallback);
} catch (RemoteException e) {
Log.e(TAG, "Error starting packet keepalive: ", e);
throw e.rethrowFromSystemServer();
}
});
}
@Override
void stopImpl() {
try {
if (mSlot != null) {
mService.stopKeepalive(mNetwork, mSlot);
mExecutor.execute(() -> {
try {
if (mSlot != null) {
mService.stopKeepalive(mNetwork, mSlot);
}
} catch (RemoteException e) {
Log.e(TAG, "Error stopping packet keepalive: ", e);
throw e.rethrowFromSystemServer();
}
} catch (RemoteException e) {
Log.e(TAG, "Error stopping packet keepalive: ", e);
stopLooper();
}
});
}
}