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

Commit 4e588baa authored by Bernardo Rufino's avatar Bernardo Rufino Committed by Android (Google) Code Review
Browse files

Merge "Move event logging to TransportClient and add connection event"

parents ec267612 aa56a6cd
Loading
Loading
Loading
Loading
+0 −5
Original line number Diff line number Diff line
@@ -29,14 +29,12 @@ import android.content.pm.ResolveInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.backup.transport.OnTransportRegisteredListener;
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportClientManager;
@@ -574,8 +572,6 @@ public class TransportManager {
            return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
        }

        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 1);

        int result;
        try {
            String transportName = transport.name();
@@ -587,7 +583,6 @@ public class TransportManager {
            result = BackupManager.SUCCESS;
        } catch (RemoteException e) {
            Slog.e(TAG, "Transport " + transportString + " died while registering");
            EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 0);
            result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
        }

+45 −0
Original line number Diff line number Diff line
@@ -29,12 +29,14 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.backup.TransportManager;

import java.lang.annotation.Retention;
@@ -419,10 +421,45 @@ public class TransportClient {
    @GuardedBy("mStateLock")
    private void setStateLocked(@State int state, @Nullable IBackupTransport transport) {
        log(Log.VERBOSE, "State: " + stateToString(mState) + " => " + stateToString(state));
        onStateTransition(mState, state);
        mState = state;
        mTransport = transport;
    }

    private void onStateTransition(int oldState, int newState) {
        String transport = mTransportComponent.flattenToShortString();
        int bound = transitionThroughState(oldState, newState, State.BOUND_AND_CONNECTING);
        int connected = transitionThroughState(oldState, newState, State.CONNECTED);
        if (bound != Transition.NO_TRANSITION) {
            int value = (bound == Transition.UP) ? 1 : 0; // 1 is bound, 0 is not bound
            EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transport, value);
        }
        if (connected != Transition.NO_TRANSITION) {
            int value = (connected == Transition.UP) ? 1 : 0; // 1 is connected, 0 is not connected
            EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_CONNECTION, transport, value);
        }
    }

    /**
     * Returns:
     *
     * <ul>
     *   <li>{@link Transition#UP}, if oldState < stateReference <= newState
     *   <li>{@link Transition#DOWN}, if oldState >= stateReference > newState
     *   <li>{@link Transition#NO_TRANSITION}, otherwise
     */
    @Transition
    private int transitionThroughState(
            @State int oldState, @State int newState, @State int stateReference) {
        if (oldState < stateReference && stateReference <= newState) {
            return Transition.UP;
        }
        if (oldState >= stateReference && stateReference > newState) {
            return Transition.DOWN;
        }
        return Transition.NO_TRANSITION;
    }

    @GuardedBy("mStateLock")
    private void checkStateIntegrityLocked() {
        switch (mState) {
@@ -481,6 +518,14 @@ public class TransportClient {
        // CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
    }

    @IntDef({Transition.DOWN, Transition.NO_TRANSITION, Transition.UP})
    @Retention(RetentionPolicy.SOURCE)
    private @interface Transition {
        int DOWN = -1;
        int NO_TRANSITION = 0;
        int UP = 1;
    }

    @IntDef({State.UNUSABLE, State.IDLE, State.BOUND_AND_CONNECTING, State.CONNECTED})
    @Retention(RetentionPolicy.SOURCE)
    private @interface State {
+1 −0
Original line number Diff line number Diff line
@@ -133,6 +133,7 @@ option java_package com.android.server
2846 full_backup_cancelled (Package|3),(Message|3)

2850 backup_transport_lifecycle (Transport|3),(Bound|1|1)
2851 backup_transport_connection (Transport|3),(Connected|1|1)


# ---------------------------
+82 −2
Original line number Diff line number Diff line
@@ -35,8 +35,10 @@ import android.os.Looper;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
import com.android.server.backup.TransportManager;
import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.ShadowEventLog;
import com.android.server.testing.SystemLoaderClasses;
import org.junit.Before;
import org.junit.Test;
@@ -48,7 +50,7 @@ import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;

@RunWith(FrameworkRobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 26)
@Config(manifest = Config.NONE, sdk = 26, shadows = {ShadowEventLog.class})
@SystemLoaderClasses({TransportManager.class, TransportClient.class})
@Presubmit
public class TransportClientTest {
@@ -60,6 +62,7 @@ public class TransportClientTest {
    @Mock private IBackupTransport.Stub mIBackupTransport;
    private TransportClient mTransportClient;
    private ComponentName mTransportComponent;
    private String mTransportString;
    private Intent mBindIntent;
    private ShadowLooper mShadowLooper;

@@ -71,6 +74,7 @@ public class TransportClientTest {
        mShadowLooper = shadowOf(mainLooper);
        mTransportComponent =
                new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport");
        mTransportString = mTransportComponent.flattenToShortString();
        mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
        mTransportClient =
                new TransportClient(
@@ -161,7 +165,7 @@ public class TransportClientTest {
    }

    @Test
    public void testConnectAsync_whenFrameworkDoesntBind_releasesConnection() throws Exception {
    public void testConnectAsync_whenFrameworkDoesNotBind_releasesConnection() throws Exception {
        when(mContext.bindServiceAsUser(
                        eq(mBindIntent),
                        any(ServiceConnection.class),
@@ -234,6 +238,82 @@ public class TransportClientTest {
                .onTransportConnectionResult(isNull(), eq(mTransportClient));
    }

    @Test
    public void testConnectAsync_beforeFrameworkCall_logsBoundTransition() {
        ShadowEventLog.clearEvents();

        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");

        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
    }

    @Test
    public void testConnectAsync_afterOnServiceConnected_logsBoundAndConnectedTransitions() {
        ShadowEventLog.clearEvents();

        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
        connection.onServiceConnected(mTransportComponent, mIBackupTransport);

        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 1);
    }

    @Test
    public void testConnectAsync_afterOnBindingDied_logsBoundAndUnboundTransitions() {
        ShadowEventLog.clearEvents();

        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
        connection.onBindingDied(mTransportComponent);

        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
    }

    @Test
    public void testUnbind_whenConnected_logsDisconnectedAndUnboundTransitions() {
        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
        connection.onServiceConnected(mTransportComponent, mIBackupTransport);
        ShadowEventLog.clearEvents();

        mTransportClient.unbind("caller1");

        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
    }

    @Test
    public void testOnServiceDisconnected_whenConnected_logsDisconnectedAndUnboundTransitions() {
        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
        connection.onServiceConnected(mTransportComponent, mIBackupTransport);
        ShadowEventLog.clearEvents();

        connection.onServiceDisconnected(mTransportComponent);

        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
    }

    @Test
    public void testOnBindingDied_whenConnected_logsDisconnectedAndUnboundTransitions() {
        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
        connection.onServiceConnected(mTransportComponent, mIBackupTransport);
        ShadowEventLog.clearEvents();

        connection.onBindingDied(mTransportComponent);

        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
    }

    private void assertEventLogged(int tag, Object... values) {
        assertThat(ShadowEventLog.hasEvent(tag, values)).isTrue();
    }

    private ServiceConnection verifyBindServiceAsUserAndCaptureServiceConnection(Context context) {
        ArgumentCaptor<ServiceConnection> connectionCaptor =
                ArgumentCaptor.forClass(ServiceConnection.class);
+71 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.testing;

import android.util.EventLog;

import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;

@Implements(EventLog.class)
public class ShadowEventLog {
    private final static LinkedHashSet<Entry> ENTRIES = new LinkedHashSet<>();

    @Implementation
    public static int writeEvent(int tag, Object... values) {
        ENTRIES.add(new Entry(tag, Arrays.asList(values)));
        // Currently we don't care about the return value, if we do, estimate it correctly
        return 0;
    }

    public static boolean hasEvent(int tag, Object... values) {
        return ENTRIES.contains(new Entry(tag, Arrays.asList(values)));
    }

    public static void clearEvents() {
        ENTRIES.clear();
    }

    public static class Entry {
        public final int tag;
        public final List<Object> values;

        public Entry(int tag, List<Object> values) {
            this.tag = tag;
            this.values = values;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Entry entry = (Entry) o;
            return tag == entry.tag && values.equals(entry.values);
        }

        @Override
        public int hashCode() {
            int result = tag;
            result = 31 * result + values.hashCode();
            return result;
        }
    }
}