Merge "IP connectivity metrics: delete obsolete logger service"
am: 37e3f22c2e
Change-Id: Ib335bc5e19c2c703d23267bf2835fc89fcf5a957
This commit is contained in:
@ -46,32 +46,7 @@ public class ConnectivityMetricsLogger {
|
||||
|
||||
public static final String DATA_KEY_EVENTS_COUNT = "count";
|
||||
|
||||
/** {@hide} */ protected IConnectivityMetricsLogger mService;
|
||||
/** {@hide} */ protected volatile long mServiceUnblockedTimestampMillis;
|
||||
private int mNumSkippedEvents;
|
||||
|
||||
public ConnectivityMetricsLogger() {
|
||||
// TODO: consider not initializing mService in constructor
|
||||
this(IConnectivityMetricsLogger.Stub.asInterface(
|
||||
ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE)));
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
@VisibleForTesting
|
||||
public ConnectivityMetricsLogger(IConnectivityMetricsLogger service) {
|
||||
mService = service;
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
protected boolean checkLoggerService() {
|
||||
if (mService != null) {
|
||||
return true;
|
||||
}
|
||||
// Two threads racing here will write the same pointer because getService
|
||||
// is idempotent once MetricsLoggerService is initialized.
|
||||
mService = IConnectivityMetricsLogger.Stub.asInterface(
|
||||
ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE));
|
||||
return mService != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,62 +63,6 @@ public class ConnectivityMetricsLogger {
|
||||
* @param data is a Parcelable instance representing the event.
|
||||
*/
|
||||
public void logEvent(long timestamp, int componentTag, int eventTag, Parcelable data) {
|
||||
if (mService == null) {
|
||||
if (DBG) {
|
||||
Log.d(TAG, "logEvent(" + componentTag + "," + eventTag + ") Service not ready");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mServiceUnblockedTimestampMillis > 0) {
|
||||
if (System.currentTimeMillis() < mServiceUnblockedTimestampMillis) {
|
||||
// Service is throttling events.
|
||||
// Don't send new events because they will be dropped.
|
||||
mNumSkippedEvents++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ConnectivityMetricsEvent skippedEventsEvent = null;
|
||||
if (mNumSkippedEvents > 0) {
|
||||
// Log number of skipped events
|
||||
Bundle b = new Bundle();
|
||||
b.putInt(DATA_KEY_EVENTS_COUNT, mNumSkippedEvents);
|
||||
|
||||
// Log the skipped event.
|
||||
// TODO: Note that some of the clients push all states events into the server,
|
||||
// If we lose some states logged here, we might mess up the statistics happened at the
|
||||
// backend. One of the options is to introduce a non-skippable flag for important events
|
||||
// that are logged.
|
||||
skippedEventsEvent = new ConnectivityMetricsEvent(mServiceUnblockedTimestampMillis,
|
||||
componentTag, TAG_SKIPPED_EVENTS, b);
|
||||
|
||||
mServiceUnblockedTimestampMillis = 0;
|
||||
}
|
||||
|
||||
ConnectivityMetricsEvent event = new ConnectivityMetricsEvent(timestamp, componentTag,
|
||||
eventTag, data);
|
||||
|
||||
try {
|
||||
long result;
|
||||
if (skippedEventsEvent == null) {
|
||||
result = mService.logEvent(event);
|
||||
} else {
|
||||
result = mService.logEvents(new ConnectivityMetricsEvent[]
|
||||
{skippedEventsEvent, event});
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
mNumSkippedEvents = 0;
|
||||
} else {
|
||||
mNumSkippedEvents++;
|
||||
if (result > 0) { // events are throttled
|
||||
mServiceUnblockedTimestampMillis = result;
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error logging event", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,33 +76,17 @@ public class ConnectivityMetricsLogger {
|
||||
* @return events
|
||||
*/
|
||||
public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
|
||||
try {
|
||||
return mService.getEvents(reference);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "IConnectivityMetricsLogger.getEvents", e);
|
||||
return null;
|
||||
}
|
||||
return new ConnectivityMetricsEvent[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register PendingIntent which will be sent when new events are ready to be retrieved.
|
||||
*/
|
||||
public boolean register(PendingIntent newEventsIntent) {
|
||||
try {
|
||||
return mService.register(newEventsIntent);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "IConnectivityMetricsLogger.register", e);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean unregister(PendingIntent newEventsIntent) {
|
||||
try {
|
||||
mService.unregister(newEventsIntent);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "IConnectivityMetricsLogger.unregister", e);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,375 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.server.connectivity;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.SystemService;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.ConnectivityMetricsEvent;
|
||||
import android.net.ConnectivityMetricsLogger;
|
||||
import android.net.IConnectivityMetricsLogger;
|
||||
import android.os.Binder;
|
||||
import android.os.Parcel;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/** {@hide} */
|
||||
public class MetricsLoggerService extends SystemService {
|
||||
private static String TAG = "ConnectivityMetricsLoggerService";
|
||||
private static final boolean DBG = true;
|
||||
private static final boolean VDBG = false;
|
||||
|
||||
public MetricsLoggerService(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
resetThrottlingCounters(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBootPhase(int phase) {
|
||||
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
|
||||
if (DBG) Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
|
||||
publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE,
|
||||
mBinder);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: read these constants from system property
|
||||
private final int EVENTS_NOTIFICATION_THRESHOLD = 300;
|
||||
private final int MAX_NUMBER_OF_EVENTS = 1000;
|
||||
private final int THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT = 1000;
|
||||
private final long THROTTLING_TIME_INTERVAL_MILLIS = DateUtils.HOUR_IN_MILLIS;
|
||||
|
||||
private int mEventCounter = 0;
|
||||
|
||||
/**
|
||||
* Reference of the last event in the list of cached events.
|
||||
*
|
||||
* When client of this service retrieves events by calling getEvents, it is passing
|
||||
* ConnectivityMetricsEvent.Reference object. After getEvents returns, that object will
|
||||
* contain this reference. The client can save it and use next time it calls getEvents.
|
||||
* This way only new events will be returned.
|
||||
*/
|
||||
private long mLastEventReference = 0;
|
||||
|
||||
private final int mThrottlingCounters[] =
|
||||
new int[ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS];
|
||||
|
||||
private long mThrottlingIntervalBoundaryMillis;
|
||||
|
||||
private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>();
|
||||
|
||||
private void enforceConnectivityInternalPermission() {
|
||||
getContext().enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.CONNECTIVITY_INTERNAL,
|
||||
"MetricsLoggerService");
|
||||
}
|
||||
|
||||
private void enforceDumpPermission() {
|
||||
getContext().enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.DUMP,
|
||||
"MetricsLoggerService");
|
||||
}
|
||||
|
||||
private void resetThrottlingCounters(long currentTimeMillis) {
|
||||
synchronized (mThrottlingCounters) {
|
||||
for (int i = 0; i < mThrottlingCounters.length; i++) {
|
||||
mThrottlingCounters[i] = 0;
|
||||
}
|
||||
mThrottlingIntervalBoundaryMillis =
|
||||
currentTimeMillis + THROTTLING_TIME_INTERVAL_MILLIS;
|
||||
}
|
||||
}
|
||||
|
||||
private void addEvent(ConnectivityMetricsEvent e) {
|
||||
if (VDBG) {
|
||||
Log.v(TAG, "writeEvent(" + e.toString() + ")");
|
||||
}
|
||||
|
||||
while (mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
|
||||
mEvents.removeFirst();
|
||||
}
|
||||
|
||||
mEvents.addLast(e);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
final MetricsLoggerImpl mBinder = new MetricsLoggerImpl();
|
||||
|
||||
/**
|
||||
* Implementation of the IConnectivityMetricsLogger interface.
|
||||
*/
|
||||
final class MetricsLoggerImpl extends IConnectivityMetricsLogger.Stub {
|
||||
|
||||
private final ArrayList<PendingIntent> mPendingIntents = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
pw.println("Permission Denial: can't dump ConnectivityMetricsLoggerService " +
|
||||
"from from pid=" + Binder.getCallingPid() + ", uid=" +
|
||||
Binder.getCallingUid());
|
||||
return;
|
||||
}
|
||||
|
||||
boolean dumpSerializedSize = false;
|
||||
boolean dumpEvents = false;
|
||||
boolean dumpDebugInfo = false;
|
||||
for (String arg : args) {
|
||||
switch (arg) {
|
||||
case "--debug":
|
||||
dumpDebugInfo = true;
|
||||
break;
|
||||
|
||||
case "--events":
|
||||
dumpEvents = true;
|
||||
break;
|
||||
|
||||
case "--size":
|
||||
dumpSerializedSize = true;
|
||||
break;
|
||||
|
||||
case "--all":
|
||||
dumpDebugInfo = true;
|
||||
dumpEvents = true;
|
||||
dumpSerializedSize = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (mEvents) {
|
||||
pw.println("Number of events: " + mEvents.size());
|
||||
pw.println("Counter: " + mEventCounter);
|
||||
if (mEvents.size() > 0) {
|
||||
pw.println("Time span: " +
|
||||
DateUtils.formatElapsedTime(
|
||||
(System.currentTimeMillis() - mEvents.peekFirst().timestamp)
|
||||
/ 1000));
|
||||
}
|
||||
|
||||
if (dumpSerializedSize) {
|
||||
Parcel p = Parcel.obtain();
|
||||
for (ConnectivityMetricsEvent e : mEvents) {
|
||||
p.writeParcelable(e, 0);
|
||||
}
|
||||
pw.println("Serialized data size: " + p.dataSize());
|
||||
p.recycle();
|
||||
}
|
||||
|
||||
if (dumpEvents) {
|
||||
pw.println();
|
||||
pw.println("Events:");
|
||||
for (ConnectivityMetricsEvent e : mEvents) {
|
||||
pw.println(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dumpDebugInfo) {
|
||||
synchronized (mThrottlingCounters) {
|
||||
pw.println();
|
||||
for (int i = 0; i < ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS; i++) {
|
||||
if (mThrottlingCounters[i] > 0) {
|
||||
pw.println("Throttling Counter #" + i + ": " + mThrottlingCounters[i]);
|
||||
}
|
||||
}
|
||||
pw.println("Throttling Time Remaining: " +
|
||||
DateUtils.formatElapsedTime(
|
||||
(mThrottlingIntervalBoundaryMillis - System.currentTimeMillis())
|
||||
/ 1000));
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (mPendingIntents) {
|
||||
if (!mPendingIntents.isEmpty()) {
|
||||
pw.println();
|
||||
pw.println("Pending intents:");
|
||||
for (PendingIntent pi : mPendingIntents) {
|
||||
pw.println(pi.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long logEvent(ConnectivityMetricsEvent event) {
|
||||
ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event};
|
||||
return logEvents(events);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param events
|
||||
*
|
||||
* Note: All events must belong to the same component.
|
||||
*
|
||||
* @return 0 on success
|
||||
* <0 if error happened
|
||||
* >0 timestamp after which new events will be accepted
|
||||
*/
|
||||
public long logEvents(ConnectivityMetricsEvent[] events) {
|
||||
enforceConnectivityInternalPermission();
|
||||
|
||||
if (events == null || events.length == 0) {
|
||||
Log.wtf(TAG, "No events passed to logEvents()");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int componentTag = events[0].componentTag;
|
||||
if (componentTag < 0 ||
|
||||
componentTag >= ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS) {
|
||||
Log.wtf(TAG, "Unexpected tag: " + componentTag);
|
||||
return -1;
|
||||
}
|
||||
|
||||
synchronized (mThrottlingCounters) {
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
if (currentTimeMillis > mThrottlingIntervalBoundaryMillis) {
|
||||
resetThrottlingCounters(currentTimeMillis);
|
||||
}
|
||||
|
||||
mThrottlingCounters[componentTag] += events.length;
|
||||
|
||||
if (mThrottlingCounters[componentTag] >
|
||||
THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT) {
|
||||
Log.w(TAG, "Too many events from #" + componentTag +
|
||||
". Block until " + mThrottlingIntervalBoundaryMillis);
|
||||
|
||||
return mThrottlingIntervalBoundaryMillis;
|
||||
}
|
||||
}
|
||||
|
||||
boolean sendPendingIntents = false;
|
||||
|
||||
synchronized (mEvents) {
|
||||
for (ConnectivityMetricsEvent e : events) {
|
||||
if (e.componentTag != componentTag) {
|
||||
Log.wtf(TAG, "Unexpected tag: " + e.componentTag);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addEvent(e);
|
||||
}
|
||||
|
||||
mLastEventReference += events.length;
|
||||
|
||||
mEventCounter += events.length;
|
||||
if (mEventCounter >= EVENTS_NOTIFICATION_THRESHOLD) {
|
||||
mEventCounter = 0;
|
||||
sendPendingIntents = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (sendPendingIntents) {
|
||||
synchronized (mPendingIntents) {
|
||||
for (PendingIntent pi : mPendingIntents) {
|
||||
if (VDBG) Log.v(TAG, "Send pending intent");
|
||||
try {
|
||||
pi.send(getContext(), 0, null, null, null);
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
Log.e(TAG, "Pending intent canceled: " + pi);
|
||||
mPendingIntents.remove(pi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve events
|
||||
*
|
||||
* @param reference of the last event previously returned. The function will return
|
||||
* events following it.
|
||||
* If 0 then all events will be returned.
|
||||
* After the function call it will contain reference of the
|
||||
* last returned event.
|
||||
* @return events
|
||||
*/
|
||||
public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
|
||||
enforceDumpPermission();
|
||||
long ref = reference.getValue();
|
||||
if (VDBG) Log.v(TAG, "getEvents(" + ref + ")");
|
||||
|
||||
ConnectivityMetricsEvent[] result;
|
||||
synchronized (mEvents) {
|
||||
if (ref > mLastEventReference) {
|
||||
Log.e(TAG, "Invalid reference");
|
||||
reference.setValue(mLastEventReference);
|
||||
return null;
|
||||
}
|
||||
if (ref < mLastEventReference - mEvents.size()) {
|
||||
ref = mLastEventReference - mEvents.size();
|
||||
}
|
||||
|
||||
int numEventsToSkip =
|
||||
mEvents.size() // Total number of events
|
||||
- (int)(mLastEventReference - ref); // Number of events to return
|
||||
|
||||
result = new ConnectivityMetricsEvent[mEvents.size() - numEventsToSkip];
|
||||
int i = 0;
|
||||
for (ConnectivityMetricsEvent e : mEvents) {
|
||||
if (numEventsToSkip > 0) {
|
||||
numEventsToSkip--;
|
||||
} else {
|
||||
result[i++] = e;
|
||||
}
|
||||
}
|
||||
|
||||
reference.setValue(mLastEventReference);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean register(PendingIntent newEventsIntent) {
|
||||
enforceDumpPermission();
|
||||
if (VDBG) Log.v(TAG, "register(" + newEventsIntent + ")");
|
||||
|
||||
synchronized (mPendingIntents) {
|
||||
if (mPendingIntents.remove(newEventsIntent)) {
|
||||
Log.w(TAG, "Replacing registered pending intent");
|
||||
}
|
||||
mPendingIntents.add(newEventsIntent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void unregister(PendingIntent newEventsIntent) {
|
||||
enforceDumpPermission();
|
||||
if (VDBG) Log.v(TAG, "unregister(" + newEventsIntent + ")");
|
||||
|
||||
synchronized (mPendingIntents) {
|
||||
if (!mPendingIntents.remove(newEventsIntent)) {
|
||||
Log.e(TAG, "Pending intent is not registered");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -61,7 +61,6 @@ import com.android.server.audio.AudioService;
|
||||
import com.android.server.camera.CameraService;
|
||||
import com.android.server.clipboard.ClipboardService;
|
||||
import com.android.server.connectivity.IpConnectivityMetrics;
|
||||
import com.android.server.connectivity.MetricsLoggerService;
|
||||
import com.android.server.devicepolicy.DevicePolicyManagerService;
|
||||
import com.android.server.display.DisplayManagerService;
|
||||
import com.android.server.display.NightDisplayService;
|
||||
@ -662,10 +661,6 @@ public final class SystemServer {
|
||||
mSystemServiceManager.startService(BluetoothService.class);
|
||||
}
|
||||
|
||||
traceBeginAndSlog("ConnectivityMetricsLoggerService");
|
||||
mSystemServiceManager.startService(MetricsLoggerService.class);
|
||||
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
|
||||
|
||||
traceBeginAndSlog("IpConnectivityMetrics");
|
||||
mSystemServiceManager.startService(IpConnectivityMetrics.class);
|
||||
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
|
||||
|
@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016, 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 android.net;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import java.util.List;
|
||||
import junit.framework.TestCase;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ConnectivityMetricsLoggerTest extends TestCase {
|
||||
|
||||
// use same Parcel object everywhere for pointer equality
|
||||
static final Bundle FAKE_EV = new Bundle();
|
||||
static final int FAKE_COMPONENT = 1;
|
||||
static final int FAKE_EVENT = 2;
|
||||
|
||||
@Mock IConnectivityMetricsLogger mService;
|
||||
ArgumentCaptor<ConnectivityMetricsEvent> evCaptor;
|
||||
ArgumentCaptor<ConnectivityMetricsEvent[]> evArrayCaptor;
|
||||
|
||||
ConnectivityMetricsLogger mLog;
|
||||
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
evCaptor = ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
|
||||
evArrayCaptor = ArgumentCaptor.forClass(ConnectivityMetricsEvent[].class);
|
||||
mLog = new ConnectivityMetricsLogger(mService);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLogEvents() throws Exception {
|
||||
mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
mLog.logEvent(3, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
|
||||
List<ConnectivityMetricsEvent> gotEvents = verifyEvents(3);
|
||||
assertEventsEqual(expectedEvent(1), gotEvents.get(0));
|
||||
assertEventsEqual(expectedEvent(2), gotEvents.get(1));
|
||||
assertEventsEqual(expectedEvent(3), gotEvents.get(2));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLogEventTriggerThrottling() throws Exception {
|
||||
when(mService.logEvent(any())).thenReturn(1234L);
|
||||
|
||||
mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
|
||||
List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1);
|
||||
assertEventsEqual(expectedEvent(1), gotEvents.get(0));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLogEventFails() throws Exception {
|
||||
when(mService.logEvent(any())).thenReturn(-1L); // Error.
|
||||
|
||||
mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
|
||||
List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1);
|
||||
assertEventsEqual(expectedEvent(1), gotEvents.get(0));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLogEventWhenThrottling() throws Exception {
|
||||
when(mService.logEvent(any())).thenReturn(Long.MAX_VALUE); // Throttled
|
||||
|
||||
// No events are logged. The service is only called once
|
||||
// After that, throttling state is maintained locally.
|
||||
mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
|
||||
List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1);
|
||||
assertEventsEqual(expectedEvent(1), gotEvents.get(0));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLogEventRecoverFromThrottling() throws Exception {
|
||||
final long throttleTimeout = System.currentTimeMillis() + 10;
|
||||
when(mService.logEvent(any())).thenReturn(throttleTimeout, 0L);
|
||||
|
||||
mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
mLog.logEvent(3, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
Thread.sleep(100);
|
||||
mLog.logEvent(53, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
|
||||
List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1);
|
||||
assertEventsEqual(expectedEvent(1), gotEvents.get(0));
|
||||
|
||||
verify(mService, times(1)).logEvents(evArrayCaptor.capture());
|
||||
ConnectivityMetricsEvent[] gotOtherEvents = evArrayCaptor.getAllValues().get(0);
|
||||
assertEquals(ConnectivityMetricsLogger.TAG_SKIPPED_EVENTS, gotOtherEvents[0].eventTag);
|
||||
assertEventsEqual(expectedEvent(53), gotOtherEvents[1]);
|
||||
}
|
||||
|
||||
List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception {
|
||||
verify(mService, times(n)).logEvent(evCaptor.capture());
|
||||
return evCaptor.getAllValues();
|
||||
}
|
||||
|
||||
static ConnectivityMetricsEvent expectedEvent(int timestamp) {
|
||||
return new ConnectivityMetricsEvent((long)timestamp, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
|
||||
}
|
||||
|
||||
/** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */
|
||||
static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) {
|
||||
assertEquals(expected.timestamp, got.timestamp);
|
||||
assertEquals(expected.componentTag, got.componentTag);
|
||||
assertEquals(expected.eventTag, got.eventTag);
|
||||
assertEquals(expected.data, got.data);
|
||||
}
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016, 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.server.connectivity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityMetricsEvent;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import static android.net.ConnectivityMetricsEvent.Reference;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - allow overriding MetricsLoggerService constants in tests.
|
||||
* - test intents are correctly sent after the notification threshold.
|
||||
* - test oldest events are correctly pushed out when internal deque is full.
|
||||
* - test throttling triggers correctly.
|
||||
*/
|
||||
public class MetricsLoggerServiceTest extends TestCase {
|
||||
|
||||
static final int COMPONENT_TAG = 1;
|
||||
static final long N_EVENTS = 10L;
|
||||
static final ConnectivityMetricsEvent EVENTS[] = new ConnectivityMetricsEvent[(int)N_EVENTS];
|
||||
static {
|
||||
for (int i = 0; i < N_EVENTS; i++) {
|
||||
EVENTS[i] = new ConnectivityMetricsEvent(i, COMPONENT_TAG, i, new Bundle());
|
||||
}
|
||||
}
|
||||
|
||||
static final ConnectivityMetricsEvent NO_EVENTS[] = new ConnectivityMetricsEvent[0];
|
||||
|
||||
@Mock Context mContext;
|
||||
MetricsLoggerService mService;
|
||||
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mService = new MetricsLoggerService(mContext);
|
||||
mService.onStart();
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testGetNoEvents() throws Exception {
|
||||
Reference r = new Reference(0);
|
||||
assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
|
||||
assertEquals(0, r.getValue());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLogAndGetEvents() throws Exception {
|
||||
mService.mBinder.logEvents(EVENTS);
|
||||
|
||||
Reference r = new Reference(0);
|
||||
|
||||
assertArrayEquals(EVENTS, mService.mBinder.getEvents(r));
|
||||
assertEquals(N_EVENTS, r.getValue());
|
||||
|
||||
assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
|
||||
assertEquals(N_EVENTS, r.getValue());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLogOneByOne() throws Exception {
|
||||
for (ConnectivityMetricsEvent ev : EVENTS) {
|
||||
mService.mBinder.logEvent(ev);
|
||||
}
|
||||
|
||||
Reference r = new Reference(0);
|
||||
|
||||
assertArrayEquals(EVENTS, mService.mBinder.getEvents(r));
|
||||
assertEquals(N_EVENTS, r.getValue());
|
||||
|
||||
assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
|
||||
assertEquals(N_EVENTS, r.getValue());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testInterleavedLogAndGet() throws Exception {
|
||||
mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 0, 3));
|
||||
|
||||
Reference r = new Reference(0);
|
||||
|
||||
assertArrayEquals(Arrays.copyOfRange(EVENTS, 0, 3), mService.mBinder.getEvents(r));
|
||||
assertEquals(3, r.getValue());
|
||||
|
||||
mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 3, 8));
|
||||
mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 8, 10));
|
||||
|
||||
assertArrayEquals(Arrays.copyOfRange(EVENTS, 3, 10), mService.mBinder.getEvents(r));
|
||||
assertEquals(N_EVENTS, r.getValue());
|
||||
|
||||
assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
|
||||
assertEquals(N_EVENTS, r.getValue());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testMultipleGetAll() throws Exception {
|
||||
mService.mBinder.logEvents(Arrays.copyOf(EVENTS, 3));
|
||||
|
||||
Reference r1 = new Reference(0);
|
||||
assertArrayEquals(Arrays.copyOf(EVENTS, 3), mService.mBinder.getEvents(r1));
|
||||
assertEquals(3, r1.getValue());
|
||||
|
||||
mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 3, 10));
|
||||
|
||||
Reference r2 = new Reference(0);
|
||||
assertArrayEquals(EVENTS, mService.mBinder.getEvents(r2));
|
||||
assertEquals(N_EVENTS, r2.getValue());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLogAndDumpConcurrently() throws Exception {
|
||||
for (int i = 0; i < 50; i++) {
|
||||
mContext = null;
|
||||
mService = null;
|
||||
setUp();
|
||||
logAndDumpConcurrently();
|
||||
}
|
||||
}
|
||||
|
||||
public void logAndDumpConcurrently() throws Exception {
|
||||
final CountDownLatch latch = new CountDownLatch((int)N_EVENTS);
|
||||
final FileDescriptor fd = new FileOutputStream("/dev/null").getFD();
|
||||
|
||||
for (ConnectivityMetricsEvent ev : EVENTS) {
|
||||
new Thread() {
|
||||
public void run() {
|
||||
mService.mBinder.logEvent(ev);
|
||||
latch.countDown();
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
new Thread() {
|
||||
public void run() {
|
||||
while (latch.getCount() > 0) {
|
||||
mService.mBinder.dump(fd, new String[]{"--all"});
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
||||
latch.await(100, TimeUnit.MILLISECONDS);
|
||||
|
||||
Reference r = new Reference(0);
|
||||
ConnectivityMetricsEvent[] got = mService.mBinder.getEvents(r);
|
||||
Arrays.sort(got, new EventComparator());
|
||||
assertArrayEquals(EVENTS, got);
|
||||
assertEquals(N_EVENTS, r.getValue());
|
||||
}
|
||||
|
||||
static class EventComparator implements Comparator<ConnectivityMetricsEvent> {
|
||||
public int compare(ConnectivityMetricsEvent ev1, ConnectivityMetricsEvent ev2) {
|
||||
return Long.compare(ev1.timestamp, ev2.timestamp);
|
||||
}
|
||||
public boolean equal(Object o) {
|
||||
return o instanceof EventComparator;
|
||||
}
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user