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 17 package com.android.server.backup.testing; 18 19 import static com.android.server.backup.testing.TestUtils.uncheck; 20 21 import static org.junit.Assert.fail; 22 import static org.mockito.ArgumentMatchers.any; 23 import static org.mockito.ArgumentMatchers.eq; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.when; 26 27 import static java.util.stream.Collectors.toList; 28 29 import android.annotation.IntDef; 30 import android.annotation.Nullable; 31 import android.content.ComponentName; 32 import android.content.Intent; 33 import android.content.pm.ResolveInfo; 34 import android.content.pm.ServiceInfo; 35 import android.os.RemoteException; 36 37 import com.android.server.backup.TransportManager; 38 import com.android.server.backup.transport.BackupTransportClient; 39 import com.android.server.backup.transport.TransportConnection; 40 import com.android.server.backup.transport.TransportNotAvailableException; 41 import com.android.server.backup.transport.TransportNotRegisteredException; 42 43 import org.robolectric.shadows.ShadowPackageManager; 44 45 import java.lang.annotation.Retention; 46 import java.lang.annotation.RetentionPolicy; 47 import java.util.List; 48 import java.util.stream.Stream; 49 50 public class TransportTestUtils { 51 /** 52 * Differently from {@link #setUpTransports(TransportManager, TransportData...)}, which 53 * configures {@link TransportManager}, this is meant to mock the environment for a real 54 * TransportManager. 55 */ setUpTransportsForTransportManager( ShadowPackageManager shadowPackageManager, TransportData... transports)56 public static void setUpTransportsForTransportManager( 57 ShadowPackageManager shadowPackageManager, TransportData... transports) 58 throws Exception { 59 for (TransportData transport : transports) { 60 if (transport.transportStatus == TransportStatus.UNREGISTERED) { 61 continue; 62 } 63 ComponentName transportComponent = transport.getTransportComponent(); 64 String packageName = transportComponent.getPackageName(); 65 ResolveInfo resolveInfo = resolveInfo(transportComponent); 66 shadowPackageManager.addResolveInfoForIntent(transportIntent(), resolveInfo); 67 shadowPackageManager.addResolveInfoForIntent( 68 transportIntent().setPackage(packageName), resolveInfo); 69 } 70 } 71 transportIntent()72 private static Intent transportIntent() { 73 return new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST); 74 } 75 resolveInfo(ComponentName transportComponent)76 private static ResolveInfo resolveInfo(ComponentName transportComponent) { 77 ResolveInfo resolveInfo = new ResolveInfo(); 78 resolveInfo.serviceInfo = new ServiceInfo(); 79 resolveInfo.serviceInfo.packageName = transportComponent.getPackageName(); 80 resolveInfo.serviceInfo.name = transportComponent.getClassName(); 81 return resolveInfo; 82 } 83 84 /** {@code transportName} has to be in the {@link ComponentName} format (with '/') */ setUpCurrentTransport( TransportManager transportManager, TransportData transport)85 public static TransportMock setUpCurrentTransport( 86 TransportManager transportManager, TransportData transport) throws Exception { 87 TransportMock transportMock = setUpTransport(transportManager, transport); 88 int status = transport.transportStatus; 89 when(transportManager.getCurrentTransportName()).thenReturn(transport.transportName); 90 if (status == TransportStatus.REGISTERED_AVAILABLE 91 || status == TransportStatus.REGISTERED_UNAVAILABLE) { 92 // Transport registered 93 when(transportManager.getCurrentTransportClient(any())) 94 .thenReturn(transportMock.mTransportConnection); 95 when(transportManager.getCurrentTransportClientOrThrow(any())) 96 .thenReturn(transportMock.mTransportConnection); 97 } else { 98 // Transport not registered 99 when(transportManager.getCurrentTransportClient(any())).thenReturn(null); 100 when(transportManager.getCurrentTransportClientOrThrow(any())) 101 .thenThrow(TransportNotRegisteredException.class); 102 } 103 return transportMock; 104 } 105 106 /** @see #setUpTransport(TransportManager, TransportData) */ setUpTransports( TransportManager transportManager, TransportData... transports)107 public static List<TransportMock> setUpTransports( 108 TransportManager transportManager, TransportData... transports) throws Exception { 109 return Stream.of(transports) 110 .map(transport -> uncheck(() -> setUpTransport(transportManager, transport))) 111 .collect(toList()); 112 } 113 setUpTransport( TransportManager transportManager, TransportData transport)114 public static TransportMock setUpTransport( 115 TransportManager transportManager, TransportData transport) throws Exception { 116 int status = transport.transportStatus; 117 String transportName = transport.transportName; 118 ComponentName transportComponent = transport.getTransportComponent(); 119 String transportDirName = transport.transportDirName; 120 121 TransportMock transportMock = mockTransport(transport); 122 if (status == TransportStatus.REGISTERED_AVAILABLE 123 || status == TransportStatus.REGISTERED_UNAVAILABLE) { 124 // Transport registered 125 when(transportManager.getTransportClient(eq(transportName), any())) 126 .thenReturn(transportMock.mTransportConnection); 127 when(transportManager.getTransportClientOrThrow(eq(transportName), any())) 128 .thenReturn(transportMock.mTransportConnection); 129 when(transportManager.getTransportName(transportComponent)).thenReturn(transportName); 130 when(transportManager.getTransportDirName(eq(transportName))) 131 .thenReturn(transportDirName); 132 when(transportManager.getTransportDirName(eq(transportComponent))) 133 .thenReturn(transportDirName); 134 when(transportManager.isTransportRegistered(eq(transportName))).thenReturn(true); 135 // TODO: Mock rest of description methods 136 } else { 137 // Transport not registered 138 when(transportManager.getTransportClient(eq(transportName), any())).thenReturn(null); 139 when(transportManager.getTransportClientOrThrow(eq(transportName), any())) 140 .thenThrow(TransportNotRegisteredException.class); 141 when(transportManager.getTransportName(transportComponent)) 142 .thenThrow(TransportNotRegisteredException.class); 143 when(transportManager.getTransportDirName(eq(transportName))) 144 .thenThrow(TransportNotRegisteredException.class); 145 when(transportManager.getTransportDirName(eq(transportComponent))) 146 .thenThrow(TransportNotRegisteredException.class); 147 when(transportManager.isTransportRegistered(eq(transportName))).thenReturn(false); 148 } 149 return transportMock; 150 } 151 mockTransport(TransportData transport)152 public static TransportMock mockTransport(TransportData transport) throws Exception { 153 final TransportConnection transportConnectionMock; 154 int status = transport.transportStatus; 155 ComponentName transportComponent = transport.getTransportComponent(); 156 if (status == TransportStatus.REGISTERED_AVAILABLE 157 || status == TransportStatus.REGISTERED_UNAVAILABLE) { 158 // Transport registered 159 transportConnectionMock = mock(TransportConnection.class); 160 when(transportConnectionMock.getTransportComponent()).thenReturn(transportComponent); 161 if (status == TransportStatus.REGISTERED_AVAILABLE) { 162 // Transport registered and available 163 BackupTransportClient transportMock = mockTransportBinder(transport); 164 when(transportConnectionMock.connectOrThrow(any())).thenReturn(transportMock); 165 when(transportConnectionMock.connect(any())).thenReturn(transportMock); 166 167 return new TransportMock(transport, transportConnectionMock, transportMock); 168 } else { 169 // Transport registered but unavailable 170 when(transportConnectionMock.connectOrThrow(any())) 171 .thenThrow(TransportNotAvailableException.class); 172 when(transportConnectionMock.connect(any())).thenReturn(null); 173 174 return new TransportMock(transport, transportConnectionMock, null); 175 } 176 } else { 177 // Transport not registered 178 return new TransportMock(transport, null, null); 179 } 180 } 181 mockTransportBinder(TransportData transport)182 private static BackupTransportClient mockTransportBinder(TransportData transport) 183 throws Exception { 184 BackupTransportClient transportBinder = mock(BackupTransportClient.class); 185 try { 186 when(transportBinder.name()).thenReturn(transport.transportName); 187 when(transportBinder.transportDirName()).thenReturn(transport.transportDirName); 188 when(transportBinder.configurationIntent()).thenReturn(transport.configurationIntent); 189 when(transportBinder.currentDestinationString()) 190 .thenReturn(transport.currentDestinationString); 191 when(transportBinder.dataManagementIntent()).thenReturn(transport.dataManagementIntent); 192 when(transportBinder.dataManagementIntentLabel()) 193 .thenReturn(transport.dataManagementLabel); 194 } catch (RemoteException e) { 195 fail("RemoteException?"); 196 } 197 return transportBinder; 198 } 199 200 public static class TransportMock { 201 public final TransportData transportData; 202 @Nullable public final TransportConnection mTransportConnection; 203 @Nullable public final BackupTransportClient transport; 204 TransportMock( TransportData transportData, @Nullable TransportConnection transportConnection, @Nullable BackupTransportClient transport)205 private TransportMock( 206 TransportData transportData, 207 @Nullable TransportConnection transportConnection, 208 @Nullable BackupTransportClient transport) { 209 this.transportData = transportData; 210 this.mTransportConnection = transportConnection; 211 this.transport = transport; 212 } 213 } 214 215 @IntDef({ 216 TransportStatus.REGISTERED_AVAILABLE, 217 TransportStatus.REGISTERED_UNAVAILABLE, 218 TransportStatus.UNREGISTERED 219 }) 220 @Retention(RetentionPolicy.SOURCE) 221 public @interface TransportStatus { 222 int REGISTERED_AVAILABLE = 0; 223 int REGISTERED_UNAVAILABLE = 1; 224 int UNREGISTERED = 2; 225 } 226 TransportTestUtils()227 private TransportTestUtils() {} 228 } 229