1 /* 2 * Copyright (C) 2023 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.tv.mdnsoffloadmanager; 18 19 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; 20 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 21 22 import static com.android.tv.mdnsoffloadmanager.TestHelpers.SERVICE_AIRPLAY; 23 import static com.android.tv.mdnsoffloadmanager.TestHelpers.SERVICE_ATV; 24 import static com.android.tv.mdnsoffloadmanager.TestHelpers.SERVICE_GOOGLECAST; 25 import static com.android.tv.mdnsoffloadmanager.TestHelpers.SERVICE_GTV; 26 import static com.android.tv.mdnsoffloadmanager.TestHelpers.makeIntent; 27 import static com.android.tv.mdnsoffloadmanager.TestHelpers.makeLinkProperties; 28 import static com.android.tv.mdnsoffloadmanager.TestHelpers.makeLowPowerStandbyPolicy; 29 import static com.android.tv.mdnsoffloadmanager.TestHelpers.verifyOffloadedServices; 30 import static com.android.tv.mdnsoffloadmanager.TestHelpers.verifyPassthroughQNames; 31 32 import static org.junit.Assert.assertEquals; 33 import static org.junit.Assert.assertFalse; 34 import static org.junit.Assert.assertNotNull; 35 import static org.junit.Assert.assertTrue; 36 import static org.junit.Assert.fail; 37 import static org.mockito.ArgumentMatchers.any; 38 import static org.mockito.ArgumentMatchers.anyByte; 39 import static org.mockito.ArgumentMatchers.anyInt; 40 import static org.mockito.ArgumentMatchers.anyString; 41 import static org.mockito.ArgumentMatchers.argThat; 42 import static org.mockito.ArgumentMatchers.eq; 43 import static org.mockito.ArgumentMatchers.notNull; 44 import static org.mockito.Mockito.atLeastOnce; 45 import static org.mockito.Mockito.mock; 46 import static org.mockito.Mockito.never; 47 import static org.mockito.Mockito.reset; 48 import static org.mockito.Mockito.verify; 49 import static org.mockito.Mockito.when; 50 51 import android.content.BroadcastReceiver; 52 import android.content.ComponentName; 53 import android.content.Context; 54 import android.content.Intent; 55 import android.content.IntentFilter; 56 import android.content.ServiceConnection; 57 import android.content.pm.PackageManager; 58 import android.content.res.Resources; 59 import android.net.ConnectivityManager; 60 import android.net.ConnectivityManager.NetworkCallback; 61 import android.net.Network; 62 import android.os.IBinder; 63 import android.os.Looper; 64 import android.os.PowerManager; 65 import android.os.RemoteException; 66 import android.os.test.TestLooper; 67 import android.util.Log; 68 69 import androidx.test.filters.SmallTest; 70 71 import com.android.tv.mdnsoffloadmanager.MdnsOffloadManagerService.Injector; 72 import com.android.tv.mdnsoffloadmanager.util.WakeLockWrapper; 73 74 import org.junit.Before; 75 import org.junit.Test; 76 import org.mockito.ArgumentCaptor; 77 import org.mockito.Captor; 78 import org.mockito.Mock; 79 import org.mockito.MockitoAnnotations; 80 import org.mockito.Spy; 81 82 import java.io.PrintWriter; 83 import java.io.StringWriter; 84 import java.util.Arrays; 85 import java.util.List; 86 87 import device.google.atv.mdns_offload.IMdnsOffload.MdnsProtocolData; 88 import device.google.atv.mdns_offload.IMdnsOffload.MdnsProtocolData.MatchCriteria; 89 import device.google.atv.mdns_offload.IMdnsOffload.PassthroughBehavior; 90 import device.google.atv.mdns_offload.IMdnsOffloadManager; 91 92 @SmallTest 93 public class MdnsOffloadManagerTest { 94 95 private static final String TAG = MdnsOffloadManagerTest.class.getSimpleName(); 96 private static final ComponentName VENDOR_SERVICE_COMPONENT = 97 ComponentName.unflattenFromString("test.vendor.offloadservice/.TestOffloadService"); 98 private static final String[] PRIORITY_LIST = { 99 "_googlecast._tcp.local.", 100 "_some._other._svc.local." 101 }; 102 private static final String IFC_0 = "imaginaryif0"; 103 private static final String IFC_1 = "imaginaryif1"; 104 private static final int APP_UID_0 = 1234; 105 private static final int SECONDARY_USER_APP_UID_0 = 101234; 106 private static final int APP_UID_1 = 1235; 107 private static final String APP_PACKAGE_0 = "first.app.package"; 108 private static final String APP_PACKAGE_1 = "some.other.package"; 109 110 @Mock 111 Resources mResources; 112 @Mock 113 IBinder mClientBinder0; 114 @Mock 115 IBinder mClientBinder1; 116 @Mock 117 ConnectivityManager mConnectivityManager; 118 @Mock 119 Network mNetwork0; 120 @Mock 121 Network mNetwork1; 122 @Mock 123 PackageManager mPackageManager; 124 @Mock 125 WakeLockWrapper mWakeLock; 126 @Spy 127 FakeMdnsOffloadService mVendorService = new FakeMdnsOffloadService(); 128 @Captor 129 ArgumentCaptor<NetworkCallback> mNetworkCallbackCaptor; 130 @Captor 131 ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor; 132 133 TestLooper mTestLooper; 134 ServiceConnection mCapturedVendorServiceConnection; 135 BroadcastReceiver mCapturedScreenBroadcastReceiver; 136 BroadcastReceiver mCapturedLowPowerStandbyPolicyReceiver; 137 MdnsOffloadManagerService mOffloadManagerService; 138 IMdnsOffloadManager mOffloadManagerBinder; 139 boolean mIsInteractive; 140 int mCallingUid; 141 PowerManager.LowPowerStandbyPolicy mLowPowerStandbyPolicy; 142 143 @Before setup()144 public void setup() throws PackageManager.NameNotFoundException { 145 mTestLooper = new TestLooper(); 146 MockitoAnnotations.initMocks(this); 147 when(mResources.getString(eq(R.string.config_mdnsOffloadVendorServiceComponent))) 148 .thenReturn(VENDOR_SERVICE_COMPONENT.flattenToShortString()); 149 when(mResources.getStringArray(eq(R.array.config_mdnsOffloadPriorityQnames))) 150 .thenReturn(PRIORITY_LIST); 151 when(mPackageManager.getPackageUid(eq(APP_PACKAGE_0), anyInt())).thenReturn(APP_UID_0); 152 when(mPackageManager.getPackageUid(eq(APP_PACKAGE_1), anyInt())).thenReturn(APP_UID_1); 153 mLowPowerStandbyPolicy = makeLowPowerStandbyPolicy(APP_PACKAGE_0); 154 mCallingUid = APP_UID_0; 155 mIsInteractive = true; 156 } 157 createOffloadManager()158 private void createOffloadManager() { 159 mOffloadManagerService = new MdnsOffloadManagerService(new Injector() { 160 @Override 161 Resources getResources() { 162 return mResources; 163 } 164 165 @Override 166 synchronized Looper getLooper() { 167 return mTestLooper.getLooper(); 168 } 169 170 @Override 171 boolean isInteractive() { 172 return mIsInteractive; 173 } 174 175 @Override 176 int getCallingUid() { 177 return mCallingUid; 178 } 179 180 @Override 181 ConnectivityManager getConnectivityManager() { 182 return mConnectivityManager; 183 } 184 185 @Override 186 PowerManager.LowPowerStandbyPolicy getLowPowerStandbyPolicy() { 187 return mLowPowerStandbyPolicy; 188 } 189 190 @Override 191 WakeLockWrapper newWakeLock() { 192 return mWakeLock; 193 } 194 195 @Override 196 PackageManager getPackageManager() { 197 return mPackageManager; 198 } 199 200 @Override 201 boolean bindService(Intent intent, ServiceConnection connection, int flags) { 202 if (!VENDOR_SERVICE_COMPONENT.equals(intent.getComponent())) { 203 fail("MDNS offload manager is expected to bind to the component provided in " + 204 "the resources."); 205 } 206 if (flags != Context.BIND_AUTO_CREATE) { 207 fail("MDNS offload manager is expected to set BIND_AUTO_CREATE flag when " + 208 "binding to vendor service."); 209 } 210 mCapturedVendorServiceConnection = connection; 211 return true; 212 } 213 214 @Override 215 void registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) { 216 if (filter.countActions() == 2 && 217 filter.hasAction(Intent.ACTION_SCREEN_ON) && 218 filter.hasAction(Intent.ACTION_SCREEN_OFF)) { 219 mCapturedScreenBroadcastReceiver = receiver; 220 return; 221 } else if (filter.countActions() == 1 && 222 filter.hasAction(PowerManager.ACTION_LOW_POWER_STANDBY_POLICY_CHANGED)) { 223 mCapturedLowPowerStandbyPolicyReceiver = receiver; 224 return; 225 } 226 fail("Unexpected broadcast receiver registered."); 227 } 228 }); 229 230 mOffloadManagerService.onCreate(); 231 mOffloadManagerBinder = (IMdnsOffloadManager) mOffloadManagerService.onBind(null); 232 verify(mConnectivityManager).registerNetworkCallback( 233 argThat(request -> 234 Arrays.stream(request.getTransportTypes()) 235 .boxed() 236 .toList() 237 .containsAll(List.of(TRANSPORT_ETHERNET, TRANSPORT_WIFI))), 238 mNetworkCallbackCaptor.capture()); 239 } 240 bindVendorService()241 private void bindVendorService() { 242 mCapturedVendorServiceConnection.onServiceConnected( 243 VENDOR_SERVICE_COMPONENT, mVendorService); 244 mTestLooper.dispatchAll(); 245 } 246 unbindVendorService()247 private void unbindVendorService() { 248 mCapturedVendorServiceConnection.onServiceDisconnected(VENDOR_SERVICE_COMPONENT); 249 mTestLooper.dispatchAll(); 250 } 251 registerNetwork(Network network, String networkInterface)252 private void registerNetwork(Network network, String networkInterface) { 253 mNetworkCallbackCaptor.getValue().onLinkPropertiesChanged(network, 254 makeLinkProperties(networkInterface)); 255 mTestLooper.dispatchAll(); 256 } 257 unregisterNetwork(Network network)258 private void unregisterNetwork(Network network) { 259 mNetworkCallbackCaptor.getValue().onLost(network); 260 mTestLooper.dispatchAll(); 261 } 262 setupDefaultOffloadManager()263 private void setupDefaultOffloadManager() { 264 createOffloadManager(); 265 bindVendorService(); 266 registerNetwork(mNetwork0, IFC_0); 267 } 268 269 @Test whenCreated_setsListenersAndBindsService()270 public void whenCreated_setsListenersAndBindsService() throws RemoteException { 271 setupDefaultOffloadManager(); 272 273 assertNotNull(mCapturedVendorServiceConnection); 274 assertNotNull(mCapturedScreenBroadcastReceiver); 275 verify(mConnectivityManager).registerNetworkCallback(any(), (NetworkCallback) notNull()); 276 // Vendor offload is reset on binding. 277 verify(mVendorService).resetAll(); 278 assertFalse(mVendorService.mOffloadState); 279 assertEquals(0, mVendorService.getOffloadData(IFC_0).offloadedRecords.size()); 280 } 281 282 283 @Test whenOffloadingRecord_propagatesToVendorService()284 public void whenOffloadingRecord_propagatesToVendorService() throws RemoteException { 285 setupDefaultOffloadManager(); 286 287 int recordKey = mOffloadManagerBinder.addProtocolResponses( 288 IFC_0, SERVICE_ATV, mClientBinder0); 289 mTestLooper.dispatchAll(); 290 291 assertTrue("Expected a valid record key", recordKey > 0); 292 FakeMdnsOffloadService.OffloadData offloadData = mVendorService.getOffloadData(IFC_0); 293 assertEquals(1, offloadData.offloadedRecords.size()); 294 MdnsProtocolData protocolData = offloadData.offloadedRecords.get(0); 295 assertEquals(SERVICE_ATV.rawOffloadPacket, protocolData.rawOffloadPacket); 296 assertEquals(1, protocolData.matchCriteriaList.size()); 297 MatchCriteria matchCriteria = protocolData.matchCriteriaList.get(0); 298 assertEquals(0x01, matchCriteria.type); // Type A response 299 assertEquals(12, matchCriteria.nameOffset); 300 } 301 302 /** 303 * Multiple records must be offloaded in the correct order, as memory for offloaded records is 304 * limited and the first records must take precedence over the subsequent ones. 305 */ 306 @Test offloadingOrderIsMaintained()307 public void offloadingOrderIsMaintained() throws RemoteException { 308 setupDefaultOffloadManager(); 309 310 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 311 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_GTV, mClientBinder0); 312 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_AIRPLAY, mClientBinder0); 313 mTestLooper.dispatchAll(); 314 315 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV, SERVICE_GTV, SERVICE_AIRPLAY); 316 } 317 318 /** 319 * When removing a record, offload of the rest of the records is re-applied in the same order 320 * as before. 321 */ 322 @Test removingOffloadedRecord_maintainsOrder()323 public void removingOffloadedRecord_maintainsOrder() throws RemoteException { 324 setupDefaultOffloadManager(); 325 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 326 int gtvRecordKey = mOffloadManagerBinder.addProtocolResponses( 327 IFC_0, SERVICE_GTV, mClientBinder0); 328 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_AIRPLAY, mClientBinder0); 329 mTestLooper.dispatchAll(); 330 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV, SERVICE_GTV, SERVICE_AIRPLAY); 331 332 mOffloadManagerBinder.removeProtocolResponses(gtvRecordKey, mClientBinder0); 333 mTestLooper.dispatchAll(); 334 335 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV, SERVICE_AIRPLAY); 336 } 337 338 @Test removingInvalidRecordKey_doesNothing()339 public void removingInvalidRecordKey_doesNothing() throws RemoteException { 340 setupDefaultOffloadManager(); 341 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 342 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_GTV, mClientBinder0); 343 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_AIRPLAY, mClientBinder0); 344 mTestLooper.dispatchAll(); 345 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV, SERVICE_GTV, SERVICE_AIRPLAY); 346 347 mOffloadManagerBinder.removeProtocolResponses(1337, mClientBinder0); 348 mTestLooper.dispatchAll(); 349 350 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV, SERVICE_GTV, SERVICE_AIRPLAY); 351 } 352 353 @Test removingRecordHoldingInvalidClientBinder_doesNothing()354 public void removingRecordHoldingInvalidClientBinder_doesNothing() throws RemoteException { 355 setupDefaultOffloadManager(); 356 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 357 int recordKey = mOffloadManagerBinder.addProtocolResponses( 358 IFC_0, SERVICE_GTV, mClientBinder0); 359 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_AIRPLAY, mClientBinder0); 360 mTestLooper.dispatchAll(); 361 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV, SERVICE_GTV, SERVICE_AIRPLAY); 362 363 mOffloadManagerBinder.removeProtocolResponses(recordKey, mClientBinder1); 364 mTestLooper.dispatchAll(); 365 366 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV, SERVICE_GTV, SERVICE_AIRPLAY); 367 } 368 369 @Test recordsFromPriorityListAreOffloadedFirst()370 public void recordsFromPriorityListAreOffloadedFirst() throws RemoteException { 371 setupDefaultOffloadManager(); 372 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 373 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_GTV, mClientBinder0); 374 mTestLooper.dispatchAll(); 375 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV, SERVICE_GTV); 376 377 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_GOOGLECAST, mClientBinder0); 378 mTestLooper.dispatchAll(); 379 380 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_GOOGLECAST, SERVICE_ATV, 381 SERVICE_GTV); 382 } 383 384 @Test whenOutOfMemoryCapacity_priorityListRecordsAreNotEvicted()385 public void whenOutOfMemoryCapacity_priorityListRecordsAreNotEvicted() throws RemoteException { 386 setupDefaultOffloadManager(); 387 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 388 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_GTV, mClientBinder0); 389 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_AIRPLAY, mClientBinder0); 390 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_GOOGLECAST, mClientBinder0); 391 mTestLooper.dispatchAll(); 392 393 verifyOffloadedServices( 394 mVendorService, IFC_0, SERVICE_GOOGLECAST, SERVICE_ATV, SERVICE_GTV); 395 } 396 397 @Test priorityListNamesAreCanonicalized()398 public void priorityListNamesAreCanonicalized() throws RemoteException { 399 when(mResources.getStringArray(eq(R.array.config_mdnsOffloadPriorityQnames))) 400 .thenReturn(new String[]{"_googlecast._tcp.local"}); // Trailing dot is missing. 401 setupDefaultOffloadManager(); 402 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 403 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_GOOGLECAST, mClientBinder0); 404 mTestLooper.dispatchAll(); 405 406 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_GOOGLECAST, SERVICE_ATV); 407 } 408 409 @Test addingToPassthroughList_setsPassthroughBehaviorAndPropagatesToVendorService()410 public void addingToPassthroughList_setsPassthroughBehaviorAndPropagatesToVendorService() 411 throws RemoteException { 412 setupDefaultOffloadManager(); 413 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 414 mTestLooper.dispatchAll(); 415 416 assertEquals( 417 PassthroughBehavior.PASSTHROUGH_LIST, 418 mVendorService.getOffloadData(IFC_0).passthroughBehavior); 419 verifyPassthroughQNames(mVendorService, IFC_0, "atv"); 420 } 421 422 /** 423 * Order of passthrough QNames must be maintained, as the vendor service will drop passthrough 424 * QNames if the chip runs out of memory. 425 */ 426 @Test passthroughOrderIsMaintained()427 public void passthroughOrderIsMaintained() throws RemoteException { 428 setupDefaultOffloadManager(); 429 430 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 431 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 432 mOffloadManagerBinder.addToPassthroughList(IFC_0, "airplay", mClientBinder0); 433 mTestLooper.dispatchAll(); 434 435 assertEquals( 436 PassthroughBehavior.PASSTHROUGH_LIST, 437 mVendorService.getOffloadData(IFC_0).passthroughBehavior); 438 verifyPassthroughQNames(mVendorService, IFC_0, "atv", "gtv", "airplay"); 439 } 440 441 @Test removingPassthroughQName_maintainsOrder()442 public void removingPassthroughQName_maintainsOrder() throws RemoteException { 443 setupDefaultOffloadManager(); 444 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 445 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 446 mOffloadManagerBinder.addToPassthroughList(IFC_0, "airplay", mClientBinder0); 447 mTestLooper.dispatchAll(); 448 449 mOffloadManagerBinder.removeFromPassthroughList(IFC_0, "gtv", mClientBinder0); 450 mTestLooper.dispatchAll(); 451 452 assertEquals( 453 PassthroughBehavior.PASSTHROUGH_LIST, 454 mVendorService.getOffloadData(IFC_0).passthroughBehavior); 455 verifyPassthroughQNames(mVendorService, IFC_0, "atv", "airplay"); 456 } 457 458 @Test removingNonexistentPassthroughQName_doesNothing()459 public void removingNonexistentPassthroughQName_doesNothing() throws RemoteException { 460 setupDefaultOffloadManager(); 461 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 462 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 463 mOffloadManagerBinder.addToPassthroughList(IFC_0, "airplay", mClientBinder0); 464 mTestLooper.dispatchAll(); 465 reset(mVendorService); // Forget previous calls related to passthrough. 466 467 mOffloadManagerBinder.removeFromPassthroughList( 468 IFC_0, "otherservice", mClientBinder0); 469 mTestLooper.dispatchAll(); 470 471 verify(mVendorService, never()).setPassthroughBehavior(eq(IFC_0), anyByte()); 472 verify(mVendorService, never()).removeFromPassthroughList(eq(IFC_0), anyString()); 473 assertEquals( 474 PassthroughBehavior.PASSTHROUGH_LIST, 475 mVendorService.getOffloadData(IFC_0).passthroughBehavior); 476 verifyPassthroughQNames(mVendorService, IFC_0, "atv", "gtv", "airplay"); 477 } 478 479 @Test removingPassthroughQNameHoldingInvalidClientBinder_doesNothing()480 public void removingPassthroughQNameHoldingInvalidClientBinder_doesNothing() 481 throws RemoteException { 482 setupDefaultOffloadManager(); 483 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 484 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 485 mOffloadManagerBinder.addToPassthroughList(IFC_0, "airplay", mClientBinder0); 486 mTestLooper.dispatchAll(); 487 488 mOffloadManagerBinder.removeFromPassthroughList(IFC_0, "gtv", mClientBinder1); 489 mTestLooper.dispatchAll(); 490 491 verifyPassthroughQNames(mVendorService, IFC_0, "atv", "gtv", "airplay"); 492 } 493 494 @Test removingAllEntriesFromPassthroughList_disablesPassthroughBehavior()495 public void removingAllEntriesFromPassthroughList_disablesPassthroughBehavior() 496 throws RemoteException { 497 setupDefaultOffloadManager(); 498 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 499 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 500 mTestLooper.dispatchAll(); 501 assertEquals( 502 PassthroughBehavior.PASSTHROUGH_LIST, 503 mVendorService.getOffloadData(IFC_0).passthroughBehavior); 504 505 mOffloadManagerBinder.removeFromPassthroughList(IFC_0, "atv", mClientBinder0); 506 mOffloadManagerBinder.removeFromPassthroughList(IFC_0, "gtv", mClientBinder0); 507 mTestLooper.dispatchAll(); 508 509 assertEquals( 510 PassthroughBehavior.DROP_ALL, 511 mVendorService.getOffloadData(IFC_0).passthroughBehavior); 512 } 513 514 @Test passthroughQNamesFromPriorityListTakePrecedence()515 public void passthroughQNamesFromPriorityListTakePrecedence() throws RemoteException { 516 setupDefaultOffloadManager(); 517 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 518 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 519 mTestLooper.dispatchAll(); 520 verifyPassthroughQNames(mVendorService, IFC_0, "atv", "gtv"); 521 522 mOffloadManagerBinder.addToPassthroughList( 523 IFC_0, "_googlecast._tcp.local", mClientBinder0); 524 mTestLooper.dispatchAll(); 525 526 verifyPassthroughQNames(mVendorService, IFC_0, "_googlecast._tcp.local", "atv", "gtv"); 527 } 528 529 /** 530 * TODO(b/271353749#comment43) Remove this requirement once vendor implementations support 531 * case-insensitive string comparisons. 532 */ 533 @Test passthroughPreservesCase()534 public void passthroughPreservesCase() throws RemoteException { 535 setupDefaultOffloadManager(); 536 537 mOffloadManagerBinder.addToPassthroughList(IFC_0, "_SERVICE012._gtv.local", mClientBinder0); 538 mTestLooper.dispatchAll(); 539 540 verifyPassthroughQNames(mVendorService, IFC_0, "_SERVICE012._gtv.local"); 541 } 542 543 @Test whenOutOfMemoryCapacity_priorityListQNamesAreNotEvicted()544 public void whenOutOfMemoryCapacity_priorityListQNamesAreNotEvicted() throws RemoteException { 545 setupDefaultOffloadManager(); 546 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 547 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 548 mOffloadManagerBinder.addToPassthroughList(IFC_0, "another", mClientBinder0); 549 mOffloadManagerBinder.addToPassthroughList(IFC_0, "service", mClientBinder0); 550 mOffloadManagerBinder.addToPassthroughList( 551 IFC_0, "_googlecast._tcp.local", mClientBinder0); 552 mTestLooper.dispatchAll(); 553 554 verifyPassthroughQNames( 555 mVendorService, IFC_0, "_googlecast._tcp.local", "atv", "gtv", "another"); 556 } 557 558 @Test whenVendorServiceBindsLate_offloadsData()559 public void whenVendorServiceBindsLate_offloadsData() throws RemoteException { 560 createOffloadManager(); 561 registerNetwork(mNetwork0, IFC_0); 562 int recordKey = mOffloadManagerBinder.addProtocolResponses( 563 IFC_0, SERVICE_ATV, mClientBinder0); 564 assertTrue("Expected a valid record key", recordKey > 0); 565 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 566 mTestLooper.dispatchAll(); 567 568 bindVendorService(); 569 570 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 571 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 572 } 573 574 @Test whenVendorServiceReconnects_restoresOffloadData()575 public void whenVendorServiceReconnects_restoresOffloadData() throws RemoteException { 576 setupDefaultOffloadManager(); 577 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 578 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 579 mTestLooper.dispatchAll(); 580 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 581 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 582 583 unbindVendorService(); 584 mVendorService = new FakeMdnsOffloadService(); 585 bindVendorService(); 586 587 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 588 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 589 } 590 591 @Test whenClientDies_cleansUpOffloadData()592 public void whenClientDies_cleansUpOffloadData() throws RemoteException { 593 setupDefaultOffloadManager(); 594 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 595 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 596 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_GOOGLECAST, mClientBinder1); 597 mOffloadManagerBinder.addToPassthroughList(IFC_0, "airplay", mClientBinder1); 598 mTestLooper.dispatchAll(); 599 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_GOOGLECAST, SERVICE_ATV); 600 verifyPassthroughQNames(mVendorService, IFC_0, "gtv", "airplay"); 601 602 verify(mClientBinder1, atLeastOnce()) 603 .linkToDeath(mDeathRecipientCaptor.capture(), eq(0)); 604 mDeathRecipientCaptor.getAllValues().forEach(IBinder.DeathRecipient::binderDied); 605 mTestLooper.dispatchAll(); 606 607 // Offload data owned by other clients is left untouched. 608 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 609 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 610 } 611 612 @Test whenNonInteractiveMode_enablesOffload()613 public void whenNonInteractiveMode_enablesOffload() throws RemoteException { 614 setupDefaultOffloadManager(); 615 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 616 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 617 mTestLooper.dispatchAll(); 618 619 mCapturedScreenBroadcastReceiver.onReceive( 620 mock(Context.class), makeIntent(Intent.ACTION_SCREEN_OFF)); 621 mTestLooper.dispatchAll(); 622 623 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 624 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 625 assertTrue(mVendorService.mOffloadState); 626 } 627 628 @Test whenInteractiveMode_disablesOffloadAndRetrievesMetrics()629 public void whenInteractiveMode_disablesOffloadAndRetrievesMetrics() throws RemoteException { 630 setupDefaultOffloadManager(); 631 int recordKey = mOffloadManagerBinder.addProtocolResponses( 632 IFC_0, SERVICE_ATV, mClientBinder0); 633 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 634 mTestLooper.dispatchAll(); 635 mCapturedScreenBroadcastReceiver.onReceive( 636 mock(Context.class), makeIntent(Intent.ACTION_SCREEN_OFF)); 637 mTestLooper.dispatchAll(); 638 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 639 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 640 assertTrue(mVendorService.mOffloadState); 641 reset(mVendorService); // Forget previous calls to hit/miss counter methods. 642 643 mCapturedScreenBroadcastReceiver.onReceive( 644 mock(Context.class), makeIntent(Intent.ACTION_SCREEN_ON)); 645 mTestLooper.dispatchAll(); 646 647 assertFalse(mVendorService.mOffloadState); 648 verify(mVendorService).getAndResetHitCounter(eq(recordKey)); 649 verify(mVendorService).getAndResetMissCounter(); 650 // Offloaded records are untouched. 651 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 652 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 653 } 654 655 @Test whenNetworkNotAvailable_noOffloadOrPassthrough()656 public void whenNetworkNotAvailable_noOffloadOrPassthrough() throws RemoteException { 657 createOffloadManager(); 658 bindVendorService(); 659 660 int recordKey = mOffloadManagerBinder.addProtocolResponses( 661 IFC_0, SERVICE_ATV, mClientBinder0); 662 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 663 mTestLooper.dispatchAll(); 664 665 assertTrue("Expected a valid record key", recordKey > 0); 666 verifyOffloadedServices(mVendorService, IFC_0); 667 verifyPassthroughQNames(mVendorService, IFC_0); 668 assertEquals( 669 PassthroughBehavior.DROP_ALL, 670 mVendorService.getOffloadData(IFC_0).passthroughBehavior); 671 } 672 673 @Test whenNetworkBecomesAvailableLate_offloadsData()674 public void whenNetworkBecomesAvailableLate_offloadsData() throws RemoteException { 675 createOffloadManager(); 676 bindVendorService(); 677 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 678 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 679 mTestLooper.dispatchAll(); 680 681 registerNetwork(mNetwork0, IFC_0); 682 683 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 684 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 685 } 686 687 @Test whenNetworkLost_removesOffloadData()688 public void whenNetworkLost_removesOffloadData() throws RemoteException { 689 setupDefaultOffloadManager(); 690 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 691 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 692 mTestLooper.dispatchAll(); 693 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 694 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 695 696 unregisterNetwork(mNetwork0); 697 698 verifyOffloadedServices(mVendorService, IFC_0); 699 verifyPassthroughQNames(mVendorService, IFC_0); 700 assertEquals( 701 PassthroughBehavior.DROP_ALL, 702 mVendorService.getOffloadData(IFC_0).passthroughBehavior); 703 } 704 705 @Test whenNetworkLost_maintainsOffloadDataOnOtherInterfaces()706 public void whenNetworkLost_maintainsOffloadDataOnOtherInterfaces() throws RemoteException { 707 setupDefaultOffloadManager(); 708 registerNetwork(mNetwork1, IFC_1); 709 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 710 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 711 mOffloadManagerBinder.addProtocolResponses(IFC_1, SERVICE_GOOGLECAST, mClientBinder0); 712 mOffloadManagerBinder.addToPassthroughList(IFC_1, "airplay", mClientBinder0); 713 mTestLooper.dispatchAll(); 714 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 715 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 716 verifyOffloadedServices(mVendorService, IFC_1, SERVICE_GOOGLECAST); 717 verifyPassthroughQNames(mVendorService, IFC_1, "airplay"); 718 719 unregisterNetwork(mNetwork1); 720 721 // Offload data on IFC_0 is left untouched. 722 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 723 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 724 assertEquals( 725 PassthroughBehavior.PASSTHROUGH_LIST, 726 mVendorService.getOffloadData(IFC_0).passthroughBehavior); 727 verifyOffloadedServices(mVendorService, IFC_1); 728 verifyPassthroughQNames(mVendorService, IFC_1); 729 assertEquals( 730 PassthroughBehavior.DROP_ALL, 731 mVendorService.getOffloadData(IFC_1).passthroughBehavior); 732 } 733 734 @Test whenNetworkRecovers_restoresOffloadData()735 public void whenNetworkRecovers_restoresOffloadData() throws RemoteException { 736 setupDefaultOffloadManager(); 737 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 738 mOffloadManagerBinder.addToPassthroughList(IFC_0, "gtv", mClientBinder0); 739 mTestLooper.dispatchAll(); 740 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 741 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 742 unregisterNetwork(mNetwork0); 743 verifyOffloadedServices(mVendorService, IFC_0); 744 verifyPassthroughQNames(mVendorService, IFC_0); 745 746 registerNetwork(mNetwork0, IFC_0); 747 748 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 749 verifyPassthroughQNames(mVendorService, IFC_0, "gtv"); 750 assertEquals( 751 PassthroughBehavior.PASSTHROUGH_LIST, 752 mVendorService.getOffloadData(IFC_0).passthroughBehavior); 753 } 754 755 @Test callingPackageNotOnLowPowerExemptedList_dataNotOffloaded()756 public void callingPackageNotOnLowPowerExemptedList_dataNotOffloaded() throws RemoteException { 757 setupDefaultOffloadManager(); 758 mCallingUid = APP_UID_1; 759 760 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 761 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 762 mTestLooper.dispatchAll(); 763 764 verifyOffloadedServices(mVendorService, IFC_0); 765 verifyPassthroughQNames(mVendorService, IFC_0); 766 } 767 768 @Test packageRemovedFromLowPowerExemptedList_correspondingDataIsCleared()769 public void packageRemovedFromLowPowerExemptedList_correspondingDataIsCleared() 770 throws RemoteException { 771 setupDefaultOffloadManager(); 772 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 773 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 774 mTestLooper.dispatchAll(); 775 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 776 verifyPassthroughQNames(mVendorService, IFC_0, "atv"); 777 778 mLowPowerStandbyPolicy = makeLowPowerStandbyPolicy(APP_PACKAGE_1); 779 mCapturedLowPowerStandbyPolicyReceiver.onReceive( 780 mock(Context.class), 781 makeIntent(PowerManager.ACTION_LOW_POWER_STANDBY_POLICY_CHANGED)); 782 mTestLooper.dispatchAll(); 783 784 verifyOffloadedServices(mVendorService, IFC_0); 785 verifyPassthroughQNames(mVendorService, IFC_0); 786 } 787 788 @Test packageAddedToLowPowerExemptedList_dataIsOffloaded()789 public void packageAddedToLowPowerExemptedList_dataIsOffloaded() throws RemoteException { 790 setupDefaultOffloadManager(); 791 mCallingUid = APP_UID_1; 792 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 793 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 794 mTestLooper.dispatchAll(); 795 verifyOffloadedServices(mVendorService, IFC_0); 796 verifyPassthroughQNames(mVendorService, IFC_0); 797 798 mLowPowerStandbyPolicy = makeLowPowerStandbyPolicy(APP_PACKAGE_0, APP_PACKAGE_1); 799 mCapturedLowPowerStandbyPolicyReceiver.onReceive( 800 mock(Context.class), 801 makeIntent(PowerManager.ACTION_LOW_POWER_STANDBY_POLICY_CHANGED)); 802 mTestLooper.dispatchAll(); 803 804 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 805 verifyPassthroughQNames(mVendorService, IFC_0, "atv"); 806 } 807 808 /** 809 * Ensure package allowlist is maintained by app ID, not UID (which is assigned per app & user 810 * combination). 811 */ 812 @Test secondaryUser_packageIsAllowlisted()813 public void secondaryUser_packageIsAllowlisted() throws RemoteException { 814 setupDefaultOffloadManager(); 815 mCallingUid = SECONDARY_USER_APP_UID_0; 816 817 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 818 mOffloadManagerBinder.addToPassthroughList(IFC_0, "atv", mClientBinder0); 819 mTestLooper.dispatchAll(); 820 821 verifyOffloadedServices(mVendorService, IFC_0, SERVICE_ATV); 822 verifyPassthroughQNames(mVendorService, IFC_0, "atv"); 823 } 824 825 @Test serviceDump_containsDebuggingInfo()826 public void serviceDump_containsDebuggingInfo() throws RemoteException { 827 setupDefaultOffloadManager(); 828 mOffloadManagerBinder.addProtocolResponses(IFC_0, SERVICE_ATV, mClientBinder0); 829 mOffloadManagerBinder.addToPassthroughList( 830 IFC_0, "atv", mClientBinder0); 831 mCallingUid = APP_UID_1; 832 mOffloadManagerBinder.addProtocolResponses(IFC_1, SERVICE_GOOGLECAST, mClientBinder1); 833 mOffloadManagerBinder.addToPassthroughList( 834 IFC_1, "gtv", mClientBinder1); 835 mTestLooper.dispatchAll(); 836 837 StringWriter resultWriter = new StringWriter(); 838 mOffloadManagerService.dump(null, new PrintWriter(resultWriter), null); 839 mTestLooper.dispatchAll(); 840 String result = resultWriter.getBuffer().toString(); 841 842 Log.d(TAG, "Service dump:\n" + result); 843 assertTrue(result.contains(""" 844 OffloadIntentStore: 845 offload intents: 846 * OffloadIntent{mNetworkInterface='imaginaryif0', mRecordKey=1, mPriority=1, mOwnerAppId=1234} 847 * OffloadIntent{mNetworkInterface='imaginaryif1', mRecordKey=2, mPriority=-2, mOwnerAppId=1235} 848 passthrough intents: 849 * PassthroughIntent{mNetworkInterface='imaginaryif0', mOriginalQName='atv', mCanonicalQName='ATV.', mPriority=0, mOwnerAppId=1234} 850 * PassthroughIntent{mNetworkInterface='imaginaryif1', mOriginalQName='gtv', mCanonicalQName='GTV.', mPriority=0, mOwnerAppId=1235} 851 852 """)); 853 assertTrue(result.contains(""" 854 InterfaceOffloadManager[imaginaryif0]: 855 mIsNetworkAvailable=true 856 current offload keys: 857 * 0 858 current passthrough qnames: 859 * atv 860 861 """)); 862 assertTrue(result.contains(""" 863 InterfaceOffloadManager[imaginaryif1]: 864 mIsNetworkAvailable=false 865 current offload keys: 866 current passthrough qnames: 867 868 """)); 869 assertTrue(result.contains(""" 870 OffloadWriter: 871 mOffloadState=false 872 isVendorServiceConnected=true 873 874 """)); 875 assertTrue(result.contains(""" 876 mRecordKey=1 877 match criteria: 878 * MatchCriteria{type=1, nameOffset=12} 879 raw offload packet: 880 000000: 00 00 00 00 00 00 00 01 00 00 00 00 03 61 74 76 | .............atv 881 000016: 00 00 01 80 01 00 00 00 05 00 04 64 50 28 14 | ...........dP(. 882 """)); 883 assertTrue(result.contains(""" 884 mRecordKey=2 885 match criteria: 886 * MatchCriteria{type=12, nameOffset=12} 887 * MatchCriteria{type=1, nameOffset=55} 888 raw offload packet: 889 000000: 00 00 00 00 00 00 00 02 00 00 00 00 0b 5f 67 6f | ............._go 890 000016: 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f | oglecast._tcp.lo 891 000032: 63 61 6c 00 00 0c 80 01 00 00 00 05 00 09 06 74 | cal............t 892 000048: 76 2d 61 62 63 c0 1d c0 2e 00 01 80 01 00 00 00 | v-abc........... 893 000064: 05 00 04 64 50 28 14 | ...dP(. 894 """)); 895 } 896 }