Merge "[CDM ContextSync] ContextSync support APIs" into udc-dev
This commit is contained in:
@@ -800,6 +800,119 @@ public final class CompanionDeviceManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for any changes to {@link com.android.server.companion.transport.Transport}.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public interface OnTransportsChangedListener {
|
||||||
|
/**
|
||||||
|
* Invoked when a change occurs to any of the transports
|
||||||
|
*
|
||||||
|
* @param associations all the associations which have connected transports
|
||||||
|
*/
|
||||||
|
void onTransportsChanged(@NonNull List<AssociationInfo> associations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a listener for any changes to
|
||||||
|
* {@link com.android.server.companion.transport.Transport}. Your app will receive a callback to
|
||||||
|
* {@link OnTransportsChangedListener} immediately with all the existing transports.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void addOnTransportsChangedListener(
|
||||||
|
@NonNull Executor executor, @NonNull OnTransportsChangedListener listener) {
|
||||||
|
final OnTransportsChangedListenerProxy proxy = new OnTransportsChangedListenerProxy(
|
||||||
|
executor, listener);
|
||||||
|
try {
|
||||||
|
mService.addOnTransportsChangedListener(proxy);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a listener to stop receiving any changes to
|
||||||
|
* {@link com.android.server.companion.transport.Transport}.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void removeOnTransportsChangedListener(
|
||||||
|
@NonNull OnTransportsChangedListener listener) {
|
||||||
|
final OnTransportsChangedListenerProxy proxy = new OnTransportsChangedListenerProxy(
|
||||||
|
null, listener);
|
||||||
|
try {
|
||||||
|
mService.removeOnTransportsChangedListener(proxy);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to remote devices
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void sendMessage(int messageType, byte[] data, int[] associationIds) {
|
||||||
|
try {
|
||||||
|
mService.sendMessage(messageType, data, associationIds);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener when a message is received for the registered message type
|
||||||
|
*
|
||||||
|
* @see #addOnMessageReceivedListener(Executor, int, OnMessageReceivedListener)
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public interface OnMessageReceivedListener {
|
||||||
|
/**
|
||||||
|
* Called when a message is received
|
||||||
|
*/
|
||||||
|
void onMessageReceived(int associationId, byte[] data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a listener to receive callbacks when a message is received by the given type
|
||||||
|
*
|
||||||
|
* @see com.android.server.companion.transport.Transport for supported message types
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void addOnMessageReceivedListener(@NonNull Executor executor, int messageType,
|
||||||
|
OnMessageReceivedListener listener) {
|
||||||
|
final OnMessageReceivedListenerProxy proxy = new OnMessageReceivedListenerProxy(
|
||||||
|
executor, listener);
|
||||||
|
try {
|
||||||
|
mService.addOnMessageReceivedListener(messageType, proxy);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a listener to stop receiving callbacks when a message is received by the given
|
||||||
|
* type
|
||||||
|
*
|
||||||
|
* @see com.android.server.companion.transport.Transport for supported message types
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void removeOnMessageReceivedListener(int messageType,
|
||||||
|
OnMessageReceivedListener listener) {
|
||||||
|
final OnMessageReceivedListenerProxy proxy = new OnMessageReceivedListenerProxy(
|
||||||
|
null, listener);
|
||||||
|
try {
|
||||||
|
mService.removeOnMessageReceivedListener(messageType, proxy);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the bluetooth device represented by the mac address was recently associated
|
* Checks whether the bluetooth device represented by the mac address was recently associated
|
||||||
* with the companion app. This allows these devices to skip the Bluetooth pairing dialog if
|
* with the companion app. This allows these devices to skip the Bluetooth pairing dialog if
|
||||||
@@ -1277,6 +1390,40 @@ public final class CompanionDeviceManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class OnTransportsChangedListenerProxy
|
||||||
|
extends IOnTransportsChangedListener.Stub {
|
||||||
|
private final Executor mExecutor;
|
||||||
|
private final OnTransportsChangedListener mListener;
|
||||||
|
|
||||||
|
private OnTransportsChangedListenerProxy(Executor executor,
|
||||||
|
OnTransportsChangedListener listener) {
|
||||||
|
mExecutor = executor;
|
||||||
|
mListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransportsChanged(@NonNull List<AssociationInfo> associations) {
|
||||||
|
mExecutor.execute(() -> mListener.onTransportsChanged(associations));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class OnMessageReceivedListenerProxy
|
||||||
|
extends IOnMessageReceivedListener.Stub {
|
||||||
|
private final Executor mExecutor;
|
||||||
|
private final OnMessageReceivedListener mListener;
|
||||||
|
|
||||||
|
private OnMessageReceivedListenerProxy(Executor executor,
|
||||||
|
OnMessageReceivedListener listener) {
|
||||||
|
mExecutor = executor;
|
||||||
|
mListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessageReceived(int associationId, byte[] data) {
|
||||||
|
mExecutor.execute(() -> mListener.onMessageReceived(associationId, data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class SystemDataTransferCallbackProxy extends ISystemDataTransferCallback.Stub {
|
private static class SystemDataTransferCallbackProxy extends ISystemDataTransferCallback.Stub {
|
||||||
private final Executor mExecutor;
|
private final Executor mExecutor;
|
||||||
private final OutcomeReceiver<Void, CompanionException> mCallback;
|
private final OutcomeReceiver<Void, CompanionException> mCallback;
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ package android.companion;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.companion.IAssociationRequestCallback;
|
import android.companion.IAssociationRequestCallback;
|
||||||
import android.companion.IOnAssociationsChangedListener;
|
import android.companion.IOnAssociationsChangedListener;
|
||||||
|
import android.companion.IOnMessageReceivedListener;
|
||||||
|
import android.companion.IOnTransportsChangedListener;
|
||||||
import android.companion.ISystemDataTransferCallback;
|
import android.companion.ISystemDataTransferCallback;
|
||||||
import android.companion.AssociationInfo;
|
import android.companion.AssociationInfo;
|
||||||
import android.companion.AssociationRequest;
|
import android.companion.AssociationRequest;
|
||||||
@@ -67,6 +69,16 @@ interface ICompanionDeviceManager {
|
|||||||
|
|
||||||
void removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId);
|
void removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId);
|
||||||
|
|
||||||
|
void addOnTransportsChangedListener(IOnTransportsChangedListener listener);
|
||||||
|
|
||||||
|
void removeOnTransportsChangedListener(IOnTransportsChangedListener listener);
|
||||||
|
|
||||||
|
void sendMessage(int messageType, in byte[] data, in int[] associationIds);
|
||||||
|
|
||||||
|
void addOnMessageReceivedListener(int messageType, IOnMessageReceivedListener listener);
|
||||||
|
|
||||||
|
void removeOnMessageReceivedListener(int messageType, IOnMessageReceivedListener listener);
|
||||||
|
|
||||||
void notifyDeviceAppeared(int associationId);
|
void notifyDeviceAppeared(int associationId);
|
||||||
|
|
||||||
void notifyDeviceDisappeared(int associationId);
|
void notifyDeviceDisappeared(int associationId);
|
||||||
|
|||||||
23
core/java/android/companion/IOnMessageReceivedListener.aidl
Normal file
23
core/java/android/companion/IOnMessageReceivedListener.aidl
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 per missions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.companion;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
interface IOnMessageReceivedListener {
|
||||||
|
|
||||||
|
oneway void onMessageReceived(int associationId, in byte[] data);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 per missions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.companion;
|
||||||
|
|
||||||
|
import android.companion.AssociationInfo;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
interface IOnTransportsChangedListener {
|
||||||
|
|
||||||
|
oneway void onTransportsChanged(in List<AssociationInfo> associations);
|
||||||
|
}
|
||||||
@@ -59,6 +59,8 @@ import android.companion.DeviceNotAssociatedException;
|
|||||||
import android.companion.IAssociationRequestCallback;
|
import android.companion.IAssociationRequestCallback;
|
||||||
import android.companion.ICompanionDeviceManager;
|
import android.companion.ICompanionDeviceManager;
|
||||||
import android.companion.IOnAssociationsChangedListener;
|
import android.companion.IOnAssociationsChangedListener;
|
||||||
|
import android.companion.IOnMessageReceivedListener;
|
||||||
|
import android.companion.IOnTransportsChangedListener;
|
||||||
import android.companion.ISystemDataTransferCallback;
|
import android.companion.ISystemDataTransferCallback;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -232,7 +234,7 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
/* cdmService */this, mAssociationStore);
|
/* cdmService */this, mAssociationStore);
|
||||||
mCompanionAppController = new CompanionApplicationController(
|
mCompanionAppController = new CompanionApplicationController(
|
||||||
context, mAssociationStore, mDevicePresenceMonitor);
|
context, mAssociationStore, mDevicePresenceMonitor);
|
||||||
mTransportManager = new CompanionTransportManager(context);
|
mTransportManager = new CompanionTransportManager(context, mAssociationStore);
|
||||||
mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore,
|
mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore,
|
||||||
mSystemDataTransferRequestStore, mTransportManager);
|
mSystemDataTransferRequestStore, mTransportManager);
|
||||||
|
|
||||||
@@ -600,6 +602,37 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
mListeners.unregister(listener);
|
mListeners.unregister(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
|
||||||
|
public void addOnTransportsChangedListener(IOnTransportsChangedListener listener) {
|
||||||
|
mTransportManager.addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
|
||||||
|
public void removeOnTransportsChangedListener(IOnTransportsChangedListener listener) {
|
||||||
|
mTransportManager.removeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
|
||||||
|
public void sendMessage(int messageType, byte[] data, int[] associationIds) {
|
||||||
|
mTransportManager.sendMessage(messageType, data, associationIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
|
||||||
|
public void addOnMessageReceivedListener(int messageType,
|
||||||
|
IOnMessageReceivedListener listener) {
|
||||||
|
mTransportManager.addListener(messageType, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeOnMessageReceivedListener(int messageType,
|
||||||
|
IOnMessageReceivedListener listener) {
|
||||||
|
mTransportManager.removeListener(messageType, listener);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void legacyDisassociate(String deviceMacAddress, String packageName, int userId) {
|
public void legacyDisassociate(String deviceMacAddress, String packageName, int userId) {
|
||||||
Log.i(TAG, "legacyDisassociate() pkg=u" + userId + "/" + packageName
|
Log.i(TAG, "legacyDisassociate() pkg=u" + userId + "/" + packageName
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import android.annotation.UserIdInt;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.companion.AssociationInfo;
|
import android.companion.AssociationInfo;
|
||||||
import android.companion.DeviceNotAssociatedException;
|
import android.companion.DeviceNotAssociatedException;
|
||||||
|
import android.companion.IOnMessageReceivedListener;
|
||||||
import android.companion.ISystemDataTransferCallback;
|
import android.companion.ISystemDataTransferCallback;
|
||||||
import android.companion.datatransfer.PermissionSyncRequest;
|
import android.companion.datatransfer.PermissionSyncRequest;
|
||||||
import android.companion.datatransfer.SystemDataTransferRequest;
|
import android.companion.datatransfer.SystemDataTransferRequest;
|
||||||
@@ -40,6 +41,7 @@ import android.content.Intent;
|
|||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
@@ -92,8 +94,18 @@ public class SystemDataTransferProcessor {
|
|||||||
mAssociationStore = associationStore;
|
mAssociationStore = associationStore;
|
||||||
mSystemDataTransferRequestStore = systemDataTransferRequestStore;
|
mSystemDataTransferRequestStore = systemDataTransferRequestStore;
|
||||||
mTransportManager = transportManager;
|
mTransportManager = transportManager;
|
||||||
mTransportManager.addListener(MESSAGE_REQUEST_PERMISSION_RESTORE,
|
IOnMessageReceivedListener messageListener = new IOnMessageReceivedListener() {
|
||||||
this::onReceivePermissionRestore);
|
@Override
|
||||||
|
public void onMessageReceived(int associationId, byte[] data) throws RemoteException {
|
||||||
|
onReceivePermissionRestore(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder asBinder() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mTransportManager.addListener(MESSAGE_REQUEST_PERMISSION_RESTORE, messageListener);
|
||||||
mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
|
mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
|
||||||
mExecutor = Executors.newSingleThreadExecutor();
|
mExecutor = Executors.newSingleThreadExecutor();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,25 +22,32 @@ import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_P
|
|||||||
import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PLATFORM_INFO;
|
import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PLATFORM_INFO;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.ActivityManagerInternal;
|
import android.app.ActivityManagerInternal;
|
||||||
|
import android.companion.AssociationInfo;
|
||||||
|
import android.companion.IOnMessageReceivedListener;
|
||||||
|
import android.companion.IOnTransportsChangedListener;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.RemoteCallbackList;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.server.LocalServices;
|
import com.android.server.LocalServices;
|
||||||
import com.android.server.companion.transport.Transport.Listener;
|
import com.android.server.companion.AssociationStore;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.HashMap;
|
import java.util.ArrayList;
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
@@ -63,30 +70,99 @@ public class CompanionTransportManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
private final AssociationStore mAssociationStore;
|
||||||
|
|
||||||
|
/** Association id -> Transport */
|
||||||
@GuardedBy("mTransports")
|
@GuardedBy("mTransports")
|
||||||
private final SparseArray<Transport> mTransports = new SparseArray<>();
|
private final SparseArray<Transport> mTransports = new SparseArray<>();
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Map<Integer, Listener> mListeners = new HashMap<>();
|
private final RemoteCallbackList<IOnTransportsChangedListener> mTransportsListeners =
|
||||||
|
new RemoteCallbackList<>();
|
||||||
|
/** Message type -> IOnMessageReceivedListener */
|
||||||
|
@NonNull
|
||||||
|
private final SparseArray<IOnMessageReceivedListener> mMessageListeners = new SparseArray<>();
|
||||||
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private Transport mTempTransport;
|
private Transport mTempTransport;
|
||||||
|
|
||||||
public CompanionTransportManager(Context context) {
|
public CompanionTransportManager(Context context, AssociationStore associationStore) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
mAssociationStore = associationStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a message listener when a message is received for the message type
|
* Add a listener to receive callbacks when a message is received for the message type
|
||||||
*/
|
*/
|
||||||
@GuardedBy("mTransports")
|
@GuardedBy("mTransports")
|
||||||
public void addListener(int message, @NonNull Listener listener) {
|
public void addListener(int message, @NonNull IOnMessageReceivedListener listener) {
|
||||||
mListeners.put(message, listener);
|
mMessageListeners.put(message, listener);
|
||||||
for (int i = 0; i < mTransports.size(); i++) {
|
for (int i = 0; i < mTransports.size(); i++) {
|
||||||
mTransports.valueAt(i).addListener(message, listener);
|
mTransports.valueAt(i).addListener(message, listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a listener to receive callbacks when any of the transports is changed
|
||||||
|
*/
|
||||||
|
@GuardedBy("mTransports")
|
||||||
|
public void addListener(IOnTransportsChangedListener listener) {
|
||||||
|
Slog.i(TAG, "Registering OnTransportsChangedListener");
|
||||||
|
mTransportsListeners.register(listener);
|
||||||
|
List<AssociationInfo> associations = new ArrayList<>();
|
||||||
|
for (int i = 0; i < mTransports.size(); i++) {
|
||||||
|
AssociationInfo association = mAssociationStore.getAssociationById(
|
||||||
|
mTransports.keyAt(i));
|
||||||
|
if (association != null) {
|
||||||
|
associations.add(association);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mTransportsListeners.broadcast(listener1 -> {
|
||||||
|
// callback to the current listener with all the associations of the transports
|
||||||
|
// immediately
|
||||||
|
if (listener1 == listener) {
|
||||||
|
try {
|
||||||
|
listener.onTransportsChanged(associations);
|
||||||
|
} catch (RemoteException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the listener for receiving callbacks when any of the transports is changed
|
||||||
|
*/
|
||||||
|
public void removeListener(IOnTransportsChangedListener listener) {
|
||||||
|
mTransportsListeners.unregister(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the listener to stop receiving calbacks when a message is received for the given type
|
||||||
|
*/
|
||||||
|
public void removeListener(int messageType, IOnMessageReceivedListener listener) {
|
||||||
|
mMessageListeners.remove(messageType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to remote devices through the transports
|
||||||
|
*/
|
||||||
|
@GuardedBy("mTransports")
|
||||||
|
public void sendMessage(int message, byte[] data, int[] associationIds) {
|
||||||
|
Slog.i(TAG, "Sending message 0x" + Integer.toHexString(message)
|
||||||
|
+ " data length " + data.length);
|
||||||
|
for (int i = 0; i < associationIds.length; i++) {
|
||||||
|
if (mTransports.contains(associationIds[i])) {
|
||||||
|
try {
|
||||||
|
mTransports.get(associationIds[i]).sendMessage(message, data);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(TAG, "Failed to send message 0x" + Integer.toHexString(message)
|
||||||
|
+ " data length " + data.length + " to association "
|
||||||
|
+ associationIds[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For the moment, we only offer transporting of system data to built-in
|
* For the moment, we only offer transporting of system data to built-in
|
||||||
* companion apps; future work will improve the security model to support
|
* companion apps; future work will improve the security model to support
|
||||||
@@ -119,6 +195,8 @@ public class CompanionTransportManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initializeTransport(associationId, fd);
|
initializeTransport(associationId, fd);
|
||||||
|
|
||||||
|
notifyOnTransportsChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,16 +208,35 @@ public class CompanionTransportManager {
|
|||||||
mTransports.delete(associationId);
|
mTransports.delete(associationId);
|
||||||
transport.stop();
|
transport.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifyOnTransportsChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mTransports")
|
||||||
|
private void notifyOnTransportsChanged() {
|
||||||
|
List<AssociationInfo> associations = new ArrayList<>();
|
||||||
|
for (int i = 0; i < mTransports.size(); i++) {
|
||||||
|
AssociationInfo association = mAssociationStore.getAssociationById(
|
||||||
|
mTransports.keyAt(i));
|
||||||
|
if (association != null) {
|
||||||
|
associations.add(association);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mTransportsListeners.broadcast(listener -> {
|
||||||
|
try {
|
||||||
|
listener.onTransportsChanged(associations);
|
||||||
|
} catch (RemoteException ignored) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@GuardedBy("mTransports")
|
@GuardedBy("mTransports")
|
||||||
private void initializeTransport(int associationId, ParcelFileDescriptor fd) {
|
private void initializeTransport(int associationId, ParcelFileDescriptor fd) {
|
||||||
|
Slog.i(TAG, "Initializing transport");
|
||||||
if (!isSecureTransportEnabled()) {
|
if (!isSecureTransportEnabled()) {
|
||||||
Transport transport = new RawTransport(associationId, fd, mContext);
|
Transport transport = new RawTransport(associationId, fd, mContext);
|
||||||
for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
|
addMessageListenersToTransport(transport);
|
||||||
transport.addListener(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
transport.start();
|
transport.start();
|
||||||
mTransports.put(associationId, transport);
|
mTransports.put(associationId, transport);
|
||||||
Slog.i(TAG, "RawTransport is created");
|
Slog.i(TAG, "RawTransport is created");
|
||||||
@@ -148,10 +245,21 @@ public class CompanionTransportManager {
|
|||||||
|
|
||||||
// Exchange platform info to decide which transport should be created
|
// Exchange platform info to decide which transport should be created
|
||||||
mTempTransport = new RawTransport(associationId, fd, mContext);
|
mTempTransport = new RawTransport(associationId, fd, mContext);
|
||||||
for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
|
addMessageListenersToTransport(mTempTransport);
|
||||||
mTempTransport.addListener(entry.getKey(), entry.getValue());
|
IOnMessageReceivedListener listener = new IOnMessageReceivedListener() {
|
||||||
}
|
@Override
|
||||||
mTempTransport.addListener(MESSAGE_REQUEST_PLATFORM_INFO, this::onPlatformInfoReceived);
|
public void onMessageReceived(int associationId, byte[] data) throws RemoteException {
|
||||||
|
synchronized (mTransports) {
|
||||||
|
onPlatformInfoReceived(associationId, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder asBinder() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mTempTransport.addListener(MESSAGE_REQUEST_PLATFORM_INFO, listener);
|
||||||
mTempTransport.start();
|
mTempTransport.start();
|
||||||
|
|
||||||
int sdk = Build.VERSION.SDK_INT;
|
int sdk = Build.VERSION.SDK_INT;
|
||||||
@@ -163,14 +271,21 @@ public class CompanionTransportManager {
|
|||||||
.put(release.getBytes());
|
.put(release.getBytes());
|
||||||
|
|
||||||
// TODO: it should check if preSharedKey is given
|
// TODO: it should check if preSharedKey is given
|
||||||
mTempTransport.requestForResponse(MESSAGE_REQUEST_PLATFORM_INFO, data.array());
|
try {
|
||||||
|
mTempTransport.sendMessage(MESSAGE_REQUEST_PLATFORM_INFO, data.array());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(TAG, "Failed to exchange platform info");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Depending on the remote platform info to decide which transport should be created
|
* Depending on the remote platform info to decide which transport should be created
|
||||||
*/
|
*/
|
||||||
@GuardedBy("mTransports")
|
@GuardedBy("CompanionTransportManager.this.mTransports")
|
||||||
private void onPlatformInfoReceived(byte[] data) {
|
private void onPlatformInfoReceived(int associationId, byte[] data) {
|
||||||
|
if (mTempTransport.getAssociationId() != associationId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// TODO: it should check if preSharedKey is given
|
// TODO: it should check if preSharedKey is given
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||||
@@ -198,12 +313,11 @@ public class CompanionTransportManager {
|
|||||||
Slog.i(TAG, "Creating a secure channel");
|
Slog.i(TAG, "Creating a secure channel");
|
||||||
transport = new SecureTransport(transport.getAssociationId(), transport.getFd(),
|
transport = new SecureTransport(transport.getAssociationId(), transport.getFd(),
|
||||||
mContext);
|
mContext);
|
||||||
for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
|
addMessageListenersToTransport(transport);
|
||||||
transport.addListener(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
transport.start();
|
transport.start();
|
||||||
}
|
}
|
||||||
mTransports.put(transport.getAssociationId(), transport);
|
mTransports.put(transport.getAssociationId(), transport);
|
||||||
|
// Doesn't need to notifyTransportsChanged here, it'll be done in attachSystemDataTransport
|
||||||
}
|
}
|
||||||
|
|
||||||
public Future<?> requestPermissionRestore(int associationId, byte[] data) {
|
public Future<?> requestPermissionRestore(int associationId, byte[] data) {
|
||||||
@@ -228,4 +342,10 @@ public class CompanionTransportManager {
|
|||||||
|
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addMessageListenersToTransport(Transport transport) {
|
||||||
|
for (int i = 0; i < mMessageListeners.size(); i++) {
|
||||||
|
transport.addListener(mMessageListeners.keyAt(i), mMessageListeners.valueAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,12 @@
|
|||||||
package com.android.server.companion.transport;
|
package com.android.server.companion.transport;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.companion.IOnMessageReceivedListener;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
@@ -45,7 +47,8 @@ public abstract class Transport {
|
|||||||
protected static final boolean DEBUG = Build.IS_DEBUGGABLE;
|
protected static final boolean DEBUG = Build.IS_DEBUGGABLE;
|
||||||
|
|
||||||
static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN
|
static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN
|
||||||
public static final int MESSAGE_REQUEST_PLATFORM_INFO = 0x63807086; // ?PFV
|
public static final int MESSAGE_REQUEST_PLATFORM_INFO = 0x63807073; // ?PFI
|
||||||
|
public static final int MESSAGE_REQUEST_CONTEXT_SYNC = 0x63678883; // ?CXS
|
||||||
public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
|
public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
|
||||||
|
|
||||||
static final int MESSAGE_RESPONSE_SUCCESS = 0x33838567; // !SUC
|
static final int MESSAGE_RESPONSE_SUCCESS = 0x33838567; // !SUC
|
||||||
@@ -59,19 +62,14 @@ public abstract class Transport {
|
|||||||
protected final OutputStream mRemoteOut;
|
protected final OutputStream mRemoteOut;
|
||||||
protected final Context mContext;
|
protected final Context mContext;
|
||||||
|
|
||||||
/** Message type -> Listener */
|
|
||||||
private final Map<Integer, Listener> mListeners;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message listener
|
* Message type -> Listener
|
||||||
|
*
|
||||||
|
* For now, the transport only supports 1 listener for each message type. If there's a need in
|
||||||
|
* the future to allow multiple listeners to receive callbacks for the same message type, the
|
||||||
|
* value of the map can be a list.
|
||||||
*/
|
*/
|
||||||
public interface Listener {
|
private final Map<Integer, IOnMessageReceivedListener> mListeners;
|
||||||
/**
|
|
||||||
* Called when a message is received
|
|
||||||
* @param data data content in the message
|
|
||||||
*/
|
|
||||||
void onDataReceived(byte[] data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isRequest(int message) {
|
private static boolean isRequest(int message) {
|
||||||
return (message & 0xFF000000) == 0x63000000;
|
return (message & 0xFF000000) == 0x63000000;
|
||||||
@@ -100,7 +98,7 @@ public abstract class Transport {
|
|||||||
* @param message Message type
|
* @param message Message type
|
||||||
* @param listener Execute when a message with the type is received
|
* @param listener Execute when a message with the type is received
|
||||||
*/
|
*/
|
||||||
public void addListener(int message, Listener listener) {
|
public void addListener(int message, IOnMessageReceivedListener listener) {
|
||||||
mListeners.put(message, listener);
|
mListeners.put(message, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +115,13 @@ public abstract class Transport {
|
|||||||
protected abstract void sendMessage(int message, int sequence, @NonNull byte[] data)
|
protected abstract void sendMessage(int message, int sequence, @NonNull byte[] data)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message
|
||||||
|
*/
|
||||||
|
public void sendMessage(int message, @NonNull byte[] data) throws IOException {
|
||||||
|
sendMessage(message, mNextSequence.incrementAndGet(), data);
|
||||||
|
}
|
||||||
|
|
||||||
public Future<byte[]> requestForResponse(int message, byte[] data) {
|
public Future<byte[]> requestForResponse(int message, byte[] data) {
|
||||||
if (DEBUG) Slog.d(TAG, "Requesting for response");
|
if (DEBUG) Slog.d(TAG, "Requesting for response");
|
||||||
final int sequence = mNextSequence.incrementAndGet();
|
final int sequence = mNextSequence.incrementAndGet();
|
||||||
@@ -165,7 +170,8 @@ public abstract class Transport {
|
|||||||
sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, data);
|
sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MESSAGE_REQUEST_PLATFORM_INFO: {
|
case MESSAGE_REQUEST_PLATFORM_INFO:
|
||||||
|
case MESSAGE_REQUEST_CONTEXT_SYNC: {
|
||||||
callback(message, data);
|
callback(message, data);
|
||||||
sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, EmptyArray.BYTE);
|
sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, EmptyArray.BYTE);
|
||||||
break;
|
break;
|
||||||
@@ -196,7 +202,13 @@ public abstract class Transport {
|
|||||||
|
|
||||||
private void callback(int message, byte[] data) {
|
private void callback(int message, byte[] data) {
|
||||||
if (mListeners.containsKey(message)) {
|
if (mListeners.containsKey(message)) {
|
||||||
mListeners.get(message).onDataReceived(data);
|
try {
|
||||||
|
mListeners.get(message).onMessageReceived(getAssociationId(), data);
|
||||||
|
Slog.i(TAG, "Message 0x" + Integer.toHexString(message)
|
||||||
|
+ " is received from associationId " + mAssociationId
|
||||||
|
+ ", sending data length " + data.length + " to the listener.");
|
||||||
|
} catch (RemoteException ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user