blob: 25076a17b79c29b1eac557ba2691f2deb1eb8999 [file] [log] [blame]
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -07001/*
2 * Copyright (C) 2021 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
17package com.android.car.telemetry.publisher;
18
Howard Hao1a045752021-10-12 17:34:08 -070019import static com.android.car.telemetry.AtomsProto.Atom.ACTIVITY_FOREGROUND_STATE_CHANGED_FIELD_NUMBER;
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -070020import static com.android.car.telemetry.AtomsProto.Atom.APP_START_MEMORY_STATE_CAPTURED_FIELD_NUMBER;
21import static com.android.car.telemetry.AtomsProto.Atom.PROCESS_MEMORY_STATE_FIELD_NUMBER;
Howard Hao1a045752021-10-12 17:34:08 -070022import static com.android.car.telemetry.TelemetryProto.StatsPublisher.SystemMetric.ACTIVITY_FOREGROUND_STATE_CHANGED;
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -070023import static com.android.car.telemetry.TelemetryProto.StatsPublisher.SystemMetric.APP_START_MEMORY_STATE_CAPTURED;
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -070024import static com.android.car.telemetry.TelemetryProto.StatsPublisher.SystemMetric.PROCESS_MEMORY_STATE;
Howard Hao1a045752021-10-12 17:34:08 -070025import static com.android.car.telemetry.publisher.StatsPublisher.ACTIVITY_FOREGROUND_STATE_CHANGED_ATOM_MATCHER_ID;
26import static com.android.car.telemetry.publisher.StatsPublisher.ACTIVITY_FOREGROUND_STATE_CHANGED_EVENT_METRIC_ID;
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -070027import static com.android.car.telemetry.publisher.StatsPublisher.APP_START_MEMORY_STATE_CAPTURED_ATOM_MATCHER_ID;
28import static com.android.car.telemetry.publisher.StatsPublisher.APP_START_MEMORY_STATE_CAPTURED_EVENT_METRIC_ID;
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -070029import static com.android.car.telemetry.publisher.StatsPublisher.PROCESS_MEMORY_STATE_FIELDS_MATCHER;
30import static com.android.car.telemetry.publisher.StatsPublisher.PROCESS_MEMORY_STATE_GAUGE_METRIC_ID;
31import static com.android.car.telemetry.publisher.StatsPublisher.PROCESS_MEMORY_STATE_MATCHER_ID;
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -070032
33import static com.google.common.truth.Truth.assertThat;
34
Zhomart Mukhamejanovfb4f29d2021-08-26 17:28:07 -070035import static org.mockito.ArgumentMatchers.any;
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -070036import static org.mockito.ArgumentMatchers.anyLong;
Zhomart Mukhamejanovfb4f29d2021-08-26 17:28:07 -070037import static org.mockito.Mockito.doThrow;
Zhomart Mukhamejanov7f3b6502021-10-05 20:34:24 -070038import static org.mockito.Mockito.reset;
39import static org.mockito.Mockito.spy;
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -070040import static org.mockito.Mockito.times;
41import static org.mockito.Mockito.verify;
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -070042import static org.mockito.Mockito.when;
43
Zhomart Mukhamejanovfb4f29d2021-08-26 17:28:07 -070044import android.app.StatsManager;
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -070045import android.os.Looper;
46import android.os.Message;
Max Dashoukdafdd222021-08-20 10:42:25 -070047import android.os.PersistableBundle;
Zhomart Mukhamejanov7f3b6502021-10-05 20:34:24 -070048import android.os.Process;
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -070049import android.os.SystemClock;
50
Howard Hao2f22ed02021-09-28 11:53:37 -070051import com.android.car.telemetry.AtomsProto.AppStartMemoryStateCaptured;
52import com.android.car.telemetry.AtomsProto.Atom;
53import com.android.car.telemetry.AtomsProto.ProcessMemoryState;
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -070054import com.android.car.telemetry.StatsLogProto;
Howard Hao2f22ed02021-09-28 11:53:37 -070055import com.android.car.telemetry.StatsLogProto.ConfigMetricsReport;
56import com.android.car.telemetry.StatsLogProto.DimensionsValue;
57import com.android.car.telemetry.StatsLogProto.DimensionsValueTuple;
58import com.android.car.telemetry.StatsLogProto.EventMetricData;
59import com.android.car.telemetry.StatsLogProto.GaugeBucketInfo;
60import com.android.car.telemetry.StatsLogProto.GaugeMetricData;
61import com.android.car.telemetry.StatsLogProto.StatsLogReport;
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -070062import com.android.car.telemetry.StatsdConfigProto;
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -070063import com.android.car.telemetry.TelemetryProto;
64import com.android.car.telemetry.databroker.DataSubscriber;
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -070065import com.android.car.test.FakeHandlerWrapper;
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -070066
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -070067import com.google.common.collect.Range;
68
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -070069import org.junit.Before;
70import org.junit.Test;
71import org.junit.runner.RunWith;
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -070072import org.mockito.ArgumentCaptor;
73import org.mockito.Captor;
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -070074import org.mockito.Mock;
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -070075import org.mockito.Mockito;
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -070076import org.mockito.junit.MockitoJUnitRunner;
77
Rui Qiuda9f0ba2021-09-14 12:15:37 -070078import java.io.File;
79import java.io.FileInputStream;
80import java.nio.file.Files;
Howard Hao2f22ed02021-09-28 11:53:37 -070081import java.util.Arrays;
Zhomart Mukhamejanov39e77482021-09-29 13:08:01 -070082import java.util.List;
Rui Qiuda9f0ba2021-09-14 12:15:37 -070083
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -070084@RunWith(MockitoJUnitRunner.class)
85public class StatsPublisherTest {
86 private static final TelemetryProto.Publisher STATS_PUBLISHER_PARAMS_1 =
87 TelemetryProto.Publisher.newBuilder()
88 .setStats(TelemetryProto.StatsPublisher.newBuilder()
89 .setSystemMetric(APP_START_MEMORY_STATE_CAPTURED))
90 .build();
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -070091 private static final TelemetryProto.Publisher STATS_PUBLISHER_PARAMS_2 =
92 TelemetryProto.Publisher.newBuilder()
93 .setStats(TelemetryProto.StatsPublisher.newBuilder()
94 .setSystemMetric(PROCESS_MEMORY_STATE))
95 .build();
Howard Hao1a045752021-10-12 17:34:08 -070096 private static final TelemetryProto.Publisher STATS_PUBLISHER_PARAMS_3 =
97 TelemetryProto.Publisher.newBuilder()
98 .setStats(TelemetryProto.StatsPublisher.newBuilder()
99 .setSystemMetric(ACTIVITY_FOREGROUND_STATE_CHANGED))
100 .build();
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -0700101 private static final TelemetryProto.Subscriber SUBSCRIBER_1 =
102 TelemetryProto.Subscriber.newBuilder()
103 .setHandler("handler_fn_1")
104 .setPublisher(STATS_PUBLISHER_PARAMS_1)
105 .build();
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700106 private static final TelemetryProto.Subscriber SUBSCRIBER_2 =
107 TelemetryProto.Subscriber.newBuilder()
108 .setHandler("handler_fn_2")
109 .setPublisher(STATS_PUBLISHER_PARAMS_2)
110 .build();
Howard Hao1a045752021-10-12 17:34:08 -0700111 private static final TelemetryProto.Subscriber SUBSCRIBER_3 =
112 TelemetryProto.Subscriber.newBuilder()
113 .setHandler("handler_fn_3")
114 .setPublisher(STATS_PUBLISHER_PARAMS_3)
115 .build();
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -0700116 private static final TelemetryProto.MetricsConfig METRICS_CONFIG =
117 TelemetryProto.MetricsConfig.newBuilder()
118 .setName("myconfig")
119 .setVersion(1)
120 .addSubscribers(SUBSCRIBER_1)
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700121 .addSubscribers(SUBSCRIBER_2)
Howard Hao1a045752021-10-12 17:34:08 -0700122 .addSubscribers(SUBSCRIBER_3)
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -0700123 .build();
124
Zhomart Mukhamejanov7f3b6502021-10-05 20:34:24 -0700125 private static final long SUBSCRIBER_1_HASH = -8101507323446050791L; // Used as configKey.
126 private static final long SUBSCRIBER_2_HASH = 2778197004730583271L; // Used as configKey.
Howard Hao1a045752021-10-12 17:34:08 -0700127 private static final long SUBSCRIBER_3_HASH = 7046592220837963576L; // Used as configKey.
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700128
Zhomart Mukhamejanov7f3b6502021-10-05 20:34:24 -0700129 // This StatsdConfig is generated for SUBSCRIBER_1.
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700130 private static final StatsdConfigProto.StatsdConfig STATSD_CONFIG_1 =
131 StatsdConfigProto.StatsdConfig.newBuilder()
132 .setId(SUBSCRIBER_1_HASH)
133 .addAtomMatcher(StatsdConfigProto.AtomMatcher.newBuilder()
134 .setId(APP_START_MEMORY_STATE_CAPTURED_ATOM_MATCHER_ID)
135 .setSimpleAtomMatcher(
136 StatsdConfigProto.SimpleAtomMatcher.newBuilder()
137 .setAtomId(
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700138 APP_START_MEMORY_STATE_CAPTURED_FIELD_NUMBER)))
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700139 .addEventMetric(StatsdConfigProto.EventMetric.newBuilder()
140 .setId(APP_START_MEMORY_STATE_CAPTURED_EVENT_METRIC_ID)
141 .setWhat(APP_START_MEMORY_STATE_CAPTURED_ATOM_MATCHER_ID))
142 .addAllowedLogSource("AID_SYSTEM")
143 .build();
144
Zhomart Mukhamejanov7f3b6502021-10-05 20:34:24 -0700145 // This StatsdConfig is generated for SUBSCRIBER_2.
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700146 private static final StatsdConfigProto.StatsdConfig STATSD_CONFIG_2 =
147 StatsdConfigProto.StatsdConfig.newBuilder()
148 .setId(SUBSCRIBER_2_HASH)
149 .addAtomMatcher(StatsdConfigProto.AtomMatcher.newBuilder()
150 // The id must be unique within StatsdConfig/matchers
151 .setId(PROCESS_MEMORY_STATE_MATCHER_ID)
152 .setSimpleAtomMatcher(StatsdConfigProto.SimpleAtomMatcher.newBuilder()
153 .setAtomId(PROCESS_MEMORY_STATE_FIELD_NUMBER)))
154 .addGaugeMetric(StatsdConfigProto.GaugeMetric.newBuilder()
155 // The id must be unique within StatsdConfig/metrics
156 .setId(PROCESS_MEMORY_STATE_GAUGE_METRIC_ID)
157 .setWhat(PROCESS_MEMORY_STATE_MATCHER_ID)
158 .setDimensionsInWhat(StatsdConfigProto.FieldMatcher.newBuilder()
159 .setField(PROCESS_MEMORY_STATE_FIELD_NUMBER)
160 .addChild(StatsdConfigProto.FieldMatcher.newBuilder()
161 .setField(1)) // ProcessMemoryState.uid
162 .addChild(StatsdConfigProto.FieldMatcher.newBuilder()
163 .setField(2)) // ProcessMemoryState.process_name
164 )
165 .setGaugeFieldsFilter(StatsdConfigProto.FieldFilter.newBuilder()
166 .setFields(PROCESS_MEMORY_STATE_FIELDS_MATCHER))
167 .setSamplingType(
168 StatsdConfigProto.GaugeMetric.SamplingType.RANDOM_ONE_SAMPLE)
169 .setBucket(StatsdConfigProto.TimeUnit.FIVE_MINUTES)
170 )
171 .addAllowedLogSource("AID_SYSTEM")
172 .addPullAtomPackages(StatsdConfigProto.PullAtomPackages.newBuilder()
173 .setAtomId(PROCESS_MEMORY_STATE_FIELD_NUMBER)
174 .addPackages("AID_SYSTEM"))
175 .build();
176
Howard Hao1a045752021-10-12 17:34:08 -0700177 // This StatsdConfig is generated for SUBSCRIBER_3.
178 private static final StatsdConfigProto.StatsdConfig STATSD_CONFIG_3 =
179 StatsdConfigProto.StatsdConfig.newBuilder()
180 .setId(SUBSCRIBER_3_HASH)
181 .addAtomMatcher(StatsdConfigProto.AtomMatcher.newBuilder()
182 .setId(ACTIVITY_FOREGROUND_STATE_CHANGED_ATOM_MATCHER_ID)
183 .setSimpleAtomMatcher(
184 StatsdConfigProto.SimpleAtomMatcher.newBuilder()
185 .setAtomId(
186 ACTIVITY_FOREGROUND_STATE_CHANGED_FIELD_NUMBER)
187 ))
188 .addEventMetric(StatsdConfigProto.EventMetric.newBuilder()
189 .setId(ACTIVITY_FOREGROUND_STATE_CHANGED_EVENT_METRIC_ID)
190 .setWhat(ACTIVITY_FOREGROUND_STATE_CHANGED_ATOM_MATCHER_ID))
191 .addAllowedLogSource("AID_SYSTEM")
192 .build();
193
Howard Hao2f22ed02021-09-28 11:53:37 -0700194 private static final EventMetricData EVENT_DATA =
195 EventMetricData.newBuilder()
196 .setElapsedTimestampNanos(99999999L)
197 .setAtom(Atom.newBuilder()
198 .setAppStartMemoryStateCaptured(
199 AppStartMemoryStateCaptured.newBuilder()
200 .setUid(1000)
201 .setActivityName("activityName")
202 .setRssInBytes(1234L)))
203 .build();
204
205 private static final GaugeMetricData GAUGE_DATA =
206 GaugeMetricData.newBuilder()
207 .addBucketInfo(GaugeBucketInfo.newBuilder()
208 .addAtom(Atom.newBuilder()
209 .setProcessMemoryState(ProcessMemoryState.newBuilder()
210 .setRssInBytes(4567L)))
211 .addElapsedTimestampNanos(445678901L))
212 .addDimensionLeafValuesInWhat(DimensionsValue.newBuilder()
213 .setValueInt(234))
214 .build();
215
Zhomart Mukhamejanov7f3b6502021-10-05 20:34:24 -0700216 private static final StatsLogProto.ConfigMetricsReportList METRICS_REPORT =
Howard Hao2f22ed02021-09-28 11:53:37 -0700217 StatsLogProto.ConfigMetricsReportList.newBuilder()
218 .addReports(ConfigMetricsReport.newBuilder()
219 .addMetrics(StatsLogReport.newBuilder()
220 .setMetricId(APP_START_MEMORY_STATE_CAPTURED_EVENT_METRIC_ID)
221 .setEventMetrics(
222 StatsLogReport.EventMetricDataWrapper.newBuilder()
223 .addData(EVENT_DATA))))
224 .addReports(ConfigMetricsReport.newBuilder()
225 .addMetrics(StatsLogReport.newBuilder()
226 .setMetricId(PROCESS_MEMORY_STATE_GAUGE_METRIC_ID)
227 .setGaugeMetrics(
228 StatsLogReport.GaugeMetricDataWrapper.newBuilder()
229 .addData(GAUGE_DATA))
230 .setDimensionsPathInWhat(DimensionsValue.newBuilder()
231 .setValueTuple(DimensionsValueTuple.newBuilder()
232 .addDimensionsValue(DimensionsValue.newBuilder()
233 .setField(1))))))
234 .build();
235
Zhomart Mukhamejanov7f3b6502021-10-05 20:34:24 -0700236 // By default the test assumes all the StatsdConfigs are valid.
237 private static final StatsLogProto.StatsdStatsReport CONFIG_STATS_REPORT =
238 StatsLogProto.StatsdStatsReport.newBuilder()
239 .addConfigStats(StatsLogProto.StatsdStatsReport.ConfigStats.newBuilder()
240 // in unit tests UID of test and app are the same
241 .setUid(Process.myUid())
242 .setId(SUBSCRIBER_1_HASH) // id is the same as configKey
243 .setIsValid(true))
244 .addConfigStats(StatsLogProto.StatsdStatsReport.ConfigStats.newBuilder()
245 // in unit tests UID of test and app are the same
246 .setUid(Process.myUid())
247 .setId(SUBSCRIBER_2_HASH) // id is the same as configKey
248 .setIsValid(true))
249 .build();
250
251 private static final StatsLogProto.ConfigMetricsReportList EMPTY_METRICS_REPORT =
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700252 StatsLogProto.ConfigMetricsReportList.newBuilder().build();
253
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700254 private static final DataSubscriber DATA_SUBSCRIBER_1 =
255 new DataSubscriber(null, METRICS_CONFIG, SUBSCRIBER_1);
256
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700257 private final FakeHandlerWrapper mFakeHandlerWrapper =
258 new FakeHandlerWrapper(Looper.getMainLooper(), FakeHandlerWrapper.Mode.QUEUEING);
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700259
260 private File mRootDirectory;
261 private StatsPublisher mPublisher; // subject
Zhomart Mukhamejanov39e77482021-09-29 13:08:01 -0700262
263 // These 2 variables are set in onPublisherFailure() callback. Defaults to null.
Zhomart Mukhamejanovfb4f29d2021-08-26 17:28:07 -0700264 private Throwable mPublisherFailure;
Zhomart Mukhamejanov39e77482021-09-29 13:08:01 -0700265 private List<TelemetryProto.MetricsConfig> mFailedConfigs;
Zhomart Mukhamejanovfb4f29d2021-08-26 17:28:07 -0700266
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -0700267 @Mock private StatsManagerProxy mStatsManager;
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -0700268
Max Dashoukdafdd222021-08-20 10:42:25 -0700269 @Captor private ArgumentCaptor<PersistableBundle> mBundleCaptor;
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700270
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -0700271 @Before
272 public void setUp() throws Exception {
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700273 mRootDirectory = Files.createTempDirectory("telemetry_test").toFile();
Zhomart Mukhamejanova16bfc92021-08-25 18:36:16 -0700274 mPublisher = createRestartedPublisher();
Zhomart Mukhamejanov7f3b6502021-10-05 20:34:24 -0700275 when(mStatsManager.getStatsMetadata()).thenReturn(CONFIG_STATS_REPORT.toByteArray());
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -0700276 }
277
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700278 /**
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700279 * Emulates a restart by creating a new StatsPublisher. StatsManager and PersistableBundle
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700280 * stays the same.
281 */
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700282 private StatsPublisher createRestartedPublisher() throws Exception {
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700283 return new StatsPublisher(
Zhomart Mukhamejanova16bfc92021-08-25 18:36:16 -0700284 this::onPublisherFailure,
285 mStatsManager,
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700286 mRootDirectory,
Zhomart Mukhamejanova16bfc92021-08-25 18:36:16 -0700287 mFakeHandlerWrapper.getMockHandler());
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700288 }
289
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -0700290 @Test
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700291 public void testAddDataSubscriber_registersNewListener() throws Exception {
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700292 mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -0700293
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700294 verify(mStatsManager, times(1))
295 .addConfig(SUBSCRIBER_1_HASH, STATSD_CONFIG_1.toByteArray());
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700296 assertThat(mPublisher.hasDataSubscriber(DATA_SUBSCRIBER_1)).isTrue();
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -0700297 }
298
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700299 @Test
300 public void testAddDataSubscriber_sameVersion_addsToStatsdOnce() throws Exception {
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700301 mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
302 mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700303
304 verify(mStatsManager, times(1))
305 .addConfig(SUBSCRIBER_1_HASH, STATSD_CONFIG_1.toByteArray());
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700306 assertThat(mPublisher.hasDataSubscriber(DATA_SUBSCRIBER_1)).isTrue();
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700307 }
308
309 @Test
310 public void testAddDataSubscriber_whenRestarted_addsToStatsdOnce() throws Exception {
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700311 mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700312 StatsPublisher publisher2 = createRestartedPublisher();
313
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700314 publisher2.addDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700315
316 verify(mStatsManager, times(1))
317 .addConfig(SUBSCRIBER_1_HASH, STATSD_CONFIG_1.toByteArray());
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700318 assertThat(publisher2.hasDataSubscriber(DATA_SUBSCRIBER_1)).isTrue();
319 }
320
321 @Test
322 public void testAddDataSubscriber_forProcessMemoryState_generatesStatsdMetrics()
323 throws Exception {
324 DataSubscriber processMemoryStateSubscriber =
325 new DataSubscriber(null, METRICS_CONFIG, SUBSCRIBER_2);
326
327 mPublisher.addDataSubscriber(processMemoryStateSubscriber);
328
329 verify(mStatsManager, times(1))
330 .addConfig(SUBSCRIBER_2_HASH, STATSD_CONFIG_2.toByteArray());
331 assertThat(mPublisher.hasDataSubscriber(processMemoryStateSubscriber)).isTrue();
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700332 }
333
334 @Test
Howard Hao1a045752021-10-12 17:34:08 -0700335 public void testAddDataSubscriber_forActivityForegroundState_generatesStatsdMetrics()
336 throws Exception {
337 DataSubscriber activityForegroundStateSubscriber =
338 new DataSubscriber(null, METRICS_CONFIG, SUBSCRIBER_3);
339
340 mPublisher.addDataSubscriber(activityForegroundStateSubscriber);
341
342 verify(mStatsManager, times(1))
343 .addConfig(SUBSCRIBER_3_HASH, STATSD_CONFIG_3.toByteArray());
344 assertThat(mPublisher.hasDataSubscriber(activityForegroundStateSubscriber)).isTrue();
345 }
346
347 @Test
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700348 public void testRemoveDataSubscriber_removesFromStatsd() throws Exception {
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700349 mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700350
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700351 mPublisher.removeDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700352
353 verify(mStatsManager, times(1)).removeConfig(SUBSCRIBER_1_HASH);
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700354 assertThat(getSavedStatsConfigs().keySet()).isEmpty();
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700355 assertThat(mPublisher.hasDataSubscriber(DATA_SUBSCRIBER_1)).isFalse();
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700356 }
357
358 @Test
359 public void testRemoveDataSubscriber_ifNotFound_nothingHappensButCallsStatsdRemove()
360 throws Exception {
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700361 mPublisher.removeDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700362
363 // It should try removing StatsdConfig from StatsD, in case it was added there before and
364 // left dangled.
365 verify(mStatsManager, times(1)).removeConfig(SUBSCRIBER_1_HASH);
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700366 assertThat(mPublisher.hasDataSubscriber(DATA_SUBSCRIBER_1)).isFalse();
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700367 }
368
369 @Test
370 public void testRemoveAllDataSubscriber_whenRestarted_removesFromStatsdAndClears()
371 throws Exception {
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700372 mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700373 StatsPublisher publisher2 = createRestartedPublisher();
374
375 publisher2.removeAllDataSubscribers();
376
377 verify(mStatsManager, times(1)).removeConfig(SUBSCRIBER_1_HASH);
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700378 assertThat(getSavedStatsConfigs().keySet()).isEmpty();
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700379 assertThat(publisher2.hasDataSubscriber(DATA_SUBSCRIBER_1)).isFalse();
Zhomart Mukhamejanov7d5ecf02021-08-22 17:21:35 -0700380 }
381
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700382 @Test
383 public void testAddDataSubscriber_queuesPeriodicTaskInTheHandler() {
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700384 mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700385
386 assertThat(mFakeHandlerWrapper.getQueuedMessages()).hasSize(1);
387 Message msg = mFakeHandlerWrapper.getQueuedMessages().get(0);
388 long expectedPullPeriodMillis = 10 * 60 * 1000; // 10 minutes
389 assertThatMessageIsScheduledWithGivenDelay(msg, expectedPullPeriodMillis);
390 }
391
392 @Test
Zhomart Mukhamejanovfb4f29d2021-08-26 17:28:07 -0700393 public void testAddDataSubscriber_whenFails_notifiesFailureConsumer() throws Exception {
394 doThrow(new StatsManager.StatsUnavailableException("fail"))
395 .when(mStatsManager).addConfig(anyLong(), any());
396
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700397 mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanovfb4f29d2021-08-26 17:28:07 -0700398
399 assertThat(mPublisherFailure).hasMessageThat().contains("Failed to add config");
Zhomart Mukhamejanov39e77482021-09-29 13:08:01 -0700400 assertThat(mFailedConfigs).hasSize(1); // got all the failed configs
Zhomart Mukhamejanovfb4f29d2021-08-26 17:28:07 -0700401 }
402
403 @Test
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700404 public void testRemoveDataSubscriber_removesPeriodicStatsdReportPull() {
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700405 mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700406
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700407 mPublisher.removeDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700408
409 assertThat(mFakeHandlerWrapper.getQueuedMessages()).isEmpty();
410 }
411
412 @Test
413 public void testRemoveAllDataSubscriber_removesPeriodicStatsdReportPull() {
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700414 mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700415
416 mPublisher.removeAllDataSubscribers();
417
418 assertThat(mFakeHandlerWrapper.getQueuedMessages()).isEmpty();
419 }
420
421 @Test
422 public void testAfterDispatchItSchedulesANewPullReportTask() throws Exception {
Zhomart Mukhamejanov60a049d2021-09-07 14:40:48 -0700423 mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700424 Message firstMessage = mFakeHandlerWrapper.getQueuedMessages().get(0);
Zhomart Mukhamejanov7f3b6502021-10-05 20:34:24 -0700425 when(mStatsManager.getReports(anyLong())).thenReturn(EMPTY_METRICS_REPORT.toByteArray());
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700426
427 mFakeHandlerWrapper.dispatchQueuedMessages();
428
429 assertThat(mFakeHandlerWrapper.getQueuedMessages()).hasSize(1);
430 Message newMessage = mFakeHandlerWrapper.getQueuedMessages().get(0);
431 assertThat(newMessage).isNotEqualTo(firstMessage);
432 long expectedPullPeriodMillis = 10 * 60 * 1000; // 10 minutes
433 assertThatMessageIsScheduledWithGivenDelay(newMessage, expectedPullPeriodMillis);
434 }
435
436 @Test
Howard Hao2f22ed02021-09-28 11:53:37 -0700437 public void testPullStatsdReport_correctlyPushesBundlesToSubscribers() throws Exception {
438 DataSubscriber subscriber1 = Mockito.mock(DataSubscriber.class);
439 when(subscriber1.getSubscriber()).thenReturn(SUBSCRIBER_1);
440 when(subscriber1.getMetricsConfig()).thenReturn(METRICS_CONFIG);
441 when(subscriber1.getPublisherParam()).thenReturn(SUBSCRIBER_1.getPublisher());
442 mPublisher.addDataSubscriber(subscriber1);
443 DataSubscriber subscriber2 = Mockito.mock(DataSubscriber.class);
444 when(subscriber2.getSubscriber()).thenReturn(SUBSCRIBER_2);
445 when(subscriber2.getMetricsConfig()).thenReturn(METRICS_CONFIG);
446 when(subscriber2.getPublisherParam()).thenReturn(SUBSCRIBER_2.getPublisher());
447 mPublisher.addDataSubscriber(subscriber2);
Zhomart Mukhamejanov7f3b6502021-10-05 20:34:24 -0700448 when(mStatsManager.getReports(anyLong())).thenReturn(METRICS_REPORT.toByteArray());
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700449
450 mFakeHandlerWrapper.dispatchQueuedMessages();
451
Howard Hao2f22ed02021-09-28 11:53:37 -0700452 verify(subscriber1).push(mBundleCaptor.capture());
453 PersistableBundle bundle1 = mBundleCaptor.getValue();
454 assertThat(bundle1.getLongArray("elapsed_timestamp_nanos"))
455 .asList().containsExactly(99999999L);
456 assertThat(bundle1.getIntArray("uid")).asList().containsExactly(1000);
457 assertThat(Arrays.asList(bundle1.getStringArray("activity_name")))
458 .containsExactly("activityName");
459 assertThat(bundle1.getLongArray("rss_in_bytes")).asList().containsExactly(1234L);
460 verify(subscriber2).push(mBundleCaptor.capture());
461 PersistableBundle bundle2 = mBundleCaptor.getValue();
462 assertThat(bundle2.getIntArray("uid")).asList().containsExactly(234);
463 assertThat(bundle2.getLongArray("rss_in_bytes")).asList().containsExactly(4567L);
464 assertThat(bundle2.getLongArray("elapsed_timestamp_nanos"))
465 .asList().containsExactly(445678901L);
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700466 }
467
Zhomart Mukhamejanov7f3b6502021-10-05 20:34:24 -0700468 @Test
469 public void testOnInvalidConfig_notifiesPublisherFailureListener() throws Exception {
470 DataSubscriber subscriber = spy(new DataSubscriber(null, METRICS_CONFIG, SUBSCRIBER_1));
471 mPublisher.addDataSubscriber(subscriber);
472 reset(mStatsManager);
473 when(mStatsManager.getStatsMetadata()).thenReturn(
474 StatsLogProto.StatsdStatsReport.newBuilder()
475 .addConfigStats(StatsLogProto.StatsdStatsReport.ConfigStats.newBuilder()
476 // in unit tests UID of test and app are the same
477 .setUid(Process.myUid())
478 .setId(SUBSCRIBER_1_HASH) // id is the same as configKey
479 .setIsValid(false))
480 .build().toByteArray());
481 when(mStatsManager.getReports(anyLong())).thenReturn(EMPTY_METRICS_REPORT.toByteArray());
482
483 mFakeHandlerWrapper.dispatchQueuedMessages();
484
485 // subscriber shouldn't get data, because of EMPTY_METRICS_REPORT.
486 verify(subscriber, times(0)).push(any());
487 assertThat(mFailedConfigs).containsExactly(METRICS_CONFIG);
488 assertThat(mPublisherFailure).hasMessageThat().contains("Found invalid configs");
489 }
Zhomart Mukhamejanov39e77482021-09-29 13:08:01 -0700490
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700491 private PersistableBundle getSavedStatsConfigs() throws Exception {
492 File savedConfigsFile = new File(mRootDirectory, StatsPublisher.SAVED_STATS_CONFIGS_FILE);
Rui Qiu4475ced2021-10-05 17:59:32 -0700493 if (!savedConfigsFile.exists()) {
494 return new PersistableBundle();
495 }
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700496 try (FileInputStream fileInputStream = new FileInputStream(savedConfigsFile)) {
497 return PersistableBundle.readFromStream(fileInputStream);
498 }
499 }
500
Zhomart Mukhamejanov39e77482021-09-29 13:08:01 -0700501 private void onPublisherFailure(AbstractPublisher publisher,
502 List<TelemetryProto.MetricsConfig> affectedConfigs, Throwable error) {
Zhomart Mukhamejanovfb4f29d2021-08-26 17:28:07 -0700503 mPublisherFailure = error;
Zhomart Mukhamejanov39e77482021-09-29 13:08:01 -0700504 mFailedConfigs = affectedConfigs;
Zhomart Mukhamejanovfb4f29d2021-08-26 17:28:07 -0700505 }
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700506
507 private static void assertThatMessageIsScheduledWithGivenDelay(Message msg, long delayMillis) {
508 long expectedTimeMillis = SystemClock.uptimeMillis() + delayMillis;
509 long deltaMillis = 1000; // +/- 1 seconds is good enough for testing
510 assertThat(msg.getWhen()).isIn(Range
511 .closed(expectedTimeMillis - deltaMillis, expectedTimeMillis + deltaMillis));
512 }
Zhomart Mukhamejanovdc4f4502021-07-08 09:29:31 -0700513}