1/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.backup.transport;
18
19import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
20import static org.mockito.ArgumentMatchers.any;
21import static org.mockito.ArgumentMatchers.anyInt;
22import static org.mockito.Mockito.argThat;
23import static org.mockito.Mockito.spy;
24import static org.mockito.Mockito.verify;
25import static org.mockito.Mockito.when;
26
27import android.content.ComponentName;
28import android.content.Context;
29import android.content.Intent;
30import android.content.ServiceConnection;
31import android.os.Bundle;
32import android.os.UserHandle;
33import android.platform.test.annotations.Presubmit;
34
35import com.android.server.testing.FrameworkRobolectricTestRunner;
36import com.android.server.testing.SystemLoaderPackages;
37import org.junit.Before;
38import org.junit.Test;
39import org.junit.runner.RunWith;
40import org.mockito.ArgumentMatcher;
41import org.mockito.Mock;
42import org.mockito.MockitoAnnotations;
43import org.robolectric.annotation.Config;
44
45@RunWith(FrameworkRobolectricTestRunner.class)
46@Config(manifest = Config.NONE, sdk = 26)
47@SystemLoaderPackages({"com.android.server.backup"})
48@Presubmit
49public class TransportClientManagerTest {
50    private static final String PACKAGE_NAME = "random.package.name";
51    private static final String CLASS_NAME = "random.package.name.transport.Transport";
52
53    @Mock private Context mContext;
54    @Mock private TransportConnectionListener mTransportConnectionListener;
55    private TransportClientManager mTransportClientManager;
56    private ComponentName mTransportComponent;
57    private Intent mBindIntent;
58
59    @Before
60    public void setUp() {
61        MockitoAnnotations.initMocks(this);
62
63        mTransportClientManager = new TransportClientManager(mContext, new TransportStats());
64        mTransportComponent = new ComponentName(PACKAGE_NAME, CLASS_NAME);
65        mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
66
67        when(mContext.bindServiceAsUser(
68                        any(Intent.class),
69                        any(ServiceConnection.class),
70                        anyInt(),
71                        any(UserHandle.class)))
72                .thenReturn(true);
73    }
74
75    @Test
76    public void testGetTransportClient() {
77        TransportClient transportClient =
78                mTransportClientManager.getTransportClient(mTransportComponent, "caller");
79
80        // Connect to be able to extract the intent
81        transportClient.connectAsync(mTransportConnectionListener, "caller");
82        verify(mContext)
83                .bindServiceAsUser(
84                        argThat(matchesIntentAndExtras(mBindIntent)),
85                        any(ServiceConnection.class),
86                        anyInt(),
87                        any(UserHandle.class));
88    }
89
90    @Test
91    public void testGetTransportClient_withExtras_createsTransportClientWithCorrectIntent() {
92        Bundle extras = new Bundle();
93        extras.putBoolean("random_extra", true);
94
95        TransportClient transportClient =
96                mTransportClientManager.getTransportClient(mTransportComponent, extras, "caller");
97
98        transportClient.connectAsync(mTransportConnectionListener, "caller");
99        mBindIntent.putExtras(extras);
100        verify(mContext)
101                .bindServiceAsUser(
102                        argThat(matchesIntentAndExtras(mBindIntent)),
103                        any(ServiceConnection.class),
104                        anyInt(),
105                        any(UserHandle.class));
106    }
107
108    @Test
109    public void testDisposeOfTransportClient() {
110        TransportClient transportClient =
111                spy(mTransportClientManager.getTransportClient(mTransportComponent, "caller"));
112
113        mTransportClientManager.disposeOfTransportClient(transportClient, "caller");
114
115        verify(transportClient).unbind(any());
116        verify(transportClient).markAsDisposed();
117    }
118
119    private ArgumentMatcher<Intent> matchesIntentAndExtras(Intent expectedIntent) {
120        return (Intent actualIntent) -> {
121            if (!expectedIntent.filterEquals(actualIntent)) {
122                return false;
123            }
124
125            Bundle expectedExtras = expectedIntent.getExtras();
126            Bundle actualExtras = actualIntent.getExtras();
127
128            if (expectedExtras == null && actualExtras == null) {
129                return true;
130            }
131
132            if (expectedExtras == null || actualExtras == null) {
133                return false;
134            }
135
136            if (expectedExtras.size() != actualExtras.size()) {
137                return false;
138            }
139
140            for (String key : expectedExtras.keySet()) {
141                if (!expectedExtras.get(key).equals(actualExtras.get(key))) {
142                    return false;
143                }
144            }
145
146            return true;
147        };
148    }
149}
150