1 /* 2 * Copyright (C) 2017 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; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.mockito.Mockito.any; 21 import static org.mockito.Mockito.mock; 22 import static org.mockito.Mockito.never; 23 import static org.mockito.Mockito.reset; 24 import static org.mockito.Mockito.timeout; 25 import static org.mockito.Mockito.verify; 26 import static org.mockito.Mockito.when; 27 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.net.nsd.NsdManager; 31 import android.net.nsd.NsdServiceInfo; 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.os.Looper; 35 import android.os.Message; 36 37 import androidx.test.filters.SmallTest; 38 import androidx.test.runner.AndroidJUnit4; 39 40 import com.android.server.NsdService.DaemonConnection; 41 import com.android.server.NsdService.DaemonConnectionSupplier; 42 import com.android.server.NsdService.NativeCallbackReceiver; 43 44 import org.junit.After; 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 import org.mockito.ArgumentCaptor; 49 import org.mockito.Mock; 50 import org.mockito.MockitoAnnotations; 51 52 // TODOs: 53 // - test client can send requests and receive replies 54 // - test NSD_ON ENABLE/DISABLED listening 55 @RunWith(AndroidJUnit4.class) 56 @SmallTest 57 public class NsdServiceTest { 58 59 static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; 60 61 long mTimeoutMs = 100; // non-final so that tests can adjust the value. 62 63 @Mock Context mContext; 64 @Mock ContentResolver mResolver; 65 @Mock NsdService.NsdSettings mSettings; 66 @Mock DaemonConnection mDaemon; 67 NativeCallbackReceiver mDaemonCallback; 68 HandlerThread mThread; 69 TestHandler mHandler; 70 71 @Before setUp()72 public void setUp() throws Exception { 73 MockitoAnnotations.initMocks(this); 74 mThread = new HandlerThread("mock-service-handler"); 75 mThread.start(); 76 mHandler = new TestHandler(mThread.getLooper()); 77 when(mContext.getContentResolver()).thenReturn(mResolver); 78 } 79 80 @After tearDown()81 public void tearDown() throws Exception { 82 if (mThread != null) { 83 mThread.quit(); 84 mThread = null; 85 } 86 } 87 88 @Test testClientsCanConnectAndDisconnect()89 public void testClientsCanConnectAndDisconnect() { 90 when(mSettings.isEnabled()).thenReturn(true); 91 92 NsdService service = makeService(); 93 94 NsdManager client1 = connectClient(service); 95 verify(mDaemon, timeout(100).times(1)).start(); 96 97 NsdManager client2 = connectClient(service); 98 99 client1.disconnect(); 100 client2.disconnect(); 101 102 verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); 103 104 client1.disconnect(); 105 client2.disconnect(); 106 } 107 108 @Test testClientRequestsAreGCedAtDisconnection()109 public void testClientRequestsAreGCedAtDisconnection() { 110 when(mSettings.isEnabled()).thenReturn(true); 111 when(mDaemon.execute(any())).thenReturn(true); 112 113 NsdService service = makeService(); 114 NsdManager client = connectClient(service); 115 116 verify(mDaemon, timeout(100).times(1)).start(); 117 118 NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); 119 request.setPort(2201); 120 121 // Client registration request 122 NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); 123 client.registerService(request, PROTOCOL, listener1); 124 verifyDaemonCommand("register 2 a_name a_type 2201"); 125 126 // Client discovery request 127 NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); 128 client.discoverServices("a_type", PROTOCOL, listener2); 129 verifyDaemonCommand("discover 3 a_type"); 130 131 // Client resolve request 132 NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class); 133 client.resolveService(request, listener3); 134 verifyDaemonCommand("resolve 4 a_name a_type local."); 135 136 // Client disconnects 137 client.disconnect(); 138 verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); 139 140 // checks that request are cleaned 141 verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4"); 142 143 client.disconnect(); 144 } 145 makeService()146 NsdService makeService() { 147 DaemonConnectionSupplier supplier = (callback) -> { 148 mDaemonCallback = callback; 149 return mDaemon; 150 }; 151 NsdService service = new NsdService(mContext, mSettings, mHandler, supplier); 152 verify(mDaemon, never()).execute(any(String.class)); 153 return service; 154 } 155 connectClient(NsdService service)156 NsdManager connectClient(NsdService service) { 157 return new NsdManager(mContext, service); 158 } 159 verifyDaemonCommands(String... wants)160 void verifyDaemonCommands(String... wants) { 161 verifyDaemonCommand(String.join(" ", wants), wants.length); 162 } 163 verifyDaemonCommand(String want)164 void verifyDaemonCommand(String want) { 165 verifyDaemonCommand(want, 1); 166 } 167 verifyDaemonCommand(String want, int n)168 void verifyDaemonCommand(String want, int n) { 169 ArgumentCaptor<Object> argumentsCaptor = ArgumentCaptor.forClass(Object.class); 170 verify(mDaemon, timeout(mTimeoutMs).times(n)).execute(argumentsCaptor.capture()); 171 String got = ""; 172 for (Object o : argumentsCaptor.getAllValues()) { 173 got += o + " "; 174 } 175 assertEquals(want, got.trim()); 176 // rearm deamon for next command verification 177 reset(mDaemon); 178 when(mDaemon.execute(any())).thenReturn(true); 179 } 180 181 public static class TestHandler extends Handler { 182 public Message lastMessage; 183 TestHandler(Looper looper)184 TestHandler(Looper looper) { 185 super(looper); 186 } 187 188 @Override handleMessage(Message msg)189 public void handleMessage(Message msg) { 190 lastMessage = obtainMessage(); 191 lastMessage.copyFrom(msg); 192 } 193 } 194 } 195