[CarTelemetryService] Add hello world kitchensink test
Bug: 200092147
Test: atest CarServiceUnitTest:DataBrokerTest
Test: manual testing
1. Flash seahawk
2. Open kitchen sink app
3. Click on Send MetricsConfig onGearChange
4. Verify status code is 0 (success)
5. Inject vehicle property event adb shell cmd car_service inject-vhal-event 0x11400400 2
6. Click on Get onGearChange Report
7. Wait a few moments and verify output is "Hello World!"
Change-Id: Ia599c19dbb927a6a5aed0c0c221e9648d6ae4c66
diff --git a/car-lib/src/android/car/telemetry/MetricsConfigKey.java b/car-lib/src/android/car/telemetry/MetricsConfigKey.java
index 6e4614b..ddc9718 100644
--- a/car-lib/src/android/car/telemetry/MetricsConfigKey.java
+++ b/car-lib/src/android/car/telemetry/MetricsConfigKey.java
@@ -20,6 +20,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* A parcelable that wraps around the Manifest name and version.
*
@@ -61,6 +63,20 @@
return 0;
}
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof MetricsConfigKey)) {
+ return false;
+ }
+ MetricsConfigKey other = (MetricsConfigKey) o;
+ return mName.equals(other.getName()) && mVersion == other.getVersion();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mVersion);
+ }
+
public static final @NonNull Parcelable.Creator<MetricsConfigKey> CREATOR =
new Parcelable.Creator<MetricsConfigKey>() {
@Override
diff --git a/packages/ScriptExecutor/src/ScriptExecutorJni.cpp b/packages/ScriptExecutor/src/ScriptExecutorJni.cpp
index 6cb0670..c11a30b 100644
--- a/packages/ScriptExecutor/src/ScriptExecutorJni.cpp
+++ b/packages/ScriptExecutor/src/ScriptExecutorJni.cpp
@@ -111,9 +111,7 @@
// TODO(b/189241508): Provide implementation to parse publishedData input,
// convert it into Lua table and push into Lua stack.
if (publishedData) {
- env->ThrowNew(env->FindClass("java/lang/RuntimeException"),
- "Parsing of publishedData is not implemented yet.");
- return;
+ LOG(WARNING) << "Parsing of publishedData is not implemented yet.";
}
// Unpack bundle in savedState, convert to Lua table and push it to Lua
diff --git a/service/src/com/android/car/telemetry/MetricsConfigStore.java b/service/src/com/android/car/telemetry/MetricsConfigStore.java
index 7a5512a..fac0b2e 100644
--- a/service/src/com/android/car/telemetry/MetricsConfigStore.java
+++ b/service/src/com/android/car/telemetry/MetricsConfigStore.java
@@ -106,6 +106,7 @@
/** Deletes the MetricsConfig from disk. Returns the success status. */
boolean deleteMetricsConfig(String metricsConfigName) {
mActiveConfigs.remove(metricsConfigName);
+ mNameVersionMap.remove(metricsConfigName);
return new File(mConfigDirectory, metricsConfigName).delete();
}
diff --git a/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java b/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java
index f1fbd24..f4b3e6a 100644
--- a/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java
+++ b/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java
@@ -59,6 +59,9 @@
private static final int MSG_HANDLE_TASK = 1;
private static final int MSG_BIND_TO_SCRIPT_EXECUTOR = 2;
+ /** Bind to script executor 5 times before entering disabled state. */
+ private static final int MAX_BIND_SCRIPT_EXECUTOR_ATTEMPTS = 5;
+
private static final String SCRIPT_EXECUTOR_PACKAGE = "com.android.car.scriptexecutor";
private static final String SCRIPT_EXECUTOR_CLASS =
"com.android.car.scriptexecutor.ScriptExecutor";
@@ -87,9 +90,15 @@
*/
private boolean mDisabled = false;
+ /** Current number of attempts to bind to ScriptExecutor. */
+ private int mBindScriptExecutorAttempts = 0;
+
/** Priority of current system to determine if a {@link ScriptExecutionTask} can run. */
private int mPriority = 1;
+ /** Waiting period between attempts to bind script executor. Can be shortened for tests. */
+ @VisibleForTesting long mBindScriptExecutorDelayMillis = 3_000L;
+
/**
* Name of the script that's currently running. If no script is running, value is null.
* A non-null script name indicates a script is running, which means DataBroker should not
@@ -144,9 +153,21 @@
mServiceConnection,
Context.BIND_AUTO_CREATE,
UserHandle.SYSTEM);
- if (!success) {
- Slog.w(CarLog.TAG_TELEMETRY, "failed to get valid connection to ScriptExecutor");
- unbindScriptExecutor();
+ if (success) {
+ mBindScriptExecutorAttempts = 0; // reset
+ return;
+ }
+ unbindScriptExecutor();
+ mBindScriptExecutorAttempts++;
+ if (mBindScriptExecutorAttempts < MAX_BIND_SCRIPT_EXECUTOR_ATTEMPTS) {
+ Slog.w(CarLog.TAG_TELEMETRY,
+ "failed to get valid connection to ScriptExecutor, retrying in "
+ + mBindScriptExecutorDelayMillis + "ms.");
+ mTelemetryHandler.sendEmptyMessageDelayed(MSG_BIND_TO_SCRIPT_EXECUTOR,
+ mBindScriptExecutorDelayMillis);
+ } else {
+ Slog.w(CarLog.TAG_TELEMETRY, "failed to get valid connection to ScriptExecutor, "
+ + "disabling DataBroker");
disableBroker();
}
}
@@ -166,7 +187,10 @@
}
}
- /** Enters into a disabled state because something irrecoverable happened. */
+ /**
+ * Enters into a disabled state because something irrecoverable happened.
+ * TODO(b/200841260): expose the state to the caller.
+ */
private void disableBroker() {
mDisabled = true;
// remove all MetricConfigs, disable all publishers, stop receiving data
@@ -323,6 +347,7 @@
// upon successful binding, a task will be scheduled to run if there are any
mTelemetryHandler.sendEmptyMessage(MSG_BIND_TO_SCRIPT_EXECUTOR);
} else {
+ Slog.d(CarLog.TAG_TELEMETRY, "invoking script executor");
// update current name because a script is currently running
mCurrentScriptName = task.getMetricsConfig().getName();
mScriptExecutor.invokeScript(
diff --git a/tests/EmbeddedKitchenSinkApp/Android.bp b/tests/EmbeddedKitchenSinkApp/Android.bp
index ef70cc9..3226c9e 100644
--- a/tests/EmbeddedKitchenSinkApp/Android.bp
+++ b/tests/EmbeddedKitchenSinkApp/Android.bp
@@ -52,6 +52,7 @@
"com.android.car.keventreader-client",
"guava",
"android.car.cluster.navigation",
+ "cartelemetry-protos",
"car-experimental-api-static-lib",
],
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/car_telemetry_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/car_telemetry_test.xml
new file mode 100644
index 0000000..2576b7d
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/car_telemetry_test.xml
@@ -0,0 +1,47 @@
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <LinearLayout
+ android:id="@+id/on_gear_change_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/send_on_gear_change_config"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/send_on_gear_change"/>
+ <Button
+ android:id="@+id/remove_on_gear_change_config"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/remove_on_gear_change"/>
+ <Button
+ android:id="@+id/get_on_gear_change_report"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/get_on_gear_change"/>
+ </LinearLayout>
+ <TextView
+ android:id="@+id/output_textview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"/>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index 2fb2de5..d43e715 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -381,4 +381,9 @@
<!-- Fullscreen Activity Test -->
<string name="nav_to_full_screen" translatable="false">Navigate to Full Screen</string>
<string name="cancel" translatable="false">Cancel</string>
+
+ <!-- CarTelemetryService Test -->
+ <string name="send_on_gear_change" translatable="false">Send MetricsConfig on_gear_change</string>
+ <string name="remove_on_gear_change" translatable="false">Remove MetricsConfig on_gear_change</string>
+ <string name="get_on_gear_change" translatable="false">Get on_gear_change Report</string>
</resources>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index beeac2a..57ea5f6 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -23,6 +23,7 @@
import android.car.hardware.hvac.CarHvacManager;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.property.CarPropertyManager;
+import android.car.telemetry.CarTelemetryManager;
import android.car.watchdog.CarWatchdogManager;
import android.content.Context;
import android.content.Intent;
@@ -72,6 +73,7 @@
import com.google.android.car.kitchensink.storagevolumes.StorageVolumesFragment;
import com.google.android.car.kitchensink.systembars.SystemBarsFragment;
import com.google.android.car.kitchensink.systemfeatures.SystemFeaturesFragment;
+import com.google.android.car.kitchensink.telemetry.CarTelemetryTestFragment;
import com.google.android.car.kitchensink.touch.TouchTestFragment;
import com.google.android.car.kitchensink.users.ProfileUserFragment;
import com.google.android.car.kitchensink.users.UserFragment;
@@ -205,6 +207,7 @@
new FragmentMenuEntry("storage volumes", StorageVolumesFragment.class),
new FragmentMenuEntry("system bars", SystemBarsFragment.class),
new FragmentMenuEntry("system features", SystemFeaturesFragment.class),
+ new FragmentMenuEntry("telemetry", CarTelemetryTestFragment.class),
new FragmentMenuEntry("touch test", TouchTestFragment.class),
new FragmentMenuEntry("users", UserFragment.class),
new FragmentMenuEntry("user restrictions", UserRestrictionsFragment.class),
@@ -223,6 +226,7 @@
private CarSensorManager mSensorManager;
private CarAppFocusManager mCarAppFocusManager;
private CarProjectionManager mCarProjectionManager;
+ private CarTelemetryManager mCarTelemetryManager;
private CarWatchdogManager mCarWatchdogManager;
private Object mPropertyManagerReady = new Object();
@@ -250,6 +254,10 @@
return mCarProjectionManager;
}
+ public CarTelemetryManager getCarTelemetryManager() {
+ return mCarTelemetryManager;
+ }
+
public CarWatchdogManager getCarWatchdogManager() {
return mCarWatchdogManager;
}
@@ -411,6 +419,8 @@
(CarAppFocusManager) car.getCarManager(Car.APP_FOCUS_SERVICE);
mCarProjectionManager =
(CarProjectionManager) car.getCarManager(Car.PROJECTION_SERVICE);
+ mCarTelemetryManager =
+ (CarTelemetryManager) car.getCarManager(Car.CAR_TELEMETRY_SERVICE);
mCarWatchdogManager =
(CarWatchdogManager) car.getCarManager(Car.CAR_WATCHDOG_SERVICE);
mPropertyManagerReady.notifyAll();
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/telemetry/CarTelemetryTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/telemetry/CarTelemetryTestFragment.java
new file mode 100644
index 0000000..7c4a4b1
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/telemetry/CarTelemetryTestFragment.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2021 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.google.android.car.kitchensink.telemetry;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.car.telemetry.CarTelemetryManager;
+import android.car.telemetry.MetricsConfigKey;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.fragment.app.Fragment;
+
+import com.android.car.telemetry.TelemetryProto;
+
+import com.google.android.car.kitchensink.KitchenSinkActivity;
+import com.google.android.car.kitchensink.R;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class CarTelemetryTestFragment extends Fragment {
+ private static final String LUA_SCRIPT_ON_GEAR_CHANGE =
+ "function onGearChange(state)\n"
+ + " result = {data = \"Hello World!\"}\n"
+ + " on_script_finished(result)\n"
+ + "end\n";
+ private static final TelemetryProto.Publisher VEHICLE_PROPERTY_PUBLISHER =
+ TelemetryProto.Publisher.newBuilder()
+ .setVehicleProperty(
+ TelemetryProto.VehiclePropertyPublisher.newBuilder()
+ .setVehiclePropertyId(VehicleProperty.GEAR_SELECTION)
+ .setReadRate(0f)
+ .build()
+ ).build();
+ private static final TelemetryProto.Subscriber VEHICLE_PROPERTY_SUBSCRIBER =
+ TelemetryProto.Subscriber.newBuilder()
+ .setHandler("onGearChange")
+ .setPublisher(VEHICLE_PROPERTY_PUBLISHER)
+ .setPriority(0)
+ .build();
+ private static final TelemetryProto.MetricsConfig METRICS_CONFIG_ON_GEAR_CHANGE_V1 =
+ TelemetryProto.MetricsConfig.newBuilder()
+ .setName("my_metrics_config")
+ .setVersion(1)
+ .setScript(LUA_SCRIPT_ON_GEAR_CHANGE)
+ .addSubscribers(VEHICLE_PROPERTY_SUBSCRIBER)
+ .build();
+ private static final MetricsConfigKey KEY_V1 = new MetricsConfigKey(
+ METRICS_CONFIG_ON_GEAR_CHANGE_V1.getName(),
+ METRICS_CONFIG_ON_GEAR_CHANGE_V1.getVersion());
+
+ private final Executor mExecutor = Executors.newSingleThreadExecutor();
+
+ private CarTelemetryManager mCarTelemetryManager;
+ private CarTelemetryResultsListenerImpl mListener;
+ private KitchenSinkActivity mActivity;
+ private TextView mOutputTextView;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ mActivity = (KitchenSinkActivity) getActivity();
+ mCarTelemetryManager = mActivity.getCarTelemetryManager();
+ mListener = new CarTelemetryResultsListenerImpl();
+ mCarTelemetryManager.setListener(mExecutor, mListener);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.car_telemetry_test, container, false);
+
+ mOutputTextView = view.findViewById(R.id.output_textview);
+ Button sendGearConfigBtn = view.findViewById(R.id.send_on_gear_change_config);
+ Button getGearReportBtn = view.findViewById(R.id.get_on_gear_change_report);
+ Button removeGearConfigBtn = view.findViewById(R.id.remove_on_gear_change_config);
+
+ sendGearConfigBtn.setOnClickListener(this::onSendGearChangeConfigBtnClick);
+ removeGearConfigBtn.setOnClickListener(this::onRemoveGearChangeConfigBtnClick);
+ getGearReportBtn.setOnClickListener(this::onGetGearChangeReportBtnClick);
+
+ return view;
+ }
+
+ private void showOutput(String s) {
+ mActivity.runOnUiThread(() -> mOutputTextView.setText(s));
+ }
+
+ private void onSendGearChangeConfigBtnClick(View view) {
+ showOutput("Sending MetricsConfig that listen for gear change...");
+ mCarTelemetryManager.addMetricsConfig(KEY_V1,
+ METRICS_CONFIG_ON_GEAR_CHANGE_V1.toByteArray());
+ }
+
+ private void onRemoveGearChangeConfigBtnClick(View view) {
+ showOutput("Removing MetricsConfig that listens for gear change...");
+ mCarTelemetryManager.removeMetricsConfig(KEY_V1);
+ }
+
+ private void onGetGearChangeReportBtnClick(View view) {
+ showOutput("Fetching report... If nothing shows up after a few seconds, "
+ + "then no result exists");
+ mCarTelemetryManager.sendFinishedReports(KEY_V1);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ }
+
+ /**
+ * Implementation of the {@link CarTelemetryManager.CarTelemetryResultsListener}. They update
+ * the view to show the outputs from the APIs of {@link CarTelemetryManager}.
+ * The callbacks are executed in {@link mExecutor}.
+ */
+ private final class CarTelemetryResultsListenerImpl
+ implements CarTelemetryManager.CarTelemetryResultsListener {
+
+ @Override
+ public void onResult(@NonNull MetricsConfigKey key, @NonNull byte[] result) {
+ PersistableBundle bundle;
+ try (ByteArrayInputStream bis = new ByteArrayInputStream(result)) {
+ bundle = PersistableBundle.readFromStream(bis);
+ } catch (IOException e) {
+ bundle = null;
+ }
+ showOutput("Result is " + bundle.toString());
+ }
+
+ @Override
+ public void onError(@NonNull MetricsConfigKey key, @NonNull byte[] error) {
+ }
+
+ @Override
+ public void onAddMetricsConfigStatus(@NonNull MetricsConfigKey key, int statusCode) {
+ showOutput("Add MetricsConfig status: " + statusCode);
+ }
+
+ @Override
+ public void onRemoveMetricsConfigStatus(@NonNull MetricsConfigKey key, boolean success) {
+ showOutput("Remove MetricsConfig status: " + success);
+ }
+ }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/telemetry/databroker/DataBrokerTest.java b/tests/carservice_unit_test/src/com/android/car/telemetry/databroker/DataBrokerTest.java
index d4f61a9..82f7e5e 100644
--- a/tests/carservice_unit_test/src/com/android/car/telemetry/databroker/DataBrokerTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/telemetry/databroker/DataBrokerTest.java
@@ -50,7 +50,9 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
import java.nio.file.Files;
import java.util.Collections;
@@ -114,8 +116,6 @@
public void setUp() throws Exception {
when(mMockCarPropertyService.getPropertyList())
.thenReturn(Collections.singletonList(PROP_CONFIG));
- // bind service should return true, otherwise broker is disabled
- when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
PublisherFactory factory = new PublisherFactory(
mMockCarPropertyService, mMockHandler, mMockStatsManager,
Files.createTempDirectory("telemetry_test").toFile());
@@ -295,11 +295,24 @@
}
@Test
- public void testScheduleNextTask_whenBindScriptExecutorFailed_shouldDisableBroker()
+ public void testScheduleNextTask_bindScriptExecutorFailedOnce_shouldRebind()
throws Exception {
- // fail all future attempts to bind to it
Mockito.reset(mMockContext);
- when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(false);
+ when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(
+ new Answer() {
+ private int mCount = 0;
+
+ @Override
+ public Object answer(InvocationOnMock invocation) {
+ if (mCount++ == 1) {
+ return false; // fail first attempt
+ }
+ ServiceConnection conn = invocation.getArgument(1);
+ conn.onServiceConnected(null, mMockScriptExecutorBinder);
+ return true; // second attempt should succeed
+ }
+ });
+ mDataBroker.mBindScriptExecutorDelayMillis = 0L; // immediately rebind for testing purpose
mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
taskQueue.add(mHighPriorityTask);
@@ -308,7 +321,27 @@
mDataBroker.scheduleNextTask();
waitForHandlerThreadToFinish();
- // all subscribers should have been removed
+ assertThat(taskQueue.peek()).isNull();
+ assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void testScheduleNextTask_bindScriptExecutorFailedMultipleTimes_shouldDisableBroker()
+ throws Exception {
+ // fail 6 future attempts to bind to it
+ Mockito.reset(mMockContext);
+ when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any()))
+ .thenReturn(false, false, false, false, false, false);
+ mDataBroker.mBindScriptExecutorDelayMillis = 0L; // immediately rebind for testing purpose
+ mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
+ PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
+ taskQueue.add(mHighPriorityTask);
+
+ // will rebind to ScriptExecutor if it is null
+ mDataBroker.scheduleNextTask();
+
+ waitForHandlerThreadToFinish();
+ // broker disabled, all subscribers should have been removed
assertThat(mDataBroker.getSubscriptionMap()).hasSize(0);
assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(0);
}