diff options
author | Android Auto Companion <no-reply@google.com> | 2023-09-25 23:29:25 -0700 |
---|---|---|
committer | Alexandre Walsh <alwa@google.com> | 2023-10-05 20:43:56 +0000 |
commit | d6307bddcdb3f87798901252e2559c98d36aa64d (patch) | |
tree | 8b2b79c6ed85220ed63533ece0fb788151efda07 | |
parent | 0531a2714f556a295ab410ada44ec4898718efae (diff) | |
download | CompanionDeviceSupport-d6307bddcdb3f87798901252e2559c98d36aa64d.tar.gz |
Project import generated by Copybaraub-automotive-master-20231102ub-automotive-master-20231011
Companion SDK version: 2.0.1
Compatible Android Mobile Version: 1.0.0
Compatible IOS Mobile Version: 1.0.0
Release-Id: aae-companiondevice-android_20230925.01_RC00
Change-Id: I9dd6116e5dbf3fa93f19206142863bba68a2cb49
9 files changed, 321 insertions, 97 deletions
diff --git a/companiondevice/build.gradle b/companiondevice/build.gradle index cbf8085..1abf7c7 100644 --- a/companiondevice/build.gradle +++ b/companiondevice/build.gradle @@ -8,13 +8,13 @@ buildscript { } android { - compileSdkVersion 33 + compileSdkVersion 34 defaultConfig { applicationId "com.google.android.companiondevicesupport" minSdkVersion 29 - targetSdkVersion 33 - versionCode 2161 + targetSdkVersion 34 + versionCode 2197 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/libs/connecteddevice/res/values/config.xml b/libs/connecteddevice/res/values/config.xml index 910e553..ee45330 100644 --- a/libs/connecteddevice/res/values/config.xml +++ b/libs/connecteddevice/res/values/config.xml @@ -17,8 +17,8 @@ <resources> <!-- Current version of the SDK --> - <string name="hu_companion_sdk_version" translatable="false">2.0.0</string> - <integer name="hu_companion_binder_version" translatable="false">0</integer> + <string name="hu_companion_sdk_version" translatable="false">2.0.1</string> + <integer name="hu_companion_binder_version" translatable="false">1</integer> <!-- Mobile SDK of later version should be compatible with the HU SDK. --> <string name="compatible_min_mobile_version_android" translatable="false">1.0.0</string> <string name="compatible_min_mobile_version_ios" translatable="false">1.0.0</string> diff --git a/libs/connecteddevice/src/com/google/android/connecteddevice/api/FeatureConnector.kt b/libs/connecteddevice/src/com/google/android/connecteddevice/api/FeatureConnector.kt index 4f61708..2631c56 100644 --- a/libs/connecteddevice/src/com/google/android/connecteddevice/api/FeatureConnector.kt +++ b/libs/connecteddevice/src/com/google/android/connecteddevice/api/FeatureConnector.kt @@ -59,7 +59,6 @@ import java.util.concurrent.locks.ReentrantLock class FeatureConnector( private val context: Context, override val featureId: ParcelUuid, - override val callback: SafeConnector.Callback, private val minSupportedVersion: Int = 0 ) : SafeConnector { @@ -75,6 +74,11 @@ class FeatureConnector( private val queryIdGenerator = QueryIdGenerator() + override lateinit var callback: SafeConnector.Callback + + override val isConnected: Boolean + get() = coordinatorProxy != null && !waitingForConnection.get() + override val connectedDevices: List<String> get() = coordinatorProxy?.getConnectedDevices() ?: emptyList() @@ -128,6 +132,10 @@ class FeatureConnector( loge("Incompatible companion platform version. Aborting.") return } + if (!this@FeatureConnector::callback.isInitialized) { + loge("FeatureConnector callback was not initialized. Aborting.") + return + } coordinatorProxy = when { platformVersion > 0 -> @@ -169,8 +177,9 @@ class FeatureConnector( } } - init { - logd("Initiating connection to companion platform.") + override fun connect(callback: SafeConnector.Callback) { + logd("Initiating connection to companion platform and initializing callback.") + this.callback = callback bindToService(ACTION_QUERY_API_VERSION, versionCheckConnection) } @@ -220,7 +229,7 @@ class FeatureConnector( } } - override fun cleanUp() { + override fun disconnect() { logd("Disconnecting from the companion platform.") coordinatorProxy?.cleanUp() coordinatorProxy = null @@ -240,7 +249,7 @@ class FeatureConnector( private fun onServiceDisconnected() { logd("Service has disconnected. Cleaning up.") - cleanUp() + disconnect() } private fun onNullBinding() { @@ -411,6 +420,7 @@ class FeatureConnector( /** A generator of unique IDs for queries. */ private class QueryIdGenerator { private val messageId = AtomicInteger(0) + fun next(): Int { val current = messageId.getAndIncrement() messageId.compareAndSet(Int.MAX_VALUE, 0) diff --git a/libs/connecteddevice/src/com/google/android/connecteddevice/api/SafeConnector.kt b/libs/connecteddevice/src/com/google/android/connecteddevice/api/SafeConnector.kt index 43d7309..513a47e 100644 --- a/libs/connecteddevice/src/com/google/android/connecteddevice/api/SafeConnector.kt +++ b/libs/connecteddevice/src/com/google/android/connecteddevice/api/SafeConnector.kt @@ -32,18 +32,23 @@ interface SafeConnector { val featureId: ParcelUuid /** [Callback] for connection events. */ - val callback: Callback + var callback: Callback /** List of ids for the currently connected devices. */ val connectedDevices: List<String> + /** Whether this [SafeConnector] is currently connected and ready for interaction. */ + val isConnected: Boolean + + /** Establishes a connection to the companion platform. */ + fun connect(callback: Callback) + /** - * Cleans up services and feature coordinators attached to the companion platform. - * Calling cleanUp will disconnect the feature from the platform and prevent it from receiving - * Companion related events. Expectation is that cleanUp will only be called before the - * SafeConnector object is disposed. - */ - fun cleanUp() + * Cleans up services and feature coordinators attached to the companion platform. Will disconnect + * the feature from the platform and prevent it from receiving Companion-related events. + * Expectation is that disconnect will only be called before the SafeConnector object is disposed. + */ + fun disconnect() /** Sends message to a device. */ fun sendMessage(deviceId: String, message: ByteArray) diff --git a/libs/connecteddevice/src/com/google/android/connecteddevice/core/MultiProtocolDeviceController.kt b/libs/connecteddevice/src/com/google/android/connecteddevice/core/MultiProtocolDeviceController.kt index bec3c8e..6436fda 100644 --- a/libs/connecteddevice/src/com/google/android/connecteddevice/core/MultiProtocolDeviceController.kt +++ b/libs/connecteddevice/src/com/google/android/connecteddevice/core/MultiProtocolDeviceController.kt @@ -164,7 +164,7 @@ constructor( val driverDevices = storage.driverAssociatedDevices for (device in driverDevices) { if (device.isConnectionEnabled) { - initiateConnectionToDevice(UUID.fromString(device.deviceId)) + initiateConnectionToDevice(UUID.fromString(device.deviceId)) } } if (!enablePassenger) { @@ -174,7 +174,7 @@ constructor( logd(TAG, "Initiating connections with passenger devices.") val passengerDevices = storage.passengerAssociatedDevices for (device in passengerDevices) { - initiateConnectionToDevice(UUID.fromString(device.deviceId)) + initiateConnectionToDevice(UUID.fromString(device.deviceId)) } } } @@ -406,6 +406,7 @@ constructor( ) = object : IDiscoveryCallback.Stub() { override fun onDeviceConnected(protocolId: String) { + metricLogger.pushConnectedEvent() logd( TAG, "New connection protocol connected for $deviceId. id: $protocolId, protocol: $protocol" @@ -443,6 +444,7 @@ constructor( } override fun onDiscoveryStartedSuccessfully() { + metricLogger.pushDiscoveryStartedEvent() logd(TAG, "Connection discovery started successfully.") } @@ -507,6 +509,7 @@ constructor( } override fun onDiscoveryStartedSuccessfully() { + metricLogger.pushAssociationStartedEvent() associationCallback.aliveOrNull()?.onAssociationStartSuccess(response) ?: run { loge( @@ -629,6 +632,7 @@ constructor( } private fun handleAssociationError(error: Int, device: ConnectedRemoteDevice) { + metricLogger.pushCompanionErrorEvent(error, duringAssociation = device.callback != null) device.callback?.aliveOrNull()?.onAssociationError(error) ?: run { loge( @@ -664,7 +668,7 @@ constructor( callback.onSecureChannelEstablished(connectedDevice) } EventLog.onSecureChannelEstablished() - metricLogger.onSecureChannelEstablished() + metricLogger.pushSecureChannelEstablishedEvent() } override fun onEstablishSecureChannelFailure(error: ChannelError) { diff --git a/libs/connecteddevice/src/com/google/android/connecteddevice/metrics/CompanionStatsLog.java b/libs/connecteddevice/src/com/google/android/connecteddevice/metrics/CompanionStatsLog.java index 08c2c19..11e7dab 100644 --- a/libs/connecteddevice/src/com/google/android/connecteddevice/metrics/CompanionStatsLog.java +++ b/libs/connecteddevice/src/com/google/android/connecteddevice/metrics/CompanionStatsLog.java @@ -17,6 +17,13 @@ public final class CompanionStatsLog { */ public static final int COMPANION_STATUS_CHANGED = 210401; + /** + * CompanionErrorReported companion_error_reported<br> + * Usage: StatsLog.write(StatsLog.COMPANION_ERROR_REPORTED, int uid, int companion_error, boolean + * during_association);<br> + */ + public static final int COMPANION_ERROR_REPORTED = 210402; + // Constants for enum values. // Values for CompanionStatusChanged.companion_status @@ -29,6 +36,28 @@ public final class CompanionStatsLog { 4; public static final int COMPANION_STATUS_CHANGED__COMPANION_STATUS__DISCONNECTED = 5; + // Values for CompanionErrorReported.companion_error + public static final int COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_UNSPECIFIED = 0; + public static final int + COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_HANDSHAKE = 1; + public static final int COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_MSG = 2; + public static final int + COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_DEVICE_ID = 3; + public static final int + COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_VERIFICATION = 4; + public static final int + COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_CHANNEL_STATE = 5; + public static final int + COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_ENCRYPTION_KEY = 6; + public static final int COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_STORAGE_FAILURE = + 7; + public static final int + COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_SECURITY_KEY = 8; + public static final int + COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INSECURE_RECIPIENT_ID_DETECTED = 9; + public static final int + COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_UNEXPECTED_DISCONNECTION = 10; + // Annotation constants. public static final byte ANNOTATION_ID_IS_UID = StatsLog.ANNOTATION_ID_IS_UID; public static final byte ANNOTATION_ID_TRUNCATE_TIMESTAMP = @@ -43,10 +72,27 @@ public final class CompanionStatsLog { public static final byte ANNOTATION_ID_STATE_NESTED = StatsLog.ANNOTATION_ID_STATE_NESTED; // Write methods + public static void write(int code, int arg1, int arg2, boolean arg3) { + final StatsEvent.Builder builder = StatsEvent.newBuilder(); + builder.setAtomId(code); + builder.writeInt(arg1); + if (COMPANION_ERROR_REPORTED == code) { + builder.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true); + } + builder.writeInt(arg2); + builder.writeBoolean(arg3); + + builder.usePooledBuffer(); + StatsLog.write(builder.build()); + } + public static void write(int code, int arg1, int arg2, long arg3) { final StatsEvent.Builder builder = StatsEvent.newBuilder(); builder.setAtomId(code); builder.writeInt(arg1); + if (COMPANION_STATUS_CHANGED == code) { + builder.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true); + } builder.writeInt(arg2); builder.writeLong(arg3); diff --git a/libs/connecteddevice/src/com/google/android/connecteddevice/metrics/EventMetricLogger.kt b/libs/connecteddevice/src/com/google/android/connecteddevice/metrics/EventMetricLogger.kt index bd3b013..a7a7a54 100644 --- a/libs/connecteddevice/src/com/google/android/connecteddevice/metrics/EventMetricLogger.kt +++ b/libs/connecteddevice/src/com/google/android/connecteddevice/metrics/EventMetricLogger.kt @@ -1,19 +1,119 @@ package com.google.android.connecteddevice.metrics import android.content.Context -import android.os.Build +import com.google.android.connecteddevice.model.Errors +import android.os.RemoteException +import com.google.android.connecteddevice.util.SafeLog.loge +import java.time.Duration +import java.time.Instant /** Logger to report Companion events to statsd */ class EventMetricLogger(private val context: Context) { private val uid = context.packageManager.getPackageUid(context.packageName, 0) - fun onSecureChannelEstablished() { - // TODO(b/273968556): unable to find related System API on Android Q build. - if(Build.VERSION.SDK_INT < Build.VERSION_CODES.R ) return - CompanionStatsLog.write( - CompanionStatsLog.COMPANION_STATUS_CHANGED, - uid, + /** The time when association or reconnect discovery get started. */ + private var discoveryStartedTime = Instant.EPOCH + private var connectedTime = Instant.EPOCH + private val defaultDuration = Duration.ZERO + + /** Pushes the association discovery started event to statsd. */ + fun pushAssociationStartedEvent() { + pushEventsWithoutException( + CompanionStatsLog.COMPANION_STATUS_CHANGED__COMPANION_STATUS__ASSOCIATION_STARTED, + defaultDuration + ) + } + + /** Pushes the reconnection discovery started event to statsd. */ + fun pushDiscoveryStartedEvent() { + discoveryStartedTime = Instant.now() + pushEventsWithoutException( + CompanionStatsLog.COMPANION_STATUS_CHANGED__COMPANION_STATUS__DISCOVERY_STARTED, + defaultDuration + ) + } + + /** Pushes device get reconnected event to statsd. */ + fun pushConnectedEvent() { + connectedTime = Instant.now() + pushEventsWithoutException( + CompanionStatsLog.COMPANION_STATUS_CHANGED__COMPANION_STATUS__CONNECTED, + Duration.between(discoveryStartedTime, connectedTime) + ) + } + + /** Pushes the secure channel established event to statsd. */ + fun pushSecureChannelEstablishedEvent() { + pushEventsWithoutException( CompanionStatsLog.COMPANION_STATUS_CHANGED__COMPANION_STATUS__SECURE_CHANNEL_ESTABLISHED, - 0 + Duration.between(connectedTime, Instant.now()) ) } + + /** Pushes device disconnected event to statsd. */ + fun pushDisconnectedEvent() { + pushEventsWithoutException( + CompanionStatsLog.COMPANION_STATUS_CHANGED__COMPANION_STATUS__DISCONNECTED, + defaultDuration + ) + } + + // TODO(b/298248724): Reports more errors. + fun pushCompanionErrorEvent(error: Int, duringAssociation: Boolean = false) { + val errorId = + when (error) { + Errors.DEVICE_ERROR_INVALID_HANDSHAKE -> + CompanionStatsLog + .COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_HANDSHAKE + Errors.DEVICE_ERROR_INVALID_MSG -> + CompanionStatsLog.COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_MSG + Errors.DEVICE_ERROR_INVALID_DEVICE_ID -> + CompanionStatsLog + .COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_DEVICE_ID + Errors.DEVICE_ERROR_INVALID_VERIFICATION -> + CompanionStatsLog + .COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_VERIFICATION + Errors.DEVICE_ERROR_INVALID_CHANNEL_STATE -> + CompanionStatsLog + .COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_CHANNEL_STATE + Errors.DEVICE_ERROR_INVALID_ENCRYPTION_KEY -> + CompanionStatsLog + .COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_ENCRYPTION_KEY + Errors.DEVICE_ERROR_STORAGE_FAILURE -> + CompanionStatsLog.COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_STORAGE_FAILURE + Errors.DEVICE_ERROR_INVALID_SECURITY_KEY -> + CompanionStatsLog + .COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INVALID_SECURITY_KEY + Errors.DEVICE_ERROR_INSECURE_RECIPIENT_ID_DETECTED -> + CompanionStatsLog + .COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_INSECURE_RECIPIENT_ID_DETECTED + Errors.DEVICE_ERROR_UNEXPECTED_DISCONNECTION -> + CompanionStatsLog + .COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_UNEXPECTED_DISCONNECTION + else -> + CompanionStatsLog.COMPANION_ERROR_REPORTED__COMPANION_ERROR__DEVICE_ERROR_UNSPECIFIED + } + try { + CompanionStatsLog.write( + CompanionStatsLog.COMPANION_ERROR_REPORTED, + uid, + errorId, + duringAssociation + ) + } catch (e: NoClassDefFoundError) { + loge(TAG, "Encounter error when pushing Companion error events to statsd; skip.") + } + } + + private fun pushEventsWithoutException(eventId: Int, duration: Duration) { + try { + CompanionStatsLog.write( + CompanionStatsLog.COMPANION_STATUS_CHANGED, uid, eventId, duration.toMillis()) + } catch (e: NoClassDefFoundError) { + loge(TAG, "Encounter error when pushing Companion events to statsd; skip.") + } + } + + companion object { + private const val TAG = "EventMetricLogger" + } } diff --git a/libs/connecteddevice/src/com/google/android/connecteddevice/service/ConnectedDeviceService.java b/libs/connecteddevice/src/com/google/android/connecteddevice/service/ConnectedDeviceService.java index bd36f00..6dc294f 100644 --- a/libs/connecteddevice/src/com/google/android/connecteddevice/service/ConnectedDeviceService.java +++ b/libs/connecteddevice/src/com/google/android/connecteddevice/service/ConnectedDeviceService.java @@ -232,7 +232,8 @@ public final class ConnectedDeviceService extends TrunkService { return; } writer.printf( - "Companion SDK version is %s", getResources().getString(R.string.hu_companion_sdk_version)); + "Companion SDK version is %s\n", + getResources().getString(R.string.hu_companion_sdk_version)); } private void cleanup() { diff --git a/libs/connecteddevice/tests/unit/src/com/google/android/connecteddevice/api/FeatureConnectorTest.kt b/libs/connecteddevice/tests/unit/src/com/google/android/connecteddevice/api/FeatureConnectorTest.kt index 5486665..c691605 100644 --- a/libs/connecteddevice/tests/unit/src/com/google/android/connecteddevice/api/FeatureConnectorTest.kt +++ b/libs/connecteddevice/tests/unit/src/com/google/android/connecteddevice/api/FeatureConnectorTest.kt @@ -28,6 +28,7 @@ import com.google.common.truth.Truth.assertThat import com.google.protobuf.ExtensionRegistryLite import java.util.UUID import java.util.concurrent.ConcurrentHashMap +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any @@ -64,11 +65,23 @@ class FeatureConnectorTest { private val testCoordinatorProxy = spy(TestCompanionApiProxy(true, listOf(testDeviceId))) + private lateinit var versionZeroConnector: FeatureConnector + private lateinit var versionOneConnector: FeatureConnector + private lateinit var versionTwoConnector: FeatureConnector + + @Before + fun setUp() { + versionZeroConnector = FeatureConnector(context, testFeatureId, minSupportedVersion = 0) + versionOneConnector = FeatureConnector(context, testFeatureId, minSupportedVersion = 1) + versionTwoConnector = FeatureConnector(context, testFeatureId, minSupportedVersion = 2) + } + @Test - fun onInit_negativeVersions_aborts() { + fun onConnect_negativeVersions_aborts() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = -1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, -1) + val connector = FeatureConnector(context, testFeatureId, minSupportedVersion = -1) + connector.connect(mockCallback) assertThat(context.bindingActions) .containsExactly(ACTION_QUERY_API_VERSION, ACTION_BIND_FEATURE_COORDINATOR) @@ -76,10 +89,11 @@ class FeatureConnectorTest { } @Test - fun onInit_clientV1_platformV0_bindToFeatureCoordinator() { + fun onConnect_clientV1_platformV0_bindToFeatureCoordinator() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 0 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy is LegacyApiProxy).isTrue() assertThat(context.bindingActions) @@ -87,10 +101,11 @@ class FeatureConnectorTest { } @Test - fun onInit_clientV1_platformV1_bindToFeatureCoordinator() { + fun onConnect_clientV1_platformV1_bindToFeatureCoordinator() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy is SafeApiProxy).isTrue() assertThat(context.bindingActions) @@ -98,10 +113,11 @@ class FeatureConnectorTest { } @Test - fun onInit_clientV1_platformV2_bindToFeatureCoordinator() { + fun onConnect_clientV1_platformV2_bindToFeatureCoordinator() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 2 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy is SafeApiProxy).isTrue() assertThat(context.bindingActions) @@ -109,10 +125,11 @@ class FeatureConnectorTest { } @Test - fun onInit_clientV2_platformV0_bindToFeatureCoordinator() { + fun onConnect_clientV2_platformV0_bindToFeatureCoordinator() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 0 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 2) + val connector = versionTwoConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy is LegacyApiProxy).isTrue() assertThat(context.bindingActions) @@ -120,11 +137,12 @@ class FeatureConnectorTest { } @Test - fun onInit_clientV1_platformV1_incorrectServiceIssuesApiNotSupportedCallback() { + fun onConnect_clientV1_platformV1_incorrectServiceIssuesApiNotSupportedCallback() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 val incorrectServiceContext = IncorrectServiceContext(mockPackageManager) - val connector = FeatureConnector(incorrectServiceContext, testFeatureId, mockCallback, 1) + val connector = FeatureConnector(incorrectServiceContext, testFeatureId, 1) + connector.connect(mockCallback) assertThat(connector.coordinatorProxy).isNull() assertThat(connector.platformVersion).isNull() @@ -133,10 +151,11 @@ class FeatureConnectorTest { } @Test - fun onInit_clientV2_platformV1_issuesApiNotSupportedCallback() { + fun onConnect_clientV2_platformV1_issuesApiNotSupportedCallback() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 2) + val connector = versionTwoConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy).isNull() assertThat(connector.platformVersion).isNull() @@ -145,10 +164,11 @@ class FeatureConnectorTest { } @Test - fun onInit_clientV2_platformV2_bindToFeatureCoordinator() { + fun onConnect_clientV2_platformV2_bindToFeatureCoordinator() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 2 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 2) + val connector = versionTwoConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy is SafeApiProxy).isTrue() assertThat(context.bindingActions) @@ -156,10 +176,11 @@ class FeatureConnectorTest { } @Test - fun onInit_clientV2_platformV3_bindToFeatureCoordinator() { + fun onConnect_clientV2_platformV3_bindToFeatureCoordinator() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 3 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 2) + val connector = versionTwoConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy is SafeApiProxy).isTrue() assertThat(context.bindingActions) @@ -182,17 +203,19 @@ class FeatureConnectorTest { } setQueryIntentServicesAnswer(nullIntentAnswer) mockPlatformVersion = 0 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy).isNull() verify(mockCallback).onFailedToConnect() } @Test - fun onSuccessfulInit_doesNotRetryBind() { + fun onSuccessfulConnect_doesNotRetryBind() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) assertThat(connector.bindAttempts == 0).isTrue() assertThat(context.bindingActions) @@ -205,7 +228,8 @@ class FeatureConnectorTest { mockPlatformVersion = 1 val failingContext = FailingContext(mockPackageManager) val shadowLooper = Shadows.shadowOf(Looper.getMainLooper()) - val connector = FeatureConnector(failingContext, testFeatureId, mockCallback, 1) + val connector = FeatureConnector(failingContext, testFeatureId, 1) + connector.connect(mockCallback) repeat(FeatureConnector.MAX_BIND_ATTEMPTS) { shadowLooper.runToEndOfTasks() } @@ -220,7 +244,8 @@ class FeatureConnectorTest { mockPlatformVersion = 0 val flakyContext = FailingLegacyContext(mockPackageManager) val shadowLooper = Shadows.shadowOf(Looper.getMainLooper()) - val connector = FeatureConnector(flakyContext, testFeatureId, mockCallback, 1) + val connector = FeatureConnector(flakyContext, testFeatureId, 1) + connector.connect(mockCallback) repeat(FeatureConnector.MAX_BIND_ATTEMPTS) { shadowLooper.runToEndOfTasks() } @@ -237,7 +262,8 @@ class FeatureConnectorTest { mockPlatformVersion = 0 val flakyContext = FlakyLegacyContext(mockPackageManager) val shadowLooper = Shadows.shadowOf(Looper.getMainLooper()) - val connector = FeatureConnector(flakyContext, testFeatureId, mockCallback, 1) + val connector = FeatureConnector(flakyContext, testFeatureId, 1) + connector.connect(mockCallback) // Should repeat once for version query bind attempt, then reset, then MAX_BIND_ATTEMPTS times // for feature coordinator bind attempt. @@ -261,7 +287,8 @@ class FeatureConnectorTest { fun onDisconnected_invokedOnVersionCheckServiceDisconnected() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.versionCheckConnection.onServiceDisconnected( ComponentName(PACKAGE_NAME, VERSION_NAME) @@ -273,7 +300,8 @@ class FeatureConnectorTest { fun onDisconnected_invokedOnVersionCheckDeadBinding() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.versionCheckConnection.onBindingDied(ComponentName(PACKAGE_NAME, VERSION_NAME)) verify(mockCallback).onDisconnected() @@ -283,7 +311,8 @@ class FeatureConnectorTest { fun onDisconnected_invokedOnFeatureCoordinatorServiceDisconnected() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.featureCoordinatorConnection.onServiceDisconnected( ComponentName(PACKAGE_NAME, FC_NAME) @@ -295,7 +324,8 @@ class FeatureConnectorTest { fun onFailedToConnect_invokedOnFeatureCoordinatorNullBinding() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.featureCoordinatorConnection.onNullBinding(ComponentName(PACKAGE_NAME, FC_NAME)) verify(mockCallback).onFailedToConnect() @@ -305,7 +335,8 @@ class FeatureConnectorTest { fun onDisconnected_invokedOnFeatureCoordinatorDeadBinding() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.featureCoordinatorConnection.onBindingDied(ComponentName(PACKAGE_NAME, FC_NAME)) verify(mockCallback).onDisconnected() @@ -315,7 +346,8 @@ class FeatureConnectorTest { fun onConnected_invokedAfterSuccessfulBind() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy is SafeApiProxy).isTrue() verify(mockCallback).onConnected() @@ -325,7 +357,8 @@ class FeatureConnectorTest { fun onConnected_invokedAfterSuccessfulLegacyBind() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 0 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy is LegacyApiProxy).isTrue() verify(mockCallback).onConnected() @@ -335,7 +368,8 @@ class FeatureConnectorTest { fun sendMessage_sendsMessage() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = testCoordinatorProxy connector.sendMessage(testDeviceId, testMessage) @@ -346,7 +380,8 @@ class FeatureConnectorTest { fun onMessageFailedToSend_invokedOnNullProxy() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = null connector.sendMessage(testDeviceId, testMessage) @@ -357,7 +392,8 @@ class FeatureConnectorTest { fun onMessageFailedToSend_invokedWhenDeviceNotFound() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = testCoordinatorProxy val unexpectedDevice = UUID.randomUUID().toString() connector.sendMessage(unexpectedDevice, testMessage) @@ -371,7 +407,8 @@ class FeatureConnectorTest { fun onMessageFailedToSend_invokedWhenSendMessageFails() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) val coordinatorProxy = TestCompanionApiProxy(false, listOf(testDeviceId)) connector.coordinatorProxy = coordinatorProxy connector.sendMessage(testDeviceId, testMessage) @@ -383,7 +420,8 @@ class FeatureConnectorTest { fun sendMessage_onMessageFailedToSend_invokedOnVersionMismatch() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 0 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.sendMessage(testDeviceId, testMessage) verify(mockCallback).onMessageFailedToSend(testDeviceId, testMessage, isTransient = false) @@ -393,7 +431,8 @@ class FeatureConnectorTest { fun sendQuery_sendsQueryToOwnFeatureId() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = testCoordinatorProxy val request = ByteUtils.randomBytes(10) val parameters = ByteUtils.randomBytes(10) @@ -412,7 +451,8 @@ class FeatureConnectorTest { fun sendQuery_addsCallbackToCallbacksMap() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = testCoordinatorProxy val request = ByteUtils.randomBytes(10) val parameters = ByteUtils.randomBytes(10) @@ -426,7 +466,8 @@ class FeatureConnectorTest { fun sendQuery_onQueryFailedToSend_invokedOnNullProxy() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = null val request = ByteUtils.randomBytes(10) val parameters = ByteUtils.randomBytes(10) @@ -440,7 +481,8 @@ class FeatureConnectorTest { fun sendQuery_onQueryFailedToSend_invokedOnDeviceIdNotFound() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = testCoordinatorProxy val request = ByteUtils.randomBytes(10) val parameters = ByteUtils.randomBytes(10) @@ -454,7 +496,8 @@ class FeatureConnectorTest { fun respondToQuery_doesNotSendResponseWithNullProxy() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = null val response = ByteUtils.randomBytes(10) connector.respondToQuery(testDeviceId, 1, success = true, response) @@ -466,7 +509,8 @@ class FeatureConnectorTest { fun respondToQuery_doesNotSendResponseWithUnrecognizedQueryId() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = testCoordinatorProxy val nonExistentQueryId = 0 val response = ByteUtils.randomBytes(10) @@ -481,7 +525,8 @@ class FeatureConnectorTest { mockPlatformVersion = 1 val queryId = 1 val response = ByteUtils.randomBytes(10) - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = testCoordinatorProxy testCoordinatorProxy.queryResponseRecipients.put(queryId, testFeatureId) connector.respondToQuery(testDeviceId, queryId, success = true, response) @@ -499,7 +544,8 @@ class FeatureConnectorTest { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 val queryId = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = testCoordinatorProxy testCoordinatorProxy.queryResponseRecipients.put(queryId, testFeatureId) connector.respondToQuery(testDeviceId, queryId, success = true, null) @@ -516,7 +562,8 @@ class FeatureConnectorTest { fun respondToQuery_onMessageFailedToSend_invokedWhenSendMessageFailed() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) val proxy = spy(TestCompanionApiProxy(false, listOf(testDeviceId))) connector.coordinatorProxy = proxy val queryId = 1 @@ -531,7 +578,8 @@ class FeatureConnectorTest { fun retrieveCompanionApplicationName_sendsAppNameQueryToSystemFeature() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) connector.coordinatorProxy = testCoordinatorProxy val callback = mock<SafeConnector.AppNameCallback>() @@ -551,7 +599,8 @@ class FeatureConnectorTest { fun retrieveCompanionApplicationName_onMessageFailedToSend_invokedWhenSendMessageFailed() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) val proxy = spy(TestCompanionApiProxy(false, listOf(testDeviceId))) connector.coordinatorProxy = proxy val appNameCallback = mock<SafeConnector.AppNameCallback>() @@ -564,7 +613,8 @@ class FeatureConnectorTest { fun retrieveAssociatedDevices_worksWithSafeListener() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) val mockListener = mock<ISafeOnAssociatedDevicesRetrievedListener>() connector.coordinatorProxy = testCoordinatorProxy @@ -577,7 +627,8 @@ class FeatureConnectorTest { fun retrieveAssociatedDevices_worksWithLegacyListener() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 0 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) val mockListener = mock<IOnAssociatedDevicesRetrievedListener>() connector.coordinatorProxy = testCoordinatorProxy @@ -590,7 +641,8 @@ class FeatureConnectorTest { fun retrieveAssociatedDevices_returnsSilentlyOnNullCoordinator() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) val mockListener = mock<ISafeOnAssociatedDevicesRetrievedListener>() connector.coordinatorProxy = null @@ -600,65 +652,71 @@ class FeatureConnectorTest { } @Test - fun onCleanUp_cleansUpFeatureCoordinator() { + fun disconnect_cleansUpFeatureCoordinator() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy).isNotNull() - connector.cleanUp() + connector.disconnect() assertThat(connector.coordinatorProxy).isNull() } @Test - fun onCleanUp_cleansUpFeatureCoordinator_legacyPlatform() { + fun disconnect_cleansUpFeatureCoordinator_legacyPlatform() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 0 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) assertThat(connector.coordinatorProxy).isNotNull() - connector.cleanUp() + connector.disconnect() assertThat(connector.coordinatorProxy).isNull() } @Test - fun onCleanUp_callsCallbackDisconnected() { + fun disconnect_callsCallbackDisconnected() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) - connector.cleanUp() + connector.disconnect() verify(mockCallback).onDisconnected() } @Test - fun onCleanUp_callsCallbackDisconnected_legacyPlatform() { + fun disconnect_callsCallbackDisconnected_legacyPlatform() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 0 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) - connector.cleanUp() + connector.disconnect() verify(mockCallback).onDisconnected() } @Test - fun onCleanUp_unbindsFromContext() { + fun disconnect_unbindsFromContext() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 1 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) - connector.cleanUp() + connector.disconnect() assertThat(context.unbindServiceConnection) .containsExactly(connector.featureCoordinatorConnection) } @Test - fun onCleanUp_unbindsFromContext_legacyPlatform() { + fun disconnect_unbindsFromContext_legacyPlatform() { setQueryIntentServicesAnswer(defaultServiceAnswer) mockPlatformVersion = 0 - val connector = FeatureConnector(context, testFeatureId, mockCallback, 1) + val connector = versionOneConnector + connector.connect(mockCallback) - connector.cleanUp() + connector.disconnect() assertThat(context.unbindServiceConnection) .containsExactly(connector.featureCoordinatorConnection) } |