diff options
author | Android Auto Companion <no-reply@google.com> | 2022-04-27 10:01:08 -0700 |
---|---|---|
committer | Erin Yan <yiranyan@google.com> | 2022-05-12 18:49:36 +0000 |
commit | e1858e69ac1a245dc9d15edfacabb634f00b84b7 (patch) | |
tree | 5d0a3ba59bfc32ba2ee21f07b6abc7a2f54bed0f | |
parent | 6e80f2f7f920a8aa2c0d3e2372d54b005bf5d2d1 (diff) | |
download | CompanionDeviceSupport-e1858e69ac1a245dc9d15edfacabb634f00b84b7.tar.gz |
Project import generated by Copybara
Included changes:
Release-Id: aae-companiondevice-android_20220427.00_RC00
Change-Id: Ifced9528c51d5d1bdfb310d65dff41c0053a728d
8 files changed, 156 insertions, 12 deletions
diff --git a/companiondevice/build.gradle b/companiondevice/build.gradle index c12b2f5..6dd8f99 100644 --- a/companiondevice/build.gradle +++ b/companiondevice/build.gradle @@ -14,7 +14,7 @@ android { applicationId "com.google.android.companiondevicesupport" minSdkVersion 29 targetSdkVersion 31 - versionCode 1441 + versionCode 1477 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/companiondevice/res/values/dimens.xml b/companiondevice/res/values/dimens.xml index c5f9a79..f98b9a3 100644 --- a/companiondevice/res/values/dimens.xml +++ b/companiondevice/res/values/dimens.xml @@ -44,7 +44,7 @@ <!-- Common --> <dimen name="divider_height">1dp</dimen> - <dimen name="qr_code_size">200dp</dimen> + <dimen name="qr_code_size">300dp</dimen> <dimen name="instruction_margin">8dp</dimen> <dimen name="connection_indicator_size">12dp</dimen> <dimen name="connection_indicator_margin_end">12dp</dimen> diff --git a/companiondevice/src/com/google/android/companiondevicesupport/AssociationActivity.java b/companiondevice/src/com/google/android/companiondevicesupport/AssociationActivity.java index 266648a..9ead484 100644 --- a/companiondevice/src/com/google/android/companiondevicesupport/AssociationActivity.java +++ b/companiondevice/src/com/google/android/companiondevicesupport/AssociationActivity.java @@ -30,9 +30,11 @@ import android.bluetooth.BluetoothAdapter; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; +import android.os.Build; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.os.StrictMode; import android.os.UserManager; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; @@ -101,6 +103,8 @@ public class AssociationActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { + // TODO(b/228328725): Remove strict mode change when the violation is resolved. + maybeEnableStrictMode(); resolveIntent(); // Set theme before calling super.onCreate(bundle) to avoid recreating activity. @@ -721,6 +725,14 @@ public class AssociationActivity extends FragmentActivity { return RemoteFeature.ACTION_ASSOCIATION_SETTING.equals(action); } + private static void maybeEnableStrictMode() { + if (!Build.TYPE.equals("user")) { + StrictMode.setThreadPolicy( + new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().penaltyDialog().build()); + StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build()); + } + } + /** Dialog fragment to turn on bluetooth. */ public static class TurnOnBluetoothDialogFragment extends DialogFragment { @Override diff --git a/libs/connecteddevice/res/values/config.xml b/libs/connecteddevice/res/values/config.xml index 281973d..4feb25c 100644 --- a/libs/connecteddevice/res/values/config.xml +++ b/libs/connecteddevice/res/values/config.xml @@ -17,5 +17,5 @@ <resources> <!-- Current version of the SDK --> - <string name="hu_companion_sdk_version" translatable="false">1.3.1</string> + <string name="hu_companion_sdk_version" translatable="false">1.3.2</string> </resources> diff --git a/libs/connecteddevice/src/com/google/android/connecteddevice/api/CompanionConnector.kt b/libs/connecteddevice/src/com/google/android/connecteddevice/api/CompanionConnector.kt index fec1907..69c9e41 100644 --- a/libs/connecteddevice/src/com/google/android/connecteddevice/api/CompanionConnector.kt +++ b/libs/connecteddevice/src/com/google/android/connecteddevice/api/CompanionConnector.kt @@ -77,6 +77,8 @@ constructor( private val isPlatformInitialized = AtomicBoolean(false) + private val waitingForConnection = AtomicBoolean(false) + private val featureCoordinatorAction = if (isForegroundProcess) { ACTION_BIND_FEATURE_COORDINATOR_FG @@ -92,6 +94,7 @@ constructor( IFeatureCoordinator.Stub.asInterface(service) ?: throw IllegalStateException("Cannot create wrapper of a null feature coordinator.") logd(TAG, "Feature coordinator is alive: ${featureCoordinator?.asBinder()?.isBinderAlive}") + waitingForConnection.set(false) this@CompanionConnector.onServiceConnected() } @@ -100,9 +103,12 @@ constructor( } override fun onNullBinding(name: ComponentName) { - loge(TAG, "Received a null binding for FeatureCoordinator.") this@CompanionConnector.onNullBinding() } + + override fun onBindingDied(name: ComponentName?) { + this@CompanionConnector.onBindingDied() + } } private val connectionCallback = @@ -231,7 +237,13 @@ constructor( initializePlatform() return } + if (waitingForConnection.compareAndSet(false, true)) { + bindAttempts = 0 + bindToService() + } + } + private fun bindToService() { logd(TAG, "Platform is not currently connected. Initiating binding.") val intent = resolveIntent(featureCoordinatorAction) @@ -250,17 +262,25 @@ constructor( bindAttempts++ if (bindAttempts > MAX_BIND_ATTEMPTS) { loge(TAG, "Failed to bind to service after $bindAttempts attempts. Aborting.") + waitingForConnection.set(false) callback?.onFailedToConnect() return } logw(TAG, "Unable to bind to service with action ${intent.action}. Trying again.") - retryHandler.postDelayed(::connect, BIND_RETRY_DURATION.toMillis()) + retryHandler.postDelayed(::bindToService, BIND_RETRY_DURATION.toMillis()) } override fun disconnect() { logd(TAG, "Disconnecting from the companion platform.") val wasConnected = isBound logd(TAG, "FeatureCoordinator is null: ${featureCoordinator == null} isBound: $isBound") + unbindFromService() + if (wasConnected) { + callback?.onDisconnected() + } + } + + private fun unbindFromService() { cleanUpFeatureCoordinator() retryHandler.removeCallbacksAndMessages(/* token= */ null) try { @@ -268,10 +288,7 @@ constructor( } catch (e: IllegalArgumentException) { logw(TAG, "Attempted to unbind an already unbound service.") } - bindAttempts = 0 - if (wasConnected) { - callback?.onDisconnected() - } + waitingForConnection.set(false) } private fun cleanUpFeatureCoordinator() { @@ -301,10 +318,14 @@ constructor( } override fun binderForAction(action: String): IBinder? { + logd(TAG, "Binder for action: $action.") return when (action) { ACTION_BIND_FEATURE_COORDINATOR, ACTION_BIND_FEATURE_COORDINATOR_FG -> featureCoordinator?.asBinder() - else -> null + else -> { + loge(TAG, "Binder for unexpected action, returning null binder.") + null + } } } @@ -634,10 +655,17 @@ constructor( } private fun onNullBinding() { - logd(TAG, "Issuing onFailedToConnect callback after null binding.") + loge(TAG, "Received a null binding for FeatureCoordinator. Unbinding service.") + unbindFromService() callback?.onFailedToConnect() } + private fun onBindingDied() { + logw(TAG, "FeatureCoordinator binding died. Unbinding service.") + unbindFromService() + callback?.onDisconnected() + } + private fun resolveIntent(action: String): Intent? { val packageManager = context.packageManager val intent = Intent(action) diff --git a/libs/connecteddevice/src/com/google/android/connecteddevice/notificationmsg/common/BaseNotificationDelegate.java b/libs/connecteddevice/src/com/google/android/connecteddevice/notificationmsg/common/BaseNotificationDelegate.java index 8196328..9b4e787 100644 --- a/libs/connecteddevice/src/com/google/android/connecteddevice/notificationmsg/common/BaseNotificationDelegate.java +++ b/libs/connecteddevice/src/com/google/android/connecteddevice/notificationmsg/common/BaseNotificationDelegate.java @@ -85,6 +85,10 @@ public class BaseNotificationDelegate { public static final String EXTRA_REMOTE_INPUT_KEY = "com.android.car.messenger.common.REMOTE_INPUT_KEY"; + /** Used to override default notification large icon. **/ + private static final String EXTRA_USE_LAUNCHER_ICON = + "com.android.car.notification.EXTRA_USE_LAUNCHER_ICON"; + private static final String REPLY = "Reply"; private static final String MARK_AS_READ = "Mark As Read"; @@ -234,6 +238,9 @@ public class BaseNotificationDelegate { notificationInfo.messageKeys.size(), notificationInfo.messageKeys.size())); + Bundle avatarBundle = new Bundle(); + avatarBundle.putBoolean(EXTRA_USE_LAUNCHER_ICON, false); + builder.addExtras(avatarBundle); if (avatarIcon != null) { builder.setLargeIcon(avatarIcon); } else if (useLetterTile) { diff --git a/libs/connecteddevice/src/com/google/android/connecteddevice/service/ConnectedDeviceFgUserService.kt b/libs/connecteddevice/src/com/google/android/connecteddevice/service/ConnectedDeviceFgUserService.kt index 5003a4f..114bc9c 100644 --- a/libs/connecteddevice/src/com/google/android/connecteddevice/service/ConnectedDeviceFgUserService.kt +++ b/libs/connecteddevice/src/com/google/android/connecteddevice/service/ConnectedDeviceFgUserService.kt @@ -89,8 +89,8 @@ class ConnectedDeviceFgUserService : TrunkService() { } override fun onBind(intent: Intent): IBinder? { + logd(TAG, "Service bound. Action: ${intent.action}") val action = intent.action ?: return null - logd(TAG, "Service bound. Action: $action") return connector.binderForAction(action) } diff --git a/libs/connecteddevice/tests/unit/src/com/google/android/connecteddevice/api/CompanionConnectorTest.kt b/libs/connecteddevice/tests/unit/src/com/google/android/connecteddevice/api/CompanionConnectorTest.kt index b09c7d4..040ea11 100644 --- a/libs/connecteddevice/tests/unit/src/com/google/android/connecteddevice/api/CompanionConnectorTest.kt +++ b/libs/connecteddevice/tests/unit/src/com/google/android/connecteddevice/api/CompanionConnectorTest.kt @@ -88,6 +88,17 @@ class CompanionConnectorTest { } @Test + fun connect_bindOnceOnlyForMultipleCalls() { + setQueryIntentServicesAnswer(defaultServiceAnswer) + val connector = CompanionConnector(context, isForegroundProcess = true) + + connector.connect() + connector.connect() + + assertThat(context.bindingActions).containsExactly(ACTION_BIND_FEATURE_COORDINATOR_FG) + } + + @Test fun connect_fgRetriesBindWithRemoteFeatureActionIfFeatureCoordinatorReturnsNullBinding() { setQueryIntentServicesAnswer(defaultServiceAnswer) val connector = CompanionConnector(context, isForegroundProcess = true) @@ -110,6 +121,54 @@ class CompanionConnectorTest { } @Test + fun connect_canConnectAfterServiceDisconnect() { + setQueryIntentServicesAnswer(defaultServiceAnswer) + val connector = CompanionConnector(context, isForegroundProcess = true) + connector.connect() + val connection = context.serviceConnection.firstOrNull() + val component = ComponentName(PACKAGE_NAME, BG_NAME) + connection?.onServiceConnected(component, mockFeatureCoordinator.asBinder()) + connection?.onServiceDisconnected(component) + + connector.connect() + + assertThat(context.bindingActions) + .containsExactly(ACTION_BIND_FEATURE_COORDINATOR_FG, ACTION_BIND_FEATURE_COORDINATOR_FG) + } + + @Test + fun connect_canConnectAfterBindingDied() { + setQueryIntentServicesAnswer(defaultServiceAnswer) + val connector = CompanionConnector(context, isForegroundProcess = true) + connector.connect() + val connection = context.serviceConnection.firstOrNull() + val component = ComponentName(PACKAGE_NAME, BG_NAME) + connection?.onServiceConnected(component, mockFeatureCoordinator.asBinder()) + connection?.onBindingDied(component) + + connector.connect() + + assertThat(context.bindingActions) + .containsExactly(ACTION_BIND_FEATURE_COORDINATOR_FG, ACTION_BIND_FEATURE_COORDINATOR_FG) + } + + @Test + fun connect_canConnectAfterNullBinding() { + setQueryIntentServicesAnswer(defaultServiceAnswer) + val connector = CompanionConnector(context, isForegroundProcess = true) + connector.connect() + val connection = context.serviceConnection.firstOrNull() + val component = ComponentName(PACKAGE_NAME, BG_NAME) + connection?.onServiceConnected(component, mockFeatureCoordinator.asBinder()) + connection?.onNullBinding(component) + + connector.connect() + + assertThat(context.bindingActions) + .containsExactly(ACTION_BIND_FEATURE_COORDINATOR_FG, ACTION_BIND_FEATURE_COORDINATOR_FG) + } + + @Test fun disconnect_invokesOnDisconnectedWhenFeatureCoordinatorConnected() { setQueryIntentServicesAnswer(defaultServiceAnswer) val connector = @@ -181,6 +240,18 @@ class CompanionConnectorTest { } @Test + fun onDisconnected_invokedWhenServiceBindingDied() { + setQueryIntentServicesAnswer(defaultServiceAnswer) + val connector = + CompanionConnector(context, isForegroundProcess = false).apply { callback = mockCallback } + + connector.connect() + context.serviceConnection.firstOrNull()?.onBindingDied(ComponentName(PACKAGE_NAME, BG_NAME)) + + verify(mockCallback).onDisconnected() + } + + @Test fun onFailedToConnect_invokedWhenServiceIsNotFound() { setQueryIntentServicesAnswer(defaultServiceAnswer) val connector = @@ -221,6 +292,32 @@ class CompanionConnectorTest { } @Test + fun unbindService_invokedWhenServiceIsNotFound() { + setQueryIntentServicesAnswer(defaultServiceAnswer) + val connector = + CompanionConnector(context, isForegroundProcess = false).apply { callback = mockCallback } + + connector.connect() + val connection = context.serviceConnection.first() + connection.onNullBinding(ComponentName(PACKAGE_NAME, FG_NAME)) + + assertThat(context.unbindServiceConnection).contains(connection) + } + + @Test + fun unbindService_invokedWhenServiceBindingDied() { + setQueryIntentServicesAnswer(defaultServiceAnswer) + val connector = + CompanionConnector(context, isForegroundProcess = false).apply { callback = mockCallback } + + connector.connect() + val connection = context.serviceConnection.first() + connection.onBindingDied(ComponentName(PACKAGE_NAME, FG_NAME)) + + assertThat(context.unbindServiceConnection).contains(connection) + } + + @Test fun featureCoordinator_isNotNullAfterServiceConnection() { setQueryIntentServicesAnswer(defaultServiceAnswer) val connector = CompanionConnector(context, isForegroundProcess = false) |