1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car;
18 
19 import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_ALREADY_EXISTS;
20 import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_NONE;
21 import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_PARSE_FAILED;
22 import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_VERSION_TOO_OLD;
23 
24 import static com.google.common.truth.Truth.assertThat;
25 import static com.google.common.truth.Truth.assertWithMessage;
26 
27 import static org.junit.Assert.assertThrows;
28 import static org.junit.Assume.assumeTrue;
29 
30 import android.annotation.NonNull;
31 import android.car.Car;
32 import android.car.telemetry.CarTelemetryManager;
33 import android.car.telemetry.MetricsConfigKey;
34 import android.os.Handler;
35 import android.os.HandlerThread;
36 import android.util.ArrayMap;
37 import android.util.Log;
38 
39 import androidx.test.ext.junit.runners.AndroidJUnit4;
40 import androidx.test.filters.MediumTest;
41 
42 import com.android.car.telemetry.CarTelemetryService;
43 import com.android.car.telemetry.TelemetryProto;
44 
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 
48 import java.util.Map;
49 import java.util.concurrent.CountDownLatch;
50 import java.util.concurrent.Executor;
51 import java.util.concurrent.TimeUnit;
52 
53 /** Test the public entry points for the CarTelemetryManager. */
54 @RunWith(AndroidJUnit4.class)
55 @MediumTest
56 public class CarTelemetryManagerTest extends MockedCarTestBase {
57     private static final long TIMEOUT_MS = 5_000L;
58     private static final String TAG = CarTelemetryManagerTest.class.getSimpleName();
59     private static final byte[] INVALID_METRICS_CONFIG = "bad config".getBytes();
60     private static final Executor DIRECT_EXECUTOR = Runnable::run;
61     private static final MetricsConfigKey KEY_V1 = new MetricsConfigKey("my_metrics_config", 1);
62     private static final MetricsConfigKey KEY_V2 = new MetricsConfigKey("my_metrics_config", 2);
63     private static final TelemetryProto.MetricsConfig METRICS_CONFIG_V1 =
64             TelemetryProto.MetricsConfig.newBuilder()
65                     .setName("my_metrics_config").setVersion(1).setScript("no-op").build();
66     private static final TelemetryProto.MetricsConfig METRICS_CONFIG_V2 =
67             METRICS_CONFIG_V1.toBuilder().setVersion(2).build();
68 
69     private final FakeCarTelemetryResultsListener mListener = new FakeCarTelemetryResultsListener();
70     private final HandlerThread mTelemetryThread =
71             CarServiceUtils.getHandlerThread(CarTelemetryService.class.getSimpleName());
72     private final Handler mHandler = new Handler(mTelemetryThread.getLooper());
73 
74     private CarTelemetryManager mCarTelemetryManager;
75     private CountDownLatch mIdleHandlerLatch = new CountDownLatch(1);
76 
77     @Override
setUp()78     public void setUp() throws Exception {
79         super.setUp();
80         assumeTrue(getCar().isFeatureEnabled(Car.CAR_TELEMETRY_SERVICE));
81 
82         mTelemetryThread.getLooper().getQueue().addIdleHandler(() -> {
83             mIdleHandlerLatch.countDown();
84             return true;
85         });
86 
87         Log.i(TAG, "attempting to get CAR_TELEMETRY_SERVICE");
88         mCarTelemetryManager = (CarTelemetryManager) getCar().getCarManager(
89                 Car.CAR_TELEMETRY_SERVICE);
90         mCarTelemetryManager.setListener(DIRECT_EXECUTOR, mListener);
91     }
92 
93     @Test
testSetClearListener()94     public void testSetClearListener() {
95         mCarTelemetryManager.clearListener();
96         mCarTelemetryManager.setListener(DIRECT_EXECUTOR, mListener);
97 
98         // setListener multiple times should fail
99         assertThrows(IllegalStateException.class,
100                 () -> mCarTelemetryManager.setListener(DIRECT_EXECUTOR, mListener));
101     }
102 
103     @Test
testApiInvocationWithoutSettingListener()104     public void testApiInvocationWithoutSettingListener() {
105         mCarTelemetryManager.clearListener();
106 
107         assertThrows(IllegalStateException.class,
108                 () -> mCarTelemetryManager.addMetricsConfig(
109                         KEY_V1, METRICS_CONFIG_V1.toByteArray()));
110         assertThrows(IllegalStateException.class,
111                 () -> mCarTelemetryManager.removeMetricsConfig(KEY_V1));
112         assertThrows(IllegalStateException.class,
113                 () -> mCarTelemetryManager.removeAllMetricsConfigs());
114         assertThrows(IllegalStateException.class,
115                 () -> mCarTelemetryManager.sendFinishedReports(KEY_V1));
116         assertThrows(IllegalStateException.class,
117                 () -> mCarTelemetryManager.sendAllFinishedReports());
118     }
119 
120     @Test
testAddMetricsConfig()121     public void testAddMetricsConfig() throws Exception {
122         // invalid config, should fail
123         mCarTelemetryManager.addMetricsConfig(KEY_V1, INVALID_METRICS_CONFIG);
124         waitForHandlerThreadToFinish();
125         assertThat(mListener.getAddConfigStatus(KEY_V1)).isEqualTo(
126                 ERROR_METRICS_CONFIG_PARSE_FAILED);
127 
128         // new valid config, should succeed
129         mCarTelemetryManager.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
130         waitForHandlerThreadToFinish();
131         assertThat(mListener.getAddConfigStatus(KEY_V1)).isEqualTo(ERROR_METRICS_CONFIG_NONE);
132 
133         // duplicate config, should fail
134         mCarTelemetryManager.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
135         waitForHandlerThreadToFinish();
136         assertThat(mListener.getAddConfigStatus(KEY_V1)).isEqualTo(
137                 ERROR_METRICS_CONFIG_ALREADY_EXISTS);
138 
139         // newer version of the config should replace older version
140         mCarTelemetryManager.addMetricsConfig(KEY_V2, METRICS_CONFIG_V2.toByteArray());
141         waitForHandlerThreadToFinish();
142         assertThat(mListener.getAddConfigStatus(KEY_V2)).isEqualTo(ERROR_METRICS_CONFIG_NONE);
143 
144         // older version of the config should not be accepted
145         mCarTelemetryManager.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
146         waitForHandlerThreadToFinish();
147         assertThat(mListener.getAddConfigStatus(KEY_V1)).isEqualTo(
148                 ERROR_METRICS_CONFIG_VERSION_TOO_OLD);
149     }
150 
waitForHandlerThreadToFinish()151     private void waitForHandlerThreadToFinish() throws Exception {
152         assertWithMessage("handler not idle in %sms", TIMEOUT_MS)
153                 .that(mIdleHandlerLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
154         mIdleHandlerLatch = new CountDownLatch(1); // reset idle handler condition
155         mHandler.runWithScissors(() -> {
156         }, TIMEOUT_MS);
157     }
158 
159 
160     private static final class FakeCarTelemetryResultsListener
161             implements CarTelemetryManager.CarTelemetryResultsListener {
162 
163         private Map<MetricsConfigKey, Integer> mAddConfigStatusMap = new ArrayMap<>();
164 
165         @Override
onResult(@onNull MetricsConfigKey key, @NonNull byte[] result)166         public void onResult(@NonNull MetricsConfigKey key, @NonNull byte[] result) {
167         }
168 
169         @Override
onError(@onNull MetricsConfigKey key, @NonNull byte[] error)170         public void onError(@NonNull MetricsConfigKey key, @NonNull byte[] error) {
171         }
172 
173         @Override
onAddMetricsConfigStatus(@onNull MetricsConfigKey key, int statusCode)174         public void onAddMetricsConfigStatus(@NonNull MetricsConfigKey key, int statusCode) {
175             mAddConfigStatusMap.put(key, statusCode);
176         }
177 
getAddConfigStatus(MetricsConfigKey key)178         public int getAddConfigStatus(MetricsConfigKey key) {
179             return mAddConfigStatusMap.getOrDefault(key, -100);
180         }
181     }
182 }
183