blob: 5b5ac286fad1aa9d830a1670df8e55267c8c419f [file] [log] [blame]
/*
* 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.android.car.telemetry.publisher;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import static java.util.concurrent.TimeUnit.MICROSECONDS;
import android.car.test.mocks.AbstractExtendedMockitoTestCase;
import android.content.Context;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkStats;
import android.os.IBinder;
import android.os.Looper;
import android.os.ServiceManager;
import android.os.SystemClock;
import com.android.car.telemetry.TelemetryProto;
import com.android.car.telemetry.TelemetryProto.ConnectivityPublisher.OemType;
import com.android.car.telemetry.TelemetryProto.ConnectivityPublisher.Transport;
import com.android.car.telemetry.databroker.DataBroker;
import com.android.car.telemetry.databroker.DataSubscriber;
import com.android.car.test.FakeHandlerWrapper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class ConnectivityPublisherTest extends AbstractExtendedMockitoTestCase {
private static final TelemetryProto.Publisher PUBLISHER_PARAMS_WIFI =
TelemetryProto.Publisher.newBuilder()
.setConnectivity(
TelemetryProto.ConnectivityPublisher.newBuilder()
.setTransport(Transport.TRANSPORT_WIFI)
.setOemType(OemType.OEM_NONE))
.build();
private static final TelemetryProto.Publisher PUBLISHER_PARAMS_CELL =
TelemetryProto.Publisher.newBuilder()
.setConnectivity(
TelemetryProto.ConnectivityPublisher.newBuilder()
.setTransport(Transport.TRANSPORT_CELLULAR)
.setOemType(OemType.OEM_MANAGED))
.build();
private static final TelemetryProto.Subscriber SUBSCRIBER_WIFI =
TelemetryProto.Subscriber.newBuilder()
.setHandler("handler_fn_wifi")
.setPublisher(PUBLISHER_PARAMS_WIFI)
.build();
private static final TelemetryProto.Subscriber SUBSCRIBER_CELL =
TelemetryProto.Subscriber.newBuilder()
.setHandler("handler_fn_2")
.setPublisher(PUBLISHER_PARAMS_CELL)
.build();
private static final TelemetryProto.MetricsConfig METRICS_CONFIG =
TelemetryProto.MetricsConfig.newBuilder()
.setName("myconfig")
.setVersion(1)
.addSubscribers(SUBSCRIBER_WIFI)
.addSubscribers(SUBSCRIBER_CELL)
.build();
/** See {@code ConnectivityPublisher#pullInitialNetstats()}. */
private static final int BASELINE_PULL_COUNT = 8;
private final NetworkStats mNetworkStatsEmpty =
new NetworkStats(/* elapsedRealtime= */ 0, /* initialSize= */ 0);
@Mock private IBinder mMockBinder;
@Mock private Context mMockContext;
@Mock private INetworkStatsService mMockNetworkStatsService;
@Mock private INetworkStatsSession mMockNetworkStatsSession;
@Mock private DataBroker mMockDataBroker;
private final FakeHandlerWrapper mFakeHandlerWrapper =
new FakeHandlerWrapper(Looper.getMainLooper(), FakeHandlerWrapper.Mode.QUEUEING);
private ConnectivityPublisher mPublisher; // subject
private DataSubscriber mDataSubscriberWifi;
private DataSubscriber mDataSubscriberCell;
@Override
protected void onSessionBuilder(CustomMockitoSessionBuilder builder) {
builder.spyStatic(ServiceManager.class);
}
@Before
public void setUp() throws Exception {
mPublisher = createRestartedPublisher();
when(mMockBinder.queryLocalInterface(anyString())).thenReturn(mMockNetworkStatsService);
doReturn(mMockBinder).when(() -> ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
when(mMockNetworkStatsService.openSessionForUsageStats(anyInt(), any()))
.thenReturn(mMockNetworkStatsSession);
mDataSubscriberWifi = new DataSubscriber(mMockDataBroker, METRICS_CONFIG, SUBSCRIBER_WIFI);
mDataSubscriberCell = new DataSubscriber(mMockDataBroker, METRICS_CONFIG, SUBSCRIBER_CELL);
}
/** Emulates a restart by creating a new ConnectivityPublisher. */
private ConnectivityPublisher createRestartedPublisher() throws Exception {
return new ConnectivityPublisher(
this::onPublisherFailure,
mMockNetworkStatsService,
mFakeHandlerWrapper.getMockHandler(),
mMockContext);
}
@Test
public void testAddDataSubscriber_storesIt_andStartsPeriodicPull() throws Exception {
mPublisher.addDataSubscriber(mDataSubscriberWifi);
assertThat(mPublisher.hasDataSubscriber(mDataSubscriberWifi)).isTrue();
// One for initial pull, second one for this addDataSubscriber.
assertThat(mFakeHandlerWrapper.getQueuedMessages()).hasSize(2);
}
@Test
public void testRemoveDataSubscriber_removesIt() throws Exception {
mPublisher.addDataSubscriber(mDataSubscriberWifi);
mPublisher.removeDataSubscriber(mDataSubscriberWifi);
assertThat(mPublisher.hasDataSubscriber(mDataSubscriberWifi)).isFalse();
// Only initial pull left, because we removed the last subscriber.
assertThat(mFakeHandlerWrapper.getQueuedMessages()).hasSize(1);
}
@Test
public void testRemoveDataSubscriber_givenWrongSubscriber_doesNothing() throws Exception {
mPublisher.addDataSubscriber(mDataSubscriberWifi);
mPublisher.removeDataSubscriber(mDataSubscriberCell);
assertThat(mPublisher.hasDataSubscriber(mDataSubscriberWifi)).isTrue();
// One for initial pull, second one for this addDataSubscriber.
assertThat(mFakeHandlerWrapper.getQueuedMessages()).hasSize(2);
}
@Test
public void testRemoveAllDataSubscribers_removesAll_andStopsPeriodicPull() throws Exception {
mPublisher.addDataSubscriber(mDataSubscriberWifi);
mPublisher.addDataSubscriber(mDataSubscriberCell);
mPublisher.removeAllDataSubscribers();
assertThat(mPublisher.hasDataSubscriber(mDataSubscriberWifi)).isFalse();
// Only initial pull left, because we removed the subscribers.
assertThat(mFakeHandlerWrapper.getQueuedMessages()).hasSize(1);
}
@Test
public void testPullsFromNetworkStatsService() throws Exception {
when(mMockNetworkStatsSession.getSummaryForAllUid(
any(), anyLong(), anyLong(), anyBoolean()))
.thenReturn(mNetworkStatsEmpty);
mPublisher.addDataSubscriber(mDataSubscriberWifi);
mFakeHandlerWrapper.dispatchQueuedMessages();
verify(mMockNetworkStatsService, times(BASELINE_PULL_COUNT + 1))
.openSessionForUsageStats(anyInt(), any());
ArgumentCaptor<Long> startCaptor = ArgumentCaptor.forClass(Long.class);
verify(mMockNetworkStatsSession, times(BASELINE_PULL_COUNT + 1))
.getSummaryForAllUid(any(), startCaptor.capture(), anyLong(), anyBoolean());
final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime(); // including sleep
final long currentTimeInMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
// Make sure that "start" time is earlier than the boot time.
assertThat(startCaptor.getValue()).isLessThan(currentTimeInMillis - elapsedMillisSinceBoot);
}
@Test
public void testSchedulesNextPeriodicPullInHandler() throws Exception {
when(mMockNetworkStatsSession.getSummaryForAllUid(
any(), anyLong(), anyLong(), anyBoolean()))
.thenReturn(mNetworkStatsEmpty);
mPublisher.addDataSubscriber(mDataSubscriberWifi);
mFakeHandlerWrapper.dispatchQueuedMessages(); // Current pull.
assertThat(mFakeHandlerWrapper.getQueuedMessages()).hasSize(1); // Next pull.
}
private void onPublisherFailure(
AbstractPublisher publisher,
List<TelemetryProto.MetricsConfig> affectedConfigs,
Throwable error) {}
}