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

This commit is contained in:
Bernardo Rufino
2018-01-17 15:39:45 +00:00
committed by Android (Google) Code Review
5 changed files with 199 additions and 7 deletions

View File

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

View File

@@ -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 {

View File

@@ -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)
# ---------------------------

View File

@@ -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);

View File

@@ -0,0 +1,71 @@
/*
* 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;
}
}
}