Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21b6b821 authored by Hugo Benichi's avatar Hugo Benichi Committed by android-build-merger
Browse files

Merge changes I9b4e55c2,I429562a0 into nyc-mr1-dev am: 41a3acc5

am: ca9a575a

Change-Id: I948403eb8ff92b2de87d8ce402921b3ede7e0b82
parents d9feb6cc ca9a575a
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;

/** {@hide} */
@SystemApi
public class ConnectivityMetricsLogger {
@@ -49,8 +51,14 @@ public class ConnectivityMetricsLogger {
    private int mNumSkippedEvents;

    public ConnectivityMetricsLogger() {
        mService = IConnectivityMetricsLogger.Stub.asInterface(ServiceManager.getService(
                CONNECTIVITY_METRICS_LOGGER_SERVICE));
        this(IConnectivityMetricsLogger.Stub.asInterface(
                ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE)));
    }

    /** {@hide} */
    @VisibleForTesting
    public ConnectivityMetricsLogger(IConnectivityMetricsLogger service) {
        mService = service;
    }

    /**
@@ -153,11 +161,10 @@ public class ConnectivityMetricsLogger {
    public boolean unregister(PendingIntent newEventsIntent) {
        try {
            mService.unregister(newEventsIntent);
            return true;
        } catch (RemoteException e) {
            Log.e(TAG, "IConnectivityMetricsLogger.unregister", e);
            return false;
        }

        return true;
    }
}
+12 −1
Original line number Diff line number Diff line
@@ -18,18 +18,29 @@ package android.net.metrics;

import android.net.ConnectivityMetricsEvent;
import android.net.ConnectivityMetricsLogger;
import android.net.IConnectivityMetricsLogger;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;

/**
 * Specialization of the ConnectivityMetricsLogger class for recording IP connectivity events.
 * {@hide}
 */
class IpConnectivityLog extends ConnectivityMetricsLogger {
public class IpConnectivityLog extends ConnectivityMetricsLogger {
    private static String TAG = "IpConnectivityMetricsLogger";
    private static final boolean DBG = false;

    public IpConnectivityLog() {
    }

    @VisibleForTesting
    public IpConnectivityLog(IConnectivityMetricsLogger service) {
        super(service);
    }

    /**
     * Log an IpConnectivity event. Contrary to logEvent(), this method does not
     * keep track of skipped events and is thread-safe for callers.
+12 −12
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.connectivity;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemService;

import android.app.PendingIntent;
@@ -60,17 +61,11 @@ public class MetricsLoggerService extends SystemService {
        }
    }

    // TODO: read from system property
    private final int MAX_NUMBER_OF_EVENTS = 1000;

    // TODO: read from system property
    // TODO: read these constants from system property
    private final int EVENTS_NOTIFICATION_THRESHOLD                   = 300;

    // TODO: read from system property
    private final int THROTTLING_TIME_INTERVAL_MILLIS = 60 * 60 * 1000; // 1 hour

    // TODO: read from system property
    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;

@@ -127,10 +122,13 @@ public class MetricsLoggerService extends SystemService {
        mEvents.addLast(e);
    }

    @VisibleForTesting
    final MetricsLoggerImpl mBinder = new MetricsLoggerImpl();

    /**
     * Implementation of the IConnectivityMetricsLogger interface.
     */
    private final IConnectivityMetricsLogger.Stub mBinder = new IConnectivityMetricsLogger.Stub() {
    final class MetricsLoggerImpl extends IConnectivityMetricsLogger.Stub {

        private final ArrayList<PendingIntent> mPendingIntents = new ArrayList<>();

@@ -223,8 +221,10 @@ public class MetricsLoggerService extends SystemService {
            }

            pw.println();
            if (mDnsListener != null) {
                mDnsListener.dump(pw);
            }
        }

        public long logEvent(ConnectivityMetricsEvent event) {
            ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event};
+165 −0
Original line number Diff line number Diff line
/*
 * 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.metrics;

import android.os.Parcel;
import android.os.Parcelable;
import android.net.ConnectivityMetricsEvent;
import android.net.IConnectivityMetricsLogger;

import junit.framework.TestCase;
import org.junit.Before;
import org.junit.Test;

import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.List;

public class IpConnectivityLogTest extends TestCase {

    static class FakeEvent extends IpConnectivityEvent implements Parcelable {
        public int describeContents() { return 0; }
        public void writeToParcel(Parcel p, int flag) { }
    }
    static final FakeEvent FAKE_EV = new FakeEvent();

    @Mock IConnectivityMetricsLogger mService;
    ArgumentCaptor<ConnectivityMetricsEvent> evCaptor;

    IpConnectivityLog mLog;

    public void setUp() {
        MockitoAnnotations.initMocks(this);
        evCaptor = ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
        mLog = new IpConnectivityLog(mService);
    }

    public void testLogEvents() throws Exception {
        assertTrue(mLog.log(1, FAKE_EV));
        assertTrue(mLog.log(2, FAKE_EV));
        assertTrue(mLog.log(3, 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));
    }

    public void testLogEventTriggerThrottling() throws Exception {
        when(mService.logEvent(any())).thenReturn(1234L);

        assertFalse(mLog.log(1, FAKE_EV));
    }

    public void testLogEventFails() throws Exception {
        when(mService.logEvent(any())).thenReturn(-1L); // Error.

        assertFalse(mLog.log(1, FAKE_EV));
    }

    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.
        assertFalse(mLog.log(1, FAKE_EV));
        assertFalse(mLog.log(2, FAKE_EV));

        List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1);
        assertEventsEqual(expectedEvent(1), gotEvents.get(0));
    }

    public void testLogEventRecoverFromThrottling() throws Exception {
        final long throttleTimeout = System.currentTimeMillis() + 50;
        when(mService.logEvent(any())).thenReturn(throttleTimeout, 0L);

        assertFalse(mLog.log(1, FAKE_EV));
        new Thread() {
            public void run() {
                busySpinLog(FAKE_EV);
            }
        }.start();

        List<ConnectivityMetricsEvent> gotEvents = verifyEvents(2, 200);
        assertEventsEqual(expectedEvent(1), gotEvents.get(0));
        assertEventsEqual(expectedEvent(2), gotEvents.get(1));
    }

    public void testLogEventRecoverFromThrottlingWithMultipleCallers() throws Exception {
        final long throttleTimeout = System.currentTimeMillis() + 50;
        when(mService.logEvent(any())).thenReturn(throttleTimeout, 0L);

        assertFalse(mLog.log(1, FAKE_EV));
        final int nCallers = 10;
        for (int i = 0; i < nCallers; i++) {
            new Thread() {
                public void run() {
                    busySpinLog(FAKE_EV);
                }
            }.start();
        }

        List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1 + nCallers, 200);
        assertEventsEqual(expectedEvent(1), gotEvents.get(0));
        for (int i = 0; i < nCallers; i++) {
            assertEventsEqual(expectedEvent(2), gotEvents.get(1 + i));
        }
    }

    void busySpinLog(Parcelable ev) {
        final long timeout = 200;
        final long stop = System.currentTimeMillis() + timeout;
        try {
            while (System.currentTimeMillis() < stop) {
                if (mLog.log(2, FAKE_EV)) {
                    return;
                }
                Thread.sleep(10);
            }
        } catch (InterruptedException e) { }
    }

    List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception {
        verify(mService, times(n)).logEvent(evCaptor.capture());
        return evCaptor.getAllValues();
    }

    List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
        verify(mService, timeout(timeoutMs).times(n)).logEvent(evCaptor.capture());
        return evCaptor.getAllValues();
    }

    static ConnectivityMetricsEvent expectedEvent(int timestamp) {
        return new ConnectivityMetricsEvent((long)timestamp, 0, 0, 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);
    }
}
+181 −0
Original line number Diff line number Diff line
/*
 * 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 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();
    }

    public void testGetNoEvents() throws Exception {
        Reference r = new Reference(0);
        assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
        assertEquals(0, r.getValue());
    }

    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());
    }

    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());
    }

    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());
    }

    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());
    }

    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;
        }
    };
}