Initial commit for connectivity.

Bug: 290265761
Bug: 291146480
Bug: 290275468
Test: None
Change-Id: Iaba64415cf81454b6e999f10bad54810884fc3da
This commit is contained in:
Salil Rajadhyaksha 2023-07-28 01:05:04 +00:00
commit 67b9284de0
12 changed files with 794 additions and 8 deletions

View file

@ -23,7 +23,6 @@ This directory contains code for the RemoteAuth module.
### AIDEGen
AIDEGen is deprecated, prefer ASfP [go/asfp](http://go/asfp)
```sh
$ source build/envsetup.sh && lunch <TARGET>
$ cd packages/modules/Connectivity
@ -31,17 +30,13 @@ $ aidegen .
# This will launch Intellij project for RemoteAuth module.
```
### ASfP
See full instructions for ASfP at [go/asfp-getting-started](http://go/asfp-getting-started)
## Build and Install
```sh
$ source build/envsetup.sh && lunch <TARGET>
$ m com.google.android.tethering deapexer
$ m com.android.tethering deapexer
$ $ANDROID_BUILD_TOP/out/host/linux-x86/bin/deapexer decompress --input \
${ANDROID_PRODUCT_OUT}/system/apex/com.google.android.tethering.capex \
${ANDROID_PRODUCT_OUT}/system/apex/com.android.tethering.capex \
--output /tmp/tethering.apex
$ adb install -r /tmp/tethering.apex
```

View file

@ -1 +1,4 @@
This is the source root for the RemoteAuthService
This is the source root for the RemoteAuthService
## Remote connectivity manager
Provides the connectivity manager to manage connections with the peer device.

View file

@ -0,0 +1,108 @@
/*
* 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 permissions and
* limitations under the License.
*/
package com.android.server.remoteauth.connectivity;
import android.annotation.NonNull;
import android.annotation.TargetApi;
import android.companion.AssociationInfo;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* Encapsulates the connection information for companion device manager connections.
*
* <p>Connection information captures the details of underlying connection such as connection id,
* type of connection and peer device mac address.
*/
// TODO(b/295407748): Change to use @DataClass.
// TODO(b/296625303): Change to VANILLA_ICE_CREAM when AssociationInfo is available in V.
@TargetApi(Build.VERSION_CODES.TIRAMISU)
public final class CdmConnectionInfo extends ConnectionInfo {
@NonNull private final AssociationInfo mAssociationInfo;
public CdmConnectionInfo(int connectionId, @NonNull AssociationInfo associationInfo) {
super(connectionId);
mAssociationInfo = associationInfo;
}
private CdmConnectionInfo(@NonNull Parcel in) {
super(in);
mAssociationInfo = in.readTypedObject(AssociationInfo.CREATOR);
}
/** Used to read CdmConnectionInfo from a Parcel */
@NonNull
public static final Parcelable.Creator<CdmConnectionInfo> CREATOR =
new Parcelable.Creator<CdmConnectionInfo>() {
public CdmConnectionInfo createFromParcel(@NonNull Parcel in) {
return new CdmConnectionInfo(in);
}
public CdmConnectionInfo[] newArray(int size) {
return new CdmConnectionInfo[size];
}
};
/** No special parcel contents. */
@Override
public int describeContents() {
return 0;
}
/**
* Flatten this CdmConnectionInfo in to a Parcel.
*
* @param out The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
*/
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeTypedObject(mAssociationInfo, 0);
}
public AssociationInfo getAssociationInfo() {
return mAssociationInfo;
}
/** Returns a string representation of ConnectionInfo. */
@Override
public String toString() {
return super.toString();
}
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof CdmConnectionInfo)) {
return false;
}
if (!super.equals(o)) {
return false;
}
CdmConnectionInfo other = (CdmConnectionInfo) o;
return mAssociationInfo.equals(other.getAssociationInfo());
}
@Override
public int hashCode() {
return Objects.hash(mAssociationInfo);
}
}

View file

@ -0,0 +1,86 @@
/*
* 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 permissions and
* limitations under the License.
*/
package com.android.server.remoteauth.connectivity;
import android.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* A connection with the peer device.
*
* <p>Connections are used to exchange data with the peer device.
*
*/
public interface Connection {
/** Unknown error. */
int ERROR_UNKNOWN = 0;
/** Message was sent successfully. */
int ERROR_OK = 1;
/** Timeout occurred while waiting for response from the peer. */
int ERROR_DEADLINE_EXCEEDED = 2;
/** Device became unavailable while sending the message. */
int ERROR_DEVICE_UNAVAILABLE = 3;
/** Represents error code. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({ERROR_UNKNOWN, ERROR_OK, ERROR_DEADLINE_EXCEEDED, ERROR_DEVICE_UNAVAILABLE})
@interface ErrorCode {}
/**
* Callback for clients to get the response of sendRequest. {@link onSuccess} is called if the
* peer device responds with Status::OK, otherwise runs the {@link onFailure} callback.
*/
abstract class MessageResponseCallback {
/**
* Called on a success.
*
* @param buffer response from the device.
*/
public void onSuccess(byte[] buffer) {}
/**
* Called when message sending fails.
*
* @param errorCode indicating the error.
*/
public void onFailure(@ErrorCode int errorCode) {}
}
/**
* Sends a request to the peer.
*
* @param request byte array to be sent to the peer device.
* @param messageResponseCallback callback to be run when the peer response is received or if an
* error occurred.
*/
void sendRequest(byte[] request, MessageResponseCallback messageResponseCallback);
/** Triggers a disconnect from the peer device. */
void disconnect();
/**
* Returns the connection information.
*
* @return A connection information object.
*/
ConnectionInfo getConnectionInfo();
}

View file

@ -0,0 +1,56 @@
/*
* 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 permissions and
* limitations under the License.
*/
package com.android.server.remoteauth.connectivity;
import static com.android.server.remoteauth.connectivity.ConnectivityManager.ReasonCode;
import android.annotation.Nullable;
/** Exception that signals that the connection request failed. */
public final class ConnectionException extends RuntimeException {
private final @ReasonCode int mReasonCode;
public ConnectionException(@ReasonCode int reasonCode) {
super();
this.mReasonCode = reasonCode;
}
public ConnectionException(@ReasonCode int reasonCode, @Nullable String message) {
super(message);
this.mReasonCode = reasonCode;
}
public ConnectionException(@ReasonCode int reasonCode, @Nullable Throwable cause) {
super(cause);
this.mReasonCode = reasonCode;
}
public ConnectionException(
@ReasonCode int reasonCode, @Nullable String message, @Nullable Throwable cause) {
super(message, cause);
this.mReasonCode = reasonCode;
}
public @ReasonCode int getReasonCode() {
return this.mReasonCode;
}
@Override
public String getMessage() {
return super.getMessage() + " Reason code: " + this.mReasonCode;
}
}

View file

@ -0,0 +1,88 @@
/*
* 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 permissions and
* limitations under the License.
*/
package com.android.server.remoteauth.connectivity;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* Encapsulates the connection information.
*
* <p>Connection information captures the details of underlying connection such as connection id,
* type of connection and peer device mac address.
*
*/
// TODO(b/295407748) Change to use @DataClass.
public abstract class ConnectionInfo implements Parcelable {
int mConnectionId;
public ConnectionInfo(int connectionId) {
mConnectionId = connectionId;
}
/** Create object from Parcel */
public ConnectionInfo(@NonNull Parcel in) {
mConnectionId = in.readInt();
}
public int getConnectionId() {
return mConnectionId;
}
/** No special parcel contents. */
@Override
public int describeContents() {
return 0;
}
/**
* Flattens this ConnectionInfo in to a Parcel.
*
* @param out The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
*/
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(mConnectionId);
}
/** Returns string representation of ConnectionInfo. */
@Override
public String toString() {
return "ConnectionInfo[" + "connectionId= " + mConnectionId + "]";
}
/** Returns true if this ConnectionInfo object is equal to the other. */
@Override
public boolean equals(Object o) {
if (!(o instanceof ConnectionInfo)) {
return false;
}
ConnectionInfo other = (ConnectionInfo) o;
return mConnectionId == other.getConnectionId();
}
/** Returns the hashcode of this object */
@Override
public int hashCode() {
return Objects.hash(mConnectionId);
}
}

View file

@ -0,0 +1,115 @@
/*
* 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 permissions and
* limitations under the License.
*/
package com.android.server.remoteauth.connectivity;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Performs discovery and triggers a connection to an associated device.
*/
public interface ConnectivityManager {
/**
* Starts device discovery.
*
* <p>Discovery continues until stopped using {@link stopDiscovery} or times out.
*
* @param discoveryFilter to filter for devices during discovery.
* @param discoveredDeviceReceiver callback to run when device is found or lost.
*/
void startDiscovery(
@NonNull DiscoveryFilter discoveryFilter,
@NonNull DiscoveredDeviceReceiver discoveredDeviceReceiver);
/**
* Stops device discovery.
*
* @param discoveryFilter filter used to start discovery.
* @param discoveredDeviceReceiver callback passed with startDiscovery.
*/
void stopDiscovery(
@NonNull DiscoveryFilter discoveryFilter,
@NonNull DiscoveredDeviceReceiver discoveredDeviceReceiver);
/** Unknown reason for connection failure. */
int ERROR_REASON_UNKNOWN = 0;
/** Indicates that the connection request timed out. */
int ERROR_CONNECTION_TIMED_OUT = 1;
/** Indicates that the connection request was refused by the peer. */
int ERROR_CONNECTION_REFUSED = 2;
/** Indicates that the peer device was unavailable. */
int ERROR_DEVICE_UNAVAILABLE = 3;
/** Reason codes for connect failure. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({ERROR_REASON_UNKNOWN, ERROR_CONNECTION_TIMED_OUT, ERROR_CONNECTION_REFUSED,
ERROR_DEVICE_UNAVAILABLE})
@interface ReasonCode {}
/**
* Initiates a connection with the peer device.
*
* @param connectionInfo of the device discovered using {@link startDiscovery}.
* @param eventListener to listen for events from the underlying transport.
* @return {@link Connection} object or null connection is not established.
* @throws ConnectionException in case connection cannot be established.
*/
@Nullable
Connection connect(
@NonNull ConnectionInfo connectionInfo, @NonNull EventListener eventListener);
/**
* Message received callback.
*
* <p>Clients should implement this callback to receive messages from the peer device.
*/
abstract class MessageReceiver {
/**
* Receive message from the peer device.
*
* <p>Clients can set empty buffer as an ACK to the request.
*
* @param messageIn message from peer device.
* @param responseCallback {@link ResponseCallback} callback to send the response back.
*/
public void onMessageReceived(byte[] messageIn, ResponseCallback responseCallback) {}
}
/**
* Starts listening for incoming messages.
*
* <p>Runs MessageReceiver callback when a message is received.
*
* @param messageReceiver to receive messages.
* @throws AssertionError if a listener is already configured.
*/
void startListening(MessageReceiver messageReceiver);
/**
* Stops listening to incoming messages.
*
* @param messageReceiver to receive messages.
*/
void stopListening(MessageReceiver messageReceiver);
}

View file

@ -0,0 +1,79 @@
/*
* 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 permissions and
* limitations under the License.
*/
package com.android.server.remoteauth.connectivity;
import android.annotation.NonNull;
import android.annotation.Nullable;
import java.util.Objects;
/** Device discovered on a network interface like Bluetooth. */
public final class DiscoveredDevice {
private @NonNull ConnectionInfo mConnectionInfo;
private @Nullable String mDisplayName;
public DiscoveredDevice(
@NonNull ConnectionInfo connectionInfo, @Nullable String displayName) {
this.mConnectionInfo = connectionInfo;
this.mDisplayName = displayName;
}
/**
* Returns connection information.
*
* @return connection information.
*/
@NonNull
public ConnectionInfo getConnectionInfo() {
return this.mConnectionInfo;
}
/**
* Returns display name of the device.
*
* @return display name string.
*/
@Nullable
public String getDisplayName() {
return this.mDisplayName;
}
/**
* Checks for equality between this and other object.
*
* @return true if equal, false otherwise.
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof DiscoveredDevice)) {
return false;
}
DiscoveredDevice other = (DiscoveredDevice) o;
return mConnectionInfo.equals(other.getConnectionInfo())
&& mDisplayName.equals(other.getDisplayName());
}
/**
* Returns hash code of the object.
*
* @return hash code.
*/
@Override
public int hashCode() {
return Objects.hash(mDisplayName, mConnectionInfo.getConnectionId());
}
}

View file

@ -0,0 +1,34 @@
/*
* 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 permissions and
* limitations under the License.
*/
package com.android.server.remoteauth.connectivity;
/** Callbacks triggered on discovery. */
public abstract class DiscoveredDeviceReceiver {
/**
* Callback called when a device matching the discovery filter is found.
*
* @param discoveredDevice the discovered device.
*/
public void onDiscovered(DiscoveredDevice discoveredDevice) {}
/**
* Callback called when a previously discovered device using {@link
* ConnectivityManager#startDiscovery} is lost.
*
* @param discoveredDevice the lost device
*/
public void onLost(DiscoveredDevice discoveredDevice) {}
}

View file

@ -0,0 +1,132 @@
/*
* 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 permissions and
* limitations under the License.
*/
package com.android.server.remoteauth.connectivity;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Filter for device discovery.
*
* <p>Callers can use this class to provide a discovery filter to the {@link
* ConnectivityManager.startDiscovery} method. A device is discovered if it matches at least one of
* the filter criteria (device type, name or peer address).
*/
public final class DiscoveryFilter {
/** Device type WATCH. */
public static final int WATCH = 0;
@Retention(RetentionPolicy.SOURCE)
@IntDef({WATCH})
public @interface DeviceType {}
private @DeviceType int mDeviceType;
private final @Nullable String mDeviceName;
private final @Nullable String mPeerAddress;
public DiscoveryFilter(
@DeviceType int deviceType, @Nullable String deviceName, @Nullable String peerAddress) {
this.mDeviceType = deviceType;
this.mDeviceName = deviceName;
this.mPeerAddress = peerAddress;
}
/**
* Returns device type.
*
* @return device type.
*/
public @DeviceType int getDeviceType() {
return this.mDeviceType;
}
/**
* Returns device name.
*
* @return device name.
*/
public @Nullable String getDeviceName() {
return this.mDeviceName;
}
/**
* Returns mac address of the peer device .
*
* @return mac address string.
*/
public @Nullable String getPeerAddress() {
return this.mPeerAddress;
}
/** Builder for {@link DiscoverFilter} */
public static class Builder {
private @DeviceType int mDeviceType;
private @Nullable String mDeviceName;
private @Nullable String mPeerAddress;
private Builder() {}
/** Static method to create a new builder */
public static Builder newInstance() {
return new Builder();
}
/**
* Sets the device type of the DiscoveryFilter.
*
* @param deviceType of the peer device.
*/
@NonNull
public Builder setDeviceType(@DeviceType int deviceType) {
mDeviceType = deviceType;
return this;
}
/**
* Sets the device name.
*
* @param deviceName May be null.
*/
@NonNull
public Builder setDeviceName(String deviceName) {
mDeviceName = deviceName;
return this;
}
/**
* Sets the peer address.
*
* @param peerAddress Mac address of the peer device.
*/
@NonNull
public Builder setPeerAddress(String peerAddress) {
mPeerAddress = peerAddress;
return this;
}
/** Builds the DiscoveryFilter object. */
@NonNull
public DiscoveryFilter build() {
return new DiscoveryFilter(this.mDeviceType, this.mDeviceName, this.mPeerAddress);
}
}
}

View file

@ -0,0 +1,27 @@
/*
* 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 permissions and
* limitations under the License.
*/
package com.android.server.remoteauth.connectivity;
import android.annotation.NonNull;
/**
* Listens to the events from underlying transport.
*/
interface EventListener {
/** Called when remote device is disconnected from the underlying transport. */
void onDisconnect(@NonNull ConnectionInfo connectionInfo);
}

View file

@ -0,0 +1,63 @@
/*
* 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 permissions and
* limitations under the License.
*/
package com.android.server.remoteauth.connectivity;
import android.annotation.NonNull;
/**
* Abstract class to expose a callback for clients to send a response to the peer device.
*
* <p>When a device receives a message on a connection, this object is constructed using the
* connection information of the connection and the message id from the incoming message. This
* object is forwarded to the clients of the connection to allow them to send a response to the peer
* device.
*/
public abstract class ResponseCallback {
private final long mMessageId;
private final ConnectionInfo mConnectionInfo;
public ResponseCallback(long messageId, @NonNull ConnectionInfo connectionInfo) {
mMessageId = messageId;
mConnectionInfo = connectionInfo;
}
/**
* Returns message id identifying the message.
*
* @return message id of this message.
*/
public long getMessageId() {
return mMessageId;
}
/**
* Returns connection info from the response.
*
* @return connection info.
*/
@NonNull
public ConnectionInfo getConnectionInfo() {
return mConnectionInfo;
}
/**
* Callback to send a response to the peer device.
*
* @param response buffer to send to the peer device.
*/
public void onResponse(@NonNull byte[] response) {}
}