Merge "Several changes to VDM Demo apps" into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
a12aab843d
@@ -18,6 +18,7 @@
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext;
|
||||
@@ -48,7 +49,6 @@ import java.net.Inet6Address;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -68,6 +68,9 @@ public class ConnectionManager {
|
||||
@ApplicationContext private final Context mContext;
|
||||
private final ConnectivityManager mConnectivityManager;
|
||||
private final Handler mBackgroundHandler;
|
||||
private final Object mSessionLock = new Object();
|
||||
|
||||
@GuardedBy("mSessionLock")
|
||||
private DiscoverySession mDiscoverySession;
|
||||
|
||||
/** Simple data structure to allow clients to query the current status. */
|
||||
@@ -76,6 +79,7 @@ public class ConnectionManager {
|
||||
public boolean connected = false;
|
||||
}
|
||||
|
||||
@GuardedBy("mSessionLock")
|
||||
private final ConnectionStatus mConnectionStatus = new ConnectionStatus();
|
||||
|
||||
/** Simple callback to notify connection and disconnection events. */
|
||||
@@ -93,8 +97,8 @@ public class ConnectionManager {
|
||||
default void onError(String message) {}
|
||||
}
|
||||
|
||||
private final List<ConnectionCallback> mConnectionCallbacks =
|
||||
Collections.synchronizedList(new ArrayList<>());
|
||||
@GuardedBy("mConnectionCallbacks")
|
||||
private final List<ConnectionCallback> mConnectionCallbacks = new ArrayList<>();
|
||||
|
||||
private final RemoteIo.StreamClosedCallback mStreamClosedCallback = this::disconnect;
|
||||
|
||||
@@ -115,22 +119,28 @@ public class ConnectionManager {
|
||||
|
||||
/** Registers a listener for connection events. */
|
||||
public void addConnectionCallback(ConnectionCallback callback) {
|
||||
mConnectionCallbacks.add(callback);
|
||||
synchronized (mConnectionCallbacks) {
|
||||
mConnectionCallbacks.add(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/** Registers a listener for connection events. */
|
||||
public void removeConnectionCallback(ConnectionCallback callback) {
|
||||
mConnectionCallbacks.remove(callback);
|
||||
synchronized (mConnectionCallbacks) {
|
||||
mConnectionCallbacks.remove(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the current connection status. */
|
||||
public ConnectionStatus getConnectionStatus() {
|
||||
return mConnectionStatus;
|
||||
synchronized (mSessionLock) {
|
||||
return mConnectionStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/** Publish a local service so remote devices can discover this device. */
|
||||
public void startHostSession() {
|
||||
if (mConnectionStatus.connected) {
|
||||
if (isConnected()) {
|
||||
return;
|
||||
}
|
||||
var unused = createSession().thenAccept(wifiAwareSession -> wifiAwareSession.publish(
|
||||
@@ -141,7 +151,7 @@ public class ConnectionManager {
|
||||
|
||||
/** Looks for published services from remote devices and subscribes to them. */
|
||||
public void startClientSession() {
|
||||
if (mConnectionStatus.connected) {
|
||||
if (isConnected()) {
|
||||
return;
|
||||
}
|
||||
var unused = createSession().thenAccept(wifiAwareSession -> wifiAwareSession.subscribe(
|
||||
@@ -150,6 +160,12 @@ public class ConnectionManager {
|
||||
mBackgroundHandler));
|
||||
}
|
||||
|
||||
private boolean isConnected() {
|
||||
synchronized (mSessionLock) {
|
||||
return mConnectionStatus.connected;
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<WifiAwareSession> createSession() {
|
||||
CompletableFuture<WifiAwareSession> wifiAwareSessionFuture = new CompletableFuture<>();
|
||||
WifiAwareManager wifiAwareManager = mContext.getSystemService(WifiAwareManager.class);
|
||||
@@ -184,53 +200,73 @@ public class ConnectionManager {
|
||||
|
||||
/** Explicitly terminate any existing connection. */
|
||||
public void disconnect() {
|
||||
if (mDiscoverySession != null) {
|
||||
mDiscoverySession.close();
|
||||
mDiscoverySession = null;
|
||||
}
|
||||
mConnectionStatus.remoteDeviceName = null;
|
||||
mConnectionStatus.connected = false;
|
||||
for (ConnectionCallback callback : mConnectionCallbacks) {
|
||||
callback.onDisconnected();
|
||||
synchronized (mSessionLock) {
|
||||
if (mDiscoverySession != null) {
|
||||
mDiscoverySession.close();
|
||||
mDiscoverySession = null;
|
||||
}
|
||||
mConnectionStatus.remoteDeviceName = null;
|
||||
mConnectionStatus.connected = false;
|
||||
synchronized (mConnectionCallbacks) {
|
||||
for (ConnectionCallback callback : mConnectionCallbacks) {
|
||||
callback.onDisconnected();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onSocketAvailable(Socket socket) throws IOException {
|
||||
mRemoteIo.initialize(socket.getInputStream(), mStreamClosedCallback);
|
||||
mRemoteIo.initialize(socket.getOutputStream(), mStreamClosedCallback);
|
||||
mConnectionStatus.connected = true;
|
||||
for (ConnectionCallback callback : mConnectionCallbacks) {
|
||||
callback.onConnected(mConnectionStatus.remoteDeviceName);
|
||||
synchronized (mSessionLock) {
|
||||
mConnectionStatus.connected = true;
|
||||
synchronized (mConnectionCallbacks) {
|
||||
for (ConnectionCallback callback : mConnectionCallbacks) {
|
||||
callback.onConnected(mConnectionStatus.remoteDeviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onError(String message) {
|
||||
Log.e(TAG, "Error: " + message);
|
||||
for (ConnectionCallback callback : mConnectionCallbacks) {
|
||||
callback.onError(message);
|
||||
synchronized (mConnectionCallbacks) {
|
||||
for (ConnectionCallback callback : mConnectionCallbacks) {
|
||||
callback.onError(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class VdmDiscoverySessionCallback extends DiscoverySessionCallback {
|
||||
|
||||
@GuardedBy("mSessionLock")
|
||||
private NetworkCallback mNetworkCallback;
|
||||
|
||||
@Override
|
||||
public void onSessionTerminated() {
|
||||
disconnect();
|
||||
if (mNetworkCallback != null) {
|
||||
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
|
||||
synchronized (mSessionLock) {
|
||||
if (mNetworkCallback != null) {
|
||||
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendLocalEndpointId(PeerHandle peerHandle) {
|
||||
mDiscoverySession.sendMessage(peerHandle, 0, getLocalEndpointId().getBytes());
|
||||
synchronized (mSessionLock) {
|
||||
mDiscoverySession.sendMessage(peerHandle, 0, getLocalEndpointId().getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
void onConnecting(byte[] remoteDeviceName) {
|
||||
mConnectionStatus.remoteDeviceName = new String(remoteDeviceName);
|
||||
Log.e(TAG, "Connecting to " + mConnectionStatus.remoteDeviceName);
|
||||
for (ConnectionCallback callback : mConnectionCallbacks) {
|
||||
callback.onConnecting(mConnectionStatus.remoteDeviceName);
|
||||
synchronized (mSessionLock) {
|
||||
mConnectionStatus.remoteDeviceName = new String(remoteDeviceName);
|
||||
Log.e(TAG, "Connecting to " + mConnectionStatus.remoteDeviceName);
|
||||
synchronized (mConnectionCallbacks) {
|
||||
for (ConnectionCallback callback : mConnectionCallbacks) {
|
||||
callback.onConnecting(mConnectionStatus.remoteDeviceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,8 +282,10 @@ public class ConnectionManager {
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
|
||||
.setNetworkSpecifier(networkSpecifierBuilder.build())
|
||||
.build();
|
||||
mNetworkCallback = networkCallback;
|
||||
mConnectivityManager.requestNetwork(networkRequest, networkCallback);
|
||||
synchronized (mSessionLock) {
|
||||
mNetworkCallback = networkCallback;
|
||||
mConnectivityManager.requestNetwork(networkRequest, mNetworkCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,12 +293,14 @@ public class ConnectionManager {
|
||||
|
||||
@Override
|
||||
public void onPublishStarted(@NonNull PublishDiscoverySession session) {
|
||||
mDiscoverySession = session;
|
||||
synchronized (mSessionLock) {
|
||||
mDiscoverySession = session;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
|
||||
if (mConnectionStatus.connected) {
|
||||
if (isConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -284,7 +324,9 @@ public class ConnectionManager {
|
||||
|
||||
@Override
|
||||
public void onSubscribeStarted(@NonNull SubscribeDiscoverySession session) {
|
||||
mDiscoverySession = session;
|
||||
synchronized (mSessionLock) {
|
||||
mDiscoverySession = session;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -295,7 +337,7 @@ public class ConnectionManager {
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
|
||||
if (mConnectionStatus.connected) {
|
||||
if (isConnected()) {
|
||||
return;
|
||||
}
|
||||
onConnecting(message);
|
||||
@@ -316,7 +358,7 @@ public class ConnectionManager {
|
||||
@Override
|
||||
public void onCapabilitiesChanged(@NonNull Network network,
|
||||
@NonNull NetworkCapabilities networkCapabilities) {
|
||||
if (mConnectionStatus.connected) {
|
||||
if (isConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,13 @@ import android.os.HandlerThread;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
|
||||
import com.example.android.vdmdemo.common.RemoteEventProto.RemoteEvent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -44,12 +45,16 @@ public class RemoteIo {
|
||||
void onStreamClosed();
|
||||
}
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private OutputStream mOutputStream = null;
|
||||
|
||||
private StreamClosedCallback mOutputStreamClosedCallback = null;
|
||||
private final Handler mSendMessageHandler;
|
||||
|
||||
private final Map<Object, MessageConsumer> mMessageConsumers =
|
||||
Collections.synchronizedMap(new ArrayMap<>());
|
||||
@GuardedBy("mMessageConsumers")
|
||||
private final Map<Object, MessageConsumer> mMessageConsumers = new ArrayMap<>();
|
||||
|
||||
@Inject
|
||||
RemoteIo() {
|
||||
@@ -60,50 +65,45 @@ public class RemoteIo {
|
||||
|
||||
@SuppressWarnings("ThreadPriorityCheck")
|
||||
void initialize(InputStream inputStream, StreamClosedCallback inputStreamClosedCallback) {
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
while (true) {
|
||||
RemoteEvent event = RemoteEvent.parseDelimitedFrom(inputStream);
|
||||
if (event == null) {
|
||||
break;
|
||||
}
|
||||
mMessageConsumers.values().forEach(consumer -> {
|
||||
if (consumer != null) {
|
||||
consumer.accept(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to obtain event: " + e);
|
||||
}
|
||||
inputStreamClosedCallback.onStreamClosed();
|
||||
});
|
||||
Thread t = new Thread(new ReceiverRunnable(inputStream, inputStreamClosedCallback));
|
||||
t.setPriority(Thread.MAX_PRIORITY);
|
||||
t.start();
|
||||
}
|
||||
|
||||
synchronized void initialize(
|
||||
void initialize(
|
||||
OutputStream outputStream, StreamClosedCallback outputStreamClosedCallback) {
|
||||
mOutputStream = outputStream;
|
||||
mOutputStreamClosedCallback = outputStreamClosedCallback;
|
||||
synchronized (mLock) {
|
||||
mOutputStream = outputStream;
|
||||
mOutputStreamClosedCallback = outputStreamClosedCallback;
|
||||
}
|
||||
}
|
||||
|
||||
/** Registers a consumer for processing events coming from the remote device. */
|
||||
public void addMessageConsumer(Consumer<RemoteEvent> consumer) {
|
||||
mMessageConsumers.put(consumer, new MessageConsumer(consumer));
|
||||
synchronized (mMessageConsumers) {
|
||||
mMessageConsumers.put(consumer, new MessageConsumer(consumer));
|
||||
}
|
||||
}
|
||||
|
||||
/** Unregisters a previously registered message consumer. */
|
||||
public void removeMessageConsumer(Consumer<RemoteEvent> consumer) {
|
||||
if (mMessageConsumers.remove(consumer) == null) {
|
||||
Log.w(TAG, "Failed to remove message consumer.");
|
||||
synchronized (mMessageConsumers) {
|
||||
if (mMessageConsumers.remove(consumer) == null) {
|
||||
Log.w(TAG, "Failed to remove message consumer.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Sends an event to the remote device. */
|
||||
public synchronized void sendMessage(RemoteEvent event) {
|
||||
if (mOutputStream != null) {
|
||||
mSendMessageHandler.post(() -> {
|
||||
public void sendMessage(RemoteEvent event) {
|
||||
synchronized (mLock) {
|
||||
if (mOutputStream == null) {
|
||||
Log.e(TAG, "Failed to send event, RemoteIO not initialized.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
mSendMessageHandler.post(() -> {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
event.writeDelimitedTo(mOutputStream);
|
||||
mOutputStream.flush();
|
||||
@@ -111,9 +111,36 @@ public class RemoteIo {
|
||||
mOutputStream = null;
|
||||
mOutputStreamClosedCallback.onStreamClosed();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.e(TAG, "Failed to send event, RemoteIO not initialized.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class ReceiverRunnable implements Runnable {
|
||||
|
||||
private final InputStream mInputStream;
|
||||
private final StreamClosedCallback mInputStreamClosedCallback;
|
||||
|
||||
ReceiverRunnable(InputStream inputStream, StreamClosedCallback inputStreamClosedCallback) {
|
||||
mInputStream = inputStream;
|
||||
mInputStreamClosedCallback = inputStreamClosedCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
RemoteEvent event = RemoteEvent.parseDelimitedFrom(mInputStream);
|
||||
if (event == null) {
|
||||
break;
|
||||
}
|
||||
synchronized (mMessageConsumers) {
|
||||
mMessageConsumers.values().forEach(consumer -> consumer.accept(event));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to obtain event: " + e);
|
||||
}
|
||||
mInputStreamClosedCallback.onStreamClosed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ function die() {
|
||||
}
|
||||
|
||||
function run_cmd_or_die() {
|
||||
"${@}" > /dev/null 2>&1 || die "Command failed: ${*}"
|
||||
"${@}" > /dev/null || die "Command failed: ${*}"
|
||||
}
|
||||
|
||||
function select_device() {
|
||||
@@ -30,6 +30,13 @@ function select_device() {
|
||||
done
|
||||
}
|
||||
|
||||
function install_app() {
|
||||
if ! adb -s "${1}" install -r -d -g "${2}" > /dev/null 2>&1; then
|
||||
adb -s "${1}" uninstall "com.example.android.vdmdemo.${3}" > /dev/null 2>&1
|
||||
run_cmd_or_die adb -s "${1}" install -r -d -g "${2}"
|
||||
fi
|
||||
}
|
||||
|
||||
[[ -f build/make/envsetup.sh ]] || die "Run this script from the root of the tree."
|
||||
|
||||
DEVICE_COUNT=$(adb devices -l | tail -n +2 | head -n -1 | wc -l)
|
||||
@@ -41,36 +48,16 @@ HOST_SERIAL=""
|
||||
CLIENT_SERIAL=""
|
||||
|
||||
echo
|
||||
if ((DEVICE_COUNT > 1)); then
|
||||
echo "Multiple devices found:"
|
||||
for i in "${!DEVICE_SERIALS[@]}"; do
|
||||
echo -e "${i}: ${DEVICE_SERIALS[${i}]}\t${DEVICE_NAMES[${i}]}"
|
||||
done
|
||||
echo "${DEVICE_COUNT}: Do not install this app"
|
||||
echo
|
||||
select_device "VDM Host"
|
||||
HOST_INDEX=$?
|
||||
select_device "VDM Client"
|
||||
CLIENT_INDEX=$?
|
||||
else
|
||||
DEVICE_SERIAL=${DEVICE_SERIALS[0]}
|
||||
DEVICE_NAME="${DEVICE_SERIAL} ${DEVICE_NAMES[0]}"
|
||||
cat << EOF
|
||||
0: VDM Host app
|
||||
1: VDM Client app
|
||||
2: All
|
||||
3: None
|
||||
|
||||
EOF
|
||||
while :; do
|
||||
read -r -p "Select apps to install to ${DEVICE_NAME} (0-3): " INDEX
|
||||
( [[ "${INDEX}" =~ ^[0-9]+$ ]] && ((INDEX >= 0 && INDEX <= 3)) ) || continue;
|
||||
((INDEX == 3)) && exit 0
|
||||
((INDEX != 0 && INDEX != 2)) && HOST_INDEX=DEVICE_COUNT
|
||||
((INDEX != 1 && INDEX != 2)) && CLIENT_INDEX=DEVICE_COUNT
|
||||
break
|
||||
done
|
||||
fi
|
||||
echo "Available devices:"
|
||||
for i in "${!DEVICE_SERIALS[@]}"; do
|
||||
echo -e "${i}: ${DEVICE_SERIALS[${i}]}\t${DEVICE_NAMES[${i}]}"
|
||||
done
|
||||
echo "${DEVICE_COUNT}: Do not install this app"
|
||||
echo
|
||||
select_device "VDM Host"
|
||||
HOST_INDEX=$?
|
||||
select_device "VDM Client"
|
||||
CLIENT_INDEX=$?
|
||||
echo
|
||||
|
||||
if ((HOST_INDEX == DEVICE_COUNT)); then
|
||||
@@ -103,15 +90,13 @@ UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true m -j "${APKS_TO_BUILD}" || die "Build fail
|
||||
if [[ -n "${CLIENT_SERIAL}" ]]; then
|
||||
echo
|
||||
echo "Installing VdmClient.apk to ${CLIENT_NAME}..."
|
||||
adb -s "${CLIENT_SERIAL}" uninstall com.example.android.vdmdemo.client > /dev/null 2>&1
|
||||
run_cmd_or_die adb -s "${CLIENT_SERIAL}" install -r -d -g "${OUT}/system/app/VdmClient/VdmClient.apk"
|
||||
install_app "${CLIENT_SERIAL}" "${OUT}/system/app/VdmClient/VdmClient.apk" client
|
||||
fi
|
||||
|
||||
if [[ -n "${HOST_SERIAL}" ]]; then
|
||||
echo
|
||||
echo "Installing VdmDemos.apk to ${HOST_NAME}..."
|
||||
adb -s "${HOST_SERIAL}" uninstall com.example.android.vdmdemo.demos > /dev/null 2>&1
|
||||
run_cmd_or_die adb -s "${HOST_SERIAL}" install -r -d -g "${OUT}/system/app/VdmDemos/VdmDemos.apk"
|
||||
install_app "${CLIENT_SERIAL}" "${OUT}/system/app/VdmDemos/VdmDemos.apk" demos
|
||||
echo
|
||||
|
||||
readonly PERM_BASENAME=com.example.android.vdmdemo.host.xml
|
||||
|
||||
Reference in New Issue
Block a user