blob: 0d3ffd129c0ea6df352c681bc42a24bd5f97e8ec [file] [log] [blame]
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001/*
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09002 * Copyright (C) 2021 The Android Open Source Project
Irfan Sheriff77ec5582012-03-22 17:01:39 -07003 *
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;
18
paulhu2b9ed952022-02-10 21:58:32 +080019import static android.net.ConnectivityManager.NETID_UNSET;
Paul Hu019621e2023-01-13 23:26:49 +080020import static android.net.nsd.NsdManager.MDNS_DISCOVERY_MANAGER_EVENT;
paulhu2b9ed952022-02-10 21:58:32 +080021import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
Paul Hu4bd98ef2023-01-12 13:42:07 +080022import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
paulhu2b9ed952022-02-10 21:58:32 +080023
Paul Hu23fa2022023-01-13 22:57:24 +080024import android.annotation.NonNull;
Paul Hu4bd98ef2023-01-12 13:42:07 +080025import android.annotation.Nullable;
paulhua262cc12019-08-12 16:25:11 +080026import android.content.Context;
Irfan Sheriff75006652012-04-17 23:15:29 -070027import android.content.Intent;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090028import android.net.ConnectivityManager;
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +090029import android.net.INetd;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090030import android.net.LinkProperties;
31import android.net.Network;
paulhu2b9ed952022-02-10 21:58:32 +080032import android.net.mdns.aidl.DiscoveryInfo;
33import android.net.mdns.aidl.GetAddressInfo;
34import android.net.mdns.aidl.IMDnsEventListener;
35import android.net.mdns.aidl.RegistrationInfo;
36import android.net.mdns.aidl.ResolutionInfo;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070037import android.net.nsd.INsdManager;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090038import android.net.nsd.INsdManagerCallback;
39import android.net.nsd.INsdServiceConnector;
paulhu2b9ed952022-02-10 21:58:32 +080040import android.net.nsd.MDnsManager;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070041import android.net.nsd.NsdManager;
paulhua262cc12019-08-12 16:25:11 +080042import android.net.nsd.NsdServiceInfo;
Hugo Benichi803a2f02017-04-24 11:35:06 +090043import android.os.Handler;
paulhua262cc12019-08-12 16:25:11 +080044import android.os.HandlerThread;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090045import android.os.IBinder;
Paul Hu4bd98ef2023-01-12 13:42:07 +080046import android.os.Looper;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070047import android.os.Message;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090048import android.os.RemoteException;
Dianne Hackborn692107e2012-08-29 18:32:08 -070049import android.os.UserHandle;
Paul Hu23fa2022023-01-13 22:57:24 +080050import android.text.TextUtils;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090051import android.util.Log;
52import android.util.Pair;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070053import android.util.SparseArray;
Hugo Benichid2552ae2017-04-11 14:42:47 +090054import android.util.SparseIntArray;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070055
paulhua262cc12019-08-12 16:25:11 +080056import com.android.internal.annotations.VisibleForTesting;
paulhua262cc12019-08-12 16:25:11 +080057import com.android.internal.util.State;
58import com.android.internal.util.StateMachine;
Paul Hu4bd98ef2023-01-12 13:42:07 +080059import com.android.net.module.util.DeviceConfigUtils;
paulhu3ffffe72021-09-16 10:15:22 +080060import com.android.net.module.util.PermissionUtils;
Paul Hu4bd98ef2023-01-12 13:42:07 +080061import com.android.server.connectivity.mdns.ExecutorProvider;
62import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
63import com.android.server.connectivity.mdns.MdnsMultinetworkSocketClient;
Paul Hu23fa2022023-01-13 22:57:24 +080064import com.android.server.connectivity.mdns.MdnsSearchOptions;
65import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
66import com.android.server.connectivity.mdns.MdnsServiceInfo;
Paul Hu4bd98ef2023-01-12 13:42:07 +080067import com.android.server.connectivity.mdns.MdnsSocketClientBase;
68import com.android.server.connectivity.mdns.MdnsSocketProvider;
paulhua262cc12019-08-12 16:25:11 +080069
Irfan Sheriff77ec5582012-03-22 17:01:39 -070070import java.io.FileDescriptor;
71import java.io.PrintWriter;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070072import java.net.InetAddress;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090073import java.net.NetworkInterface;
74import java.net.SocketException;
75import java.net.UnknownHostException;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070076import java.util.HashMap;
Paul Hu23fa2022023-01-13 22:57:24 +080077import java.util.List;
78import java.util.regex.Matcher;
79import java.util.regex.Pattern;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070080
Irfan Sheriff77ec5582012-03-22 17:01:39 -070081/**
82 * Network Service Discovery Service handles remote service discovery operation requests by
83 * implementing the INsdManager interface.
84 *
85 * @hide
86 */
87public class NsdService extends INsdManager.Stub {
88 private static final String TAG = "NsdService";
89 private static final String MDNS_TAG = "mDnsConnector";
Paul Hu4bd98ef2023-01-12 13:42:07 +080090 private static final String MDNS_DISCOVERY_MANAGER_VERSION = "mdns_discovery_manager_version";
Paul Hu23fa2022023-01-13 22:57:24 +080091 private static final String LOCAL_DOMAIN_NAME = "local";
Irfan Sheriff77ec5582012-03-22 17:01:39 -070092
paulhu2b9ed952022-02-10 21:58:32 +080093 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Luke Huang92860f92021-06-23 06:29:30 +000094 private static final long CLEANUP_DELAY_MS = 10000;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090095 private static final int IFACE_IDX_ANY = 0;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070096
Hugo Benichi32be63d2017-04-05 14:06:11 +090097 private final Context mContext;
Hugo Benichi32be63d2017-04-05 14:06:11 +090098 private final NsdStateMachine mNsdStateMachine;
paulhu2b9ed952022-02-10 21:58:32 +080099 private final MDnsManager mMDnsManager;
100 private final MDnsEventCallback mMDnsEventCallback;
Paul Hu4bd98ef2023-01-12 13:42:07 +0800101 @Nullable
102 private final MdnsMultinetworkSocketClient mMdnsSocketClient;
103 @Nullable
104 private final MdnsDiscoveryManager mMdnsDiscoveryManager;
105 @Nullable
106 private final MdnsSocketProvider mMdnsSocketProvider;
Paul Hu23fa2022023-01-13 22:57:24 +0800107 // WARNING : Accessing these values in any thread is not safe, it must only be changed in the
paulhu2b9ed952022-02-10 21:58:32 +0800108 // state machine thread. If change this outside state machine, it will need to introduce
109 // synchronization.
110 private boolean mIsDaemonStarted = false;
Paul Hu23fa2022023-01-13 22:57:24 +0800111 private boolean mIsMonitoringSocketsStarted = false;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700112
113 /**
114 * Clients receiving asynchronous messages
115 */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900116 private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700117
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700118 /* A map from unique id to client info */
Hugo Benichi32be63d2017-04-05 14:06:11 +0900119 private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700120
Luke Huang05298582021-06-13 16:52:05 +0000121 private final long mCleanupDelayMs;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700122
Hugo Benichi32be63d2017-04-05 14:06:11 +0900123 private static final int INVALID_ID = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700124 private int mUniqueId = 1;
Luke Huangf7277ed2021-07-12 21:15:10 +0800125 // The count of the connected legacy clients.
126 private int mLegacyClientCount = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700127
Paul Hu23fa2022023-01-13 22:57:24 +0800128 private static class MdnsListener implements MdnsServiceBrowserListener {
129 protected final int mClientId;
130 protected final int mTransactionId;
131 @NonNull
132 protected final NsdServiceInfo mReqServiceInfo;
133 @NonNull
134 protected final String mListenedServiceType;
135
136 MdnsListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
137 @NonNull String listenedServiceType) {
138 mClientId = clientId;
139 mTransactionId = transactionId;
140 mReqServiceInfo = reqServiceInfo;
141 mListenedServiceType = listenedServiceType;
142 }
143
144 @NonNull
145 public String getListenedServiceType() {
146 return mListenedServiceType;
147 }
148
149 @Override
150 public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo) { }
151
152 @Override
153 public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) { }
154
155 @Override
156 public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
157
158 @Override
159 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo) { }
160
161 @Override
162 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) { }
163
164 @Override
165 public void onSearchStoppedWithError(int error) { }
166
167 @Override
168 public void onSearchFailedToStart() { }
169
170 @Override
171 public void onDiscoveryQuerySent(@NonNull List<String> subtypes, int transactionId) { }
172
173 @Override
174 public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode) { }
175 }
176
177 private class DiscoveryListener extends MdnsListener {
178
179 DiscoveryListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
180 @NonNull String listenServiceType) {
181 super(clientId, transactionId, reqServiceInfo, listenServiceType);
182 }
183
184 @Override
185 public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo) {
Paul Hu019621e2023-01-13 23:26:49 +0800186 mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
187 NsdManager.SERVICE_FOUND,
188 new MdnsEvent(mClientId, mReqServiceInfo.getServiceType(), serviceInfo));
Paul Hu23fa2022023-01-13 22:57:24 +0800189 }
190
191 @Override
192 public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) {
193 // TODO: implement service name removed callback.
194 }
195 }
196
Paul Hu019621e2023-01-13 23:26:49 +0800197 /**
198 * Data class of mdns service callback information.
199 */
200 private static class MdnsEvent {
201 final int mClientId;
202 @NonNull
203 final String mRequestedServiceType;
204 @NonNull
205 final MdnsServiceInfo mMdnsServiceInfo;
206
207 MdnsEvent(int clientId, @NonNull String requestedServiceType,
208 @NonNull MdnsServiceInfo mdnsServiceInfo) {
209 mClientId = clientId;
210 mRequestedServiceType = requestedServiceType;
211 mMdnsServiceInfo = mdnsServiceInfo;
212 }
213 }
214
Irfan Sheriff75006652012-04-17 23:15:29 -0700215 private class NsdStateMachine extends StateMachine {
216
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700217 private final DefaultState mDefaultState = new DefaultState();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700218 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff75006652012-04-17 23:15:29 -0700219
220 @Override
Wink Saville358f5d42012-05-29 12:40:46 -0700221 protected String getWhatToString(int what) {
Hugo Benichi32be63d2017-04-05 14:06:11 +0900222 return NsdManager.nameOf(what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700223 }
224
Luke Huang92860f92021-06-23 06:29:30 +0000225 private void maybeStartDaemon() {
paulhu2b9ed952022-02-10 21:58:32 +0800226 if (mIsDaemonStarted) {
227 if (DBG) Log.d(TAG, "Daemon is already started.");
228 return;
229 }
230 mMDnsManager.registerEventListener(mMDnsEventCallback);
231 mMDnsManager.startDaemon();
232 mIsDaemonStarted = true;
Luke Huang05298582021-06-13 16:52:05 +0000233 maybeScheduleStop();
234 }
235
paulhu2b9ed952022-02-10 21:58:32 +0800236 private void maybeStopDaemon() {
237 if (!mIsDaemonStarted) {
238 if (DBG) Log.d(TAG, "Daemon has not been started.");
239 return;
240 }
241 mMDnsManager.unregisterEventListener(mMDnsEventCallback);
242 mMDnsManager.stopDaemon();
243 mIsDaemonStarted = false;
244 }
245
Luke Huang92860f92021-06-23 06:29:30 +0000246 private boolean isAnyRequestActive() {
247 return mIdToClientInfoMap.size() != 0;
248 }
249
250 private void scheduleStop() {
251 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
252 }
253 private void maybeScheduleStop() {
Luke Huangf7277ed2021-07-12 21:15:10 +0800254 // The native daemon should stay alive and can't be cleanup
255 // if any legacy client connected.
256 if (!isAnyRequestActive() && mLegacyClientCount == 0) {
Luke Huang92860f92021-06-23 06:29:30 +0000257 scheduleStop();
Luke Huang05298582021-06-13 16:52:05 +0000258 }
259 }
260
Luke Huang92860f92021-06-23 06:29:30 +0000261 private void cancelStop() {
Luke Huang05298582021-06-13 16:52:05 +0000262 this.removeMessages(NsdManager.DAEMON_CLEANUP);
263 }
264
Paul Hu23fa2022023-01-13 22:57:24 +0800265 private void maybeStartMonitoringSockets() {
266 if (mIsMonitoringSocketsStarted) {
267 if (DBG) Log.d(TAG, "Socket monitoring is already started.");
268 return;
269 }
270
271 mMdnsSocketProvider.startMonitoringSockets();
272 mIsMonitoringSocketsStarted = true;
273 }
274
275 private void maybeStopMonitoringSockets() {
276 if (!mIsMonitoringSocketsStarted) {
277 if (DBG) Log.d(TAG, "Socket monitoring has not been started.");
278 return;
279 }
280 mMdnsSocketProvider.stopMonitoringSockets();
281 mIsMonitoringSocketsStarted = false;
282 }
283
284 private void maybeStopMonitoringSocketsIfNoActiveRequest() {
285 if (!isAnyRequestActive()) {
286 maybeStopMonitoringSockets();
287 }
288 }
289
Hugo Benichi803a2f02017-04-24 11:35:06 +0900290 NsdStateMachine(String name, Handler handler) {
291 super(name, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700292 addState(mDefaultState);
Irfan Sheriff75006652012-04-17 23:15:29 -0700293 addState(mEnabledState, mDefaultState);
paulhu5568f452021-11-30 13:31:29 +0800294 State initialState = mEnabledState;
Hugo Benichi912db992017-04-24 16:41:03 +0900295 setInitialState(initialState);
Wink Saville358f5d42012-05-29 12:40:46 -0700296 setLogRecSize(25);
Irfan Sheriff75006652012-04-17 23:15:29 -0700297 }
298
299 class DefaultState extends State {
300 @Override
301 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900302 final ClientInfo cInfo;
303 final int clientId = msg.arg2;
Irfan Sheriff75006652012-04-17 23:15:29 -0700304 switch (msg.what) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900305 case NsdManager.REGISTER_CLIENT:
306 final Pair<NsdServiceConnector, INsdManagerCallback> arg =
307 (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj;
308 final INsdManagerCallback cb = arg.second;
309 try {
310 cb.asBinder().linkToDeath(arg.first, 0);
311 cInfo = new ClientInfo(cb);
312 mClients.put(arg.first, cInfo);
313 } catch (RemoteException e) {
314 Log.w(TAG, "Client " + clientId + " has already died");
Irfan Sheriff75006652012-04-17 23:15:29 -0700315 }
316 break;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900317 case NsdManager.UNREGISTER_CLIENT:
318 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
319 cInfo = mClients.remove(connector);
Dave Plattfeff2af2014-03-07 14:48:22 -0800320 if (cInfo != null) {
Paul Hu23fa2022023-01-13 22:57:24 +0800321 if (mMdnsDiscoveryManager != null) {
322 cInfo.unregisterAllListeners();
323 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800324 cInfo.expungeAllRequests();
Luke Huangf7277ed2021-07-12 21:15:10 +0800325 if (cInfo.isLegacy()) {
326 mLegacyClientCount -= 1;
327 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800328 }
Paul Hu23fa2022023-01-13 22:57:24 +0800329 if (mMdnsDiscoveryManager != null) {
330 maybeStopMonitoringSocketsIfNoActiveRequest();
331 }
Luke Huangf7277ed2021-07-12 21:15:10 +0800332 maybeScheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700333 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700334 case NsdManager.DISCOVER_SERVICES:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900335 cInfo = getClientInfoForReply(msg);
336 if (cInfo != null) {
337 cInfo.onDiscoverServicesFailed(
338 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
339 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700340 break;
341 case NsdManager.STOP_DISCOVERY:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900342 cInfo = getClientInfoForReply(msg);
343 if (cInfo != null) {
344 cInfo.onStopDiscoveryFailed(
345 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
346 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700347 break;
348 case NsdManager.REGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900349 cInfo = getClientInfoForReply(msg);
350 if (cInfo != null) {
351 cInfo.onRegisterServiceFailed(
352 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
353 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700354 break;
355 case NsdManager.UNREGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900356 cInfo = getClientInfoForReply(msg);
357 if (cInfo != null) {
358 cInfo.onUnregisterServiceFailed(
359 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
360 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700361 break;
362 case NsdManager.RESOLVE_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900363 cInfo = getClientInfoForReply(msg);
364 if (cInfo != null) {
365 cInfo.onResolveServiceFailed(
366 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
367 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700368 break;
Luke Huang05298582021-06-13 16:52:05 +0000369 case NsdManager.DAEMON_CLEANUP:
paulhu2b9ed952022-02-10 21:58:32 +0800370 maybeStopDaemon();
Luke Huang05298582021-06-13 16:52:05 +0000371 break;
Luke Huangf7277ed2021-07-12 21:15:10 +0800372 // This event should be only sent by the legacy (target SDK < S) clients.
373 // Mark the sending client as legacy.
374 case NsdManager.DAEMON_STARTUP:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900375 cInfo = getClientInfoForReply(msg);
Luke Huangf7277ed2021-07-12 21:15:10 +0800376 if (cInfo != null) {
377 cancelStop();
378 cInfo.setLegacy();
379 mLegacyClientCount += 1;
380 maybeStartDaemon();
381 }
382 break;
Paul Hu23fa2022023-01-13 22:57:24 +0800383 case NsdManager.MDNS_MONITORING_SOCKETS_CLEANUP:
384 maybeStopMonitoringSockets();
385 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700386 default:
paulhub2225702021-11-17 09:35:33 +0800387 Log.e(TAG, "Unhandled " + msg);
Irfan Sheriff75006652012-04-17 23:15:29 -0700388 return NOT_HANDLED;
389 }
390 return HANDLED;
391 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900392
393 private ClientInfo getClientInfoForReply(Message msg) {
394 final ListenerArgs args = (ListenerArgs) msg.obj;
395 return mClients.get(args.connector);
396 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700397 }
398
Irfan Sheriff75006652012-04-17 23:15:29 -0700399 class EnabledState extends State {
400 @Override
401 public void enter() {
402 sendNsdStateChangeBroadcast(true);
Irfan Sheriff75006652012-04-17 23:15:29 -0700403 }
404
405 @Override
406 public void exit() {
Luke Huang05298582021-06-13 16:52:05 +0000407 // TODO: it is incorrect to stop the daemon without expunging all requests
408 // and sending error callbacks to clients.
Luke Huang92860f92021-06-23 06:29:30 +0000409 scheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700410 }
411
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700412 private boolean requestLimitReached(ClientInfo clientInfo) {
413 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
paulhub2225702021-11-17 09:35:33 +0800414 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700415 return true;
416 }
417 return false;
418 }
419
Dave Plattfeff2af2014-03-07 14:48:22 -0800420 private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700421 clientInfo.mClientIds.put(clientId, globalId);
Dave Plattfeff2af2014-03-07 14:48:22 -0800422 clientInfo.mClientRequests.put(clientId, what);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700423 mIdToClientInfoMap.put(globalId, clientInfo);
Luke Huang05298582021-06-13 16:52:05 +0000424 // Remove the cleanup event because here comes a new request.
425 cancelStop();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700426 }
427
428 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
Hugo Benichid2552ae2017-04-11 14:42:47 +0900429 clientInfo.mClientIds.delete(clientId);
430 clientInfo.mClientRequests.delete(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700431 mIdToClientInfoMap.remove(globalId);
Luke Huang05298582021-06-13 16:52:05 +0000432 maybeScheduleStop();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700433 }
434
Paul Hu23fa2022023-01-13 22:57:24 +0800435 private void storeListenerMap(int clientId, int transactionId, MdnsListener listener,
436 ClientInfo clientInfo) {
437 clientInfo.mClientIds.put(clientId, transactionId);
438 clientInfo.mListeners.put(clientId, listener);
439 mIdToClientInfoMap.put(transactionId, clientInfo);
440 removeMessages(NsdManager.MDNS_MONITORING_SOCKETS_CLEANUP);
441 }
442
443 private void removeListenerMap(int clientId, int transactionId, ClientInfo clientInfo) {
444 clientInfo.mClientIds.delete(clientId);
445 clientInfo.mListeners.delete(clientId);
446 mIdToClientInfoMap.remove(transactionId);
447 maybeStopMonitoringSocketsIfNoActiveRequest();
448 }
449
450 /**
451 * Check the given service type is valid and construct it to a service type
452 * which can use for discovery / resolution service.
453 *
454 * <p> The valid service type should be 2 labels, or 3 labels if the query is for a
455 * subtype (see RFC6763 7.1). Each label is up to 63 characters and must start with an
456 * underscore; they are alphanumerical characters or dashes or underscore, except the
457 * last one that is just alphanumerical. The last label must be _tcp or _udp.
458 *
459 * @param serviceType the request service type for discovery / resolution service
460 * @return constructed service type or null if the given service type is invalid.
461 */
462 @Nullable
463 private String constructServiceType(String serviceType) {
464 if (TextUtils.isEmpty(serviceType)) return null;
465
466 final Pattern serviceTypePattern = Pattern.compile(
467 "^(_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]\\.)?"
468 + "(_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]\\._(?:tcp|udp))$");
469 final Matcher matcher = serviceTypePattern.matcher(serviceType);
470 if (!matcher.matches()) return null;
471 return matcher.group(1) == null
472 ? serviceType + ".local"
473 : matcher.group(1) + "._sub" + matcher.group(2) + ".local";
474 }
475
Irfan Sheriff75006652012-04-17 23:15:29 -0700476 @Override
477 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900478 final ClientInfo clientInfo;
479 final int id;
480 final int clientId = msg.arg2;
481 final ListenerArgs args;
Irfan Sheriff75006652012-04-17 23:15:29 -0700482 switch (msg.what) {
Irfan Sheriff75006652012-04-17 23:15:29 -0700483 case NsdManager.DISCOVER_SERVICES:
paulhub2225702021-11-17 09:35:33 +0800484 if (DBG) Log.d(TAG, "Discover services");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900485 args = (ListenerArgs) msg.obj;
486 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000487 // If the binder death notification for a INsdManagerCallback was received
488 // before any calls are received by NsdService, the clientInfo would be
489 // cleared and cause NPE. Add a null check here to prevent this corner case.
490 if (clientInfo == null) {
491 Log.e(TAG, "Unknown connector in discovery");
492 break;
493 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700494
495 if (requestLimitReached(clientInfo)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900496 clientInfo.onDiscoverServicesFailed(
497 clientId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriff75006652012-04-17 23:15:29 -0700498 break;
499 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700500
Paul Hu23fa2022023-01-13 22:57:24 +0800501 final NsdServiceInfo info = args.serviceInfo;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700502 id = getUniqueId();
Paul Hu23fa2022023-01-13 22:57:24 +0800503 if (mMdnsDiscoveryManager != null) {
504 final String serviceType = constructServiceType(info.getServiceType());
505 if (serviceType == null) {
506 clientInfo.onDiscoverServicesFailed(clientId,
507 NsdManager.FAILURE_INTERNAL_ERROR);
508 break;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700509 }
Paul Hu23fa2022023-01-13 22:57:24 +0800510
511 maybeStartMonitoringSockets();
512 final MdnsListener listener =
513 new DiscoveryListener(clientId, id, info, serviceType);
514 final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
515 .setNetwork(info.getNetwork())
516 .setIsPassiveMode(true)
517 .build();
518 mMdnsDiscoveryManager.registerListener(serviceType, listener, options);
519 storeListenerMap(clientId, id, listener, clientInfo);
520 clientInfo.onDiscoverServicesStarted(clientId, info);
Irfan Sheriff75006652012-04-17 23:15:29 -0700521 } else {
Paul Hu23fa2022023-01-13 22:57:24 +0800522 maybeStartDaemon();
523 if (discoverServices(id, info)) {
524 if (DBG) {
525 Log.d(TAG, "Discover " + msg.arg2 + " " + id
526 + info.getServiceType());
527 }
528 storeRequestMap(clientId, id, clientInfo, msg.what);
529 clientInfo.onDiscoverServicesStarted(clientId, info);
530 } else {
531 stopServiceDiscovery(id);
532 clientInfo.onDiscoverServicesFailed(clientId,
533 NsdManager.FAILURE_INTERNAL_ERROR);
534 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700535 }
536 break;
537 case NsdManager.STOP_DISCOVERY:
paulhub2225702021-11-17 09:35:33 +0800538 if (DBG) Log.d(TAG, "Stop service discovery");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900539 args = (ListenerArgs) msg.obj;
540 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000541 // If the binder death notification for a INsdManagerCallback was received
542 // before any calls are received by NsdService, the clientInfo would be
543 // cleared and cause NPE. Add a null check here to prevent this corner case.
544 if (clientInfo == null) {
545 Log.e(TAG, "Unknown connector in stop discovery");
546 break;
547 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700548
549 try {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900550 id = clientInfo.mClientIds.get(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700551 } catch (NullPointerException e) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900552 clientInfo.onStopDiscoveryFailed(
553 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700554 break;
555 }
Paul Hu23fa2022023-01-13 22:57:24 +0800556 if (mMdnsDiscoveryManager != null) {
557 final MdnsListener listener = clientInfo.mListeners.get(clientId);
558 if (listener == null) {
559 clientInfo.onStopDiscoveryFailed(
560 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
561 break;
562 }
563 mMdnsDiscoveryManager.unregisterListener(
564 listener.getListenedServiceType(), listener);
565 removeListenerMap(clientId, id, clientInfo);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900566 clientInfo.onStopDiscoverySucceeded(clientId);
Irfan Sheriff75006652012-04-17 23:15:29 -0700567 } else {
Paul Hu23fa2022023-01-13 22:57:24 +0800568 removeRequestMap(clientId, id, clientInfo);
569 if (stopServiceDiscovery(id)) {
570 clientInfo.onStopDiscoverySucceeded(clientId);
571 } else {
572 clientInfo.onStopDiscoveryFailed(
573 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
574 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700575 }
576 break;
577 case NsdManager.REGISTER_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800578 if (DBG) Log.d(TAG, "Register service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900579 args = (ListenerArgs) msg.obj;
580 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000581 // If the binder death notification for a INsdManagerCallback was received
582 // before any calls are received by NsdService, the clientInfo would be
583 // cleared and cause NPE. Add a null check here to prevent this corner case.
584 if (clientInfo == null) {
585 Log.e(TAG, "Unknown connector in registration");
586 break;
587 }
588
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700589 if (requestLimitReached(clientInfo)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900590 clientInfo.onRegisterServiceFailed(
591 clientId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700592 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700593 }
594
Luke Huang05298582021-06-13 16:52:05 +0000595 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700596 id = getUniqueId();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900597 if (registerService(id, args.serviceInfo)) {
paulhub2225702021-11-17 09:35:33 +0800598 if (DBG) Log.d(TAG, "Register " + clientId + " " + id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900599 storeRequestMap(clientId, id, clientInfo, msg.what);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700600 // Return success after mDns reports success
Irfan Sheriff75006652012-04-17 23:15:29 -0700601 } else {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700602 unregisterService(id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900603 clientInfo.onRegisterServiceFailed(
604 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700605 }
606 break;
607 case NsdManager.UNREGISTER_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800608 if (DBG) Log.d(TAG, "unregister service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900609 args = (ListenerArgs) msg.obj;
610 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000611 // If the binder death notification for a INsdManagerCallback was received
612 // before any calls are received by NsdService, the clientInfo would be
613 // cleared and cause NPE. Add a null check here to prevent this corner case.
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900614 if (clientInfo == null) {
paulhub2225702021-11-17 09:35:33 +0800615 Log.e(TAG, "Unknown connector in unregistration");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700616 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700617 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900618 id = clientInfo.mClientIds.get(clientId);
619 removeRequestMap(clientId, id, clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700620 if (unregisterService(id)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900621 clientInfo.onUnregisterServiceSucceeded(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700622 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900623 clientInfo.onUnregisterServiceFailed(
624 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700625 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700626 break;
627 case NsdManager.RESOLVE_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800628 if (DBG) Log.d(TAG, "Resolve service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900629 args = (ListenerArgs) msg.obj;
630 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000631 // If the binder death notification for a INsdManagerCallback was received
632 // before any calls are received by NsdService, the clientInfo would be
633 // cleared and cause NPE. Add a null check here to prevent this corner case.
634 if (clientInfo == null) {
635 Log.e(TAG, "Unknown connector in resolution");
636 break;
637 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700638
639 if (clientInfo.mResolvedService != null) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900640 clientInfo.onResolveServiceFailed(
641 clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
Irfan Sheriff75006652012-04-17 23:15:29 -0700642 break;
643 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700644
Luke Huang05298582021-06-13 16:52:05 +0000645 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700646 id = getUniqueId();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900647 if (resolveService(id, args.serviceInfo)) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700648 clientInfo.mResolvedService = new NsdServiceInfo();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900649 storeRequestMap(clientId, id, clientInfo, msg.what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700650 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900651 clientInfo.onResolveServiceFailed(
652 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700653 }
654 break;
paulhu2b9ed952022-02-10 21:58:32 +0800655 case MDNS_SERVICE_EVENT:
656 if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
Hugo Benichif0c84092017-04-05 14:43:29 +0900657 return NOT_HANDLED;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700658 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700659 break;
Paul Hu019621e2023-01-13 23:26:49 +0800660 case MDNS_DISCOVERY_MANAGER_EVENT:
661 if (!handleMdnsDiscoveryManagerEvent(msg.arg1, msg.arg2, msg.obj)) {
662 return NOT_HANDLED;
663 }
664 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700665 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900666 return NOT_HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700667 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900668 return HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700669 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700670
paulhu2b9ed952022-02-10 21:58:32 +0800671 private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700672 NsdServiceInfo servInfo;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700673 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
674 if (clientInfo == null) {
paulhu2b9ed952022-02-10 21:58:32 +0800675 Log.e(TAG, String.format("id %d for %d has no client mapping", id, code));
Hugo Benichif0c84092017-04-05 14:43:29 +0900676 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700677 }
678
679 /* This goes in response as msg.arg2 */
Christopher Lane74411222014-04-25 18:39:07 -0700680 int clientId = clientInfo.getClientId(id);
681 if (clientId < 0) {
Vinit Deshapnde930a8512013-06-25 19:45:03 -0700682 // This can happen because of race conditions. For example,
683 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
684 // and we may get in this situation.
paulhu2b9ed952022-02-10 21:58:32 +0800685 Log.d(TAG, String.format("%d for listener id %d that is no longer active",
686 code, id));
Hugo Benichif0c84092017-04-05 14:43:29 +0900687 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700688 }
Hugo Benichi32be63d2017-04-05 14:06:11 +0900689 if (DBG) {
paulhu2b9ed952022-02-10 21:58:32 +0800690 Log.d(TAG, String.format("MDns service event code:%d id=%d", code, id));
Hugo Benichi32be63d2017-04-05 14:06:11 +0900691 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700692 switch (code) {
paulhu2b9ed952022-02-10 21:58:32 +0800693 case IMDnsEventListener.SERVICE_FOUND: {
694 final DiscoveryInfo info = (DiscoveryInfo) obj;
695 final String name = info.serviceName;
696 final String type = info.registrationType;
697 servInfo = new NsdServiceInfo(name, type);
698 final int foundNetId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900699 if (foundNetId == 0L) {
700 // Ignore services that do not have a Network: they are not usable
701 // by apps, as they would need privileged permissions to use
702 // interfaces that do not have an associated Network.
703 break;
704 }
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900705 setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900706 clientInfo.onServiceFound(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700707 break;
paulhu2b9ed952022-02-10 21:58:32 +0800708 }
709 case IMDnsEventListener.SERVICE_LOST: {
710 final DiscoveryInfo info = (DiscoveryInfo) obj;
711 final String name = info.serviceName;
712 final String type = info.registrationType;
713 final int lostNetId = info.netId;
714 servInfo = new NsdServiceInfo(name, type);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900715 // The network could be set to null (netId 0) if it was torn down when the
716 // service is lost
717 // TODO: avoid returning null in that case, possibly by remembering
718 // found services on the same interface index and their network at the time
719 setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900720 clientInfo.onServiceLost(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700721 break;
paulhu2b9ed952022-02-10 21:58:32 +0800722 }
723 case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900724 clientInfo.onDiscoverServicesFailed(
725 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700726 break;
paulhu2b9ed952022-02-10 21:58:32 +0800727 case IMDnsEventListener.SERVICE_REGISTERED: {
728 final RegistrationInfo info = (RegistrationInfo) obj;
729 final String name = info.serviceName;
730 servInfo = new NsdServiceInfo(name, null /* serviceType */);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900731 clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700732 break;
paulhu2b9ed952022-02-10 21:58:32 +0800733 }
734 case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900735 clientInfo.onRegisterServiceFailed(
736 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700737 break;
paulhu2b9ed952022-02-10 21:58:32 +0800738 case IMDnsEventListener.SERVICE_RESOLVED: {
739 final ResolutionInfo info = (ResolutionInfo) obj;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700740 int index = 0;
paulhu2b9ed952022-02-10 21:58:32 +0800741 final String fullName = info.serviceFullName;
742 while (index < fullName.length() && fullName.charAt(index) != '.') {
743 if (fullName.charAt(index) == '\\') {
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700744 ++index;
745 }
746 ++index;
747 }
paulhu2b9ed952022-02-10 21:58:32 +0800748 if (index >= fullName.length()) {
749 Log.e(TAG, "Invalid service found " + fullName);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700750 break;
751 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900752
paulhube186602022-04-12 07:18:23 +0000753 String name = unescape(fullName.substring(0, index));
paulhu2b9ed952022-02-10 21:58:32 +0800754 String rest = fullName.substring(index);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700755 String type = rest.replace(".local.", "");
756
757 clientInfo.mResolvedService.setServiceName(name);
758 clientInfo.mResolvedService.setServiceType(type);
paulhu2b9ed952022-02-10 21:58:32 +0800759 clientInfo.mResolvedService.setPort(info.port);
760 clientInfo.mResolvedService.setTxtRecords(info.txtRecord);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900761 // Network will be added after SERVICE_GET_ADDR_SUCCESS
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700762
763 stopResolveService(id);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800764 removeRequestMap(clientId, id, clientInfo);
765
paulhu2b9ed952022-02-10 21:58:32 +0800766 final int id2 = getUniqueId();
767 if (getAddrInfo(id2, info.hostname, info.interfaceIdx)) {
Dave Plattfeff2af2014-03-07 14:48:22 -0800768 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800769 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900770 clientInfo.onResolveServiceFailed(
771 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700772 clientInfo.mResolvedService = null;
773 }
774 break;
paulhu2b9ed952022-02-10 21:58:32 +0800775 }
776 case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700777 /* NNN resolveId errorCode */
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700778 stopResolveService(id);
779 removeRequestMap(clientId, id, clientInfo);
780 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900781 clientInfo.onResolveServiceFailed(
782 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700783 break;
paulhu2b9ed952022-02-10 21:58:32 +0800784 case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700785 /* NNN resolveId errorCode */
786 stopGetAddrInfo(id);
787 removeRequestMap(clientId, id, clientInfo);
788 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900789 clientInfo.onResolveServiceFailed(
790 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700791 break;
paulhu2b9ed952022-02-10 21:58:32 +0800792 case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900793 /* NNN resolveId hostname ttl addr interfaceIdx netId */
paulhu2b9ed952022-02-10 21:58:32 +0800794 final GetAddressInfo info = (GetAddressInfo) obj;
795 final String address = info.address;
796 final int netId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900797 InetAddress serviceHost = null;
798 try {
paulhu2b9ed952022-02-10 21:58:32 +0800799 serviceHost = InetAddress.getByName(address);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900800 } catch (UnknownHostException e) {
801 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
802 }
803
804 // If the resolved service is on an interface without a network, consider it
805 // as a failure: it would not be usable by apps as they would need
806 // privileged permissions.
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900807 if (netId != NETID_UNSET && serviceHost != null) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900808 clientInfo.mResolvedService.setHost(serviceHost);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900809 setServiceNetworkForCallback(clientInfo.mResolvedService,
810 netId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900811 clientInfo.onResolveServiceSucceeded(
812 clientId, clientInfo.mResolvedService);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900813 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900814 clientInfo.onResolveServiceFailed(
815 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700816 }
817 stopGetAddrInfo(id);
818 removeRequestMap(clientId, id, clientInfo);
819 clientInfo.mResolvedService = null;
820 break;
paulhu2b9ed952022-02-10 21:58:32 +0800821 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700822 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900823 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700824 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900825 return true;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700826 }
Paul Hu019621e2023-01-13 23:26:49 +0800827
828 private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent(final MdnsEvent event) {
829 final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
830 final String serviceType = event.mRequestedServiceType;
831 final String serviceName = serviceInfo.getServiceInstanceName();
832 final NsdServiceInfo servInfo = new NsdServiceInfo(serviceName, serviceType);
833 final Network network = serviceInfo.getNetwork();
834 setServiceNetworkForCallback(
835 servInfo,
836 network == null ? NETID_UNSET : network.netId,
837 serviceInfo.getInterfaceIndex());
838 return servInfo;
839 }
840
841 private boolean handleMdnsDiscoveryManagerEvent(
842 int transactionId, int code, Object obj) {
843 final ClientInfo clientInfo = mIdToClientInfoMap.get(transactionId);
844 if (clientInfo == null) {
845 Log.e(TAG, String.format(
846 "id %d for %d has no client mapping", transactionId, code));
847 return false;
848 }
849
850 final MdnsEvent event = (MdnsEvent) obj;
851 final int clientId = event.mClientId;
852 if (DBG) {
853 Log.d(TAG, String.format("MdnsDiscoveryManager event code=%s transactionId=%d",
854 NsdManager.nameOf(code), transactionId));
855 }
856 switch (code) {
857 case NsdManager.SERVICE_FOUND:
858 clientInfo.onServiceFound(
859 clientId, buildNsdServiceInfoFromMdnsEvent(event));
860 break;
861 default:
862 return false;
863 }
864 return true;
865 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700866 }
867 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700868
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900869 private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
870 switch (netId) {
871 case NETID_UNSET:
872 info.setNetwork(null);
873 break;
874 case INetd.LOCAL_NET_ID:
875 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally
876 // visible / usable for apps, so do not return it. Store the interface
877 // index instead, so at least if the client tries to resolve the service
878 // with that NsdServiceInfo, it will be done on the same interface.
879 // If they recreate the NsdServiceInfo themselves, resolution would be
880 // done on all interfaces as before T, which should also work.
881 info.setNetwork(null);
882 info.setInterfaceIndex(ifaceIdx);
883 break;
884 default:
885 info.setNetwork(new Network(netId));
886 }
887 }
888
paulhube186602022-04-12 07:18:23 +0000889 // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable
890 // for passing to standard system DNS APIs such as res_query() . Thus, make the service name
891 // unescape for getting right service address. See "Notes on DNS Name Escaping" on
892 // external/mdnsresponder/mDNSShared/dns_sd.h for more details.
893 private String unescape(String s) {
894 StringBuilder sb = new StringBuilder(s.length());
895 for (int i = 0; i < s.length(); ++i) {
896 char c = s.charAt(i);
897 if (c == '\\') {
898 if (++i >= s.length()) {
899 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
900 break;
901 }
902 c = s.charAt(i);
903 if (c != '.' && c != '\\') {
904 if (i + 2 >= s.length()) {
905 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
906 break;
907 }
908 c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10
909 + (s.charAt(i + 2) - '0'));
910 i += 2;
911 }
912 }
913 sb.append(c);
914 }
915 return sb.toString();
916 }
917
Hugo Benichi803a2f02017-04-24 11:35:06 +0900918 @VisibleForTesting
paulhu2b9ed952022-02-10 21:58:32 +0800919 NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
Paul Hu4bd98ef2023-01-12 13:42:07 +0800920 this(ctx, handler, cleanupDelayMs, new Dependencies());
921 }
922
923 @VisibleForTesting
924 NsdService(Context ctx, Handler handler, long cleanupDelayMs, Dependencies deps) {
Luke Huang05298582021-06-13 16:52:05 +0000925 mCleanupDelayMs = cleanupDelayMs;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900926 mContext = ctx;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900927 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700928 mNsdStateMachine.start();
paulhu2b9ed952022-02-10 21:58:32 +0800929 mMDnsManager = ctx.getSystemService(MDnsManager.class);
930 mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
Paul Hu4bd98ef2023-01-12 13:42:07 +0800931 if (deps.isMdnsDiscoveryManagerEnabled(ctx)) {
932 mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper());
933 mMdnsSocketClient =
934 new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
935 mMdnsDiscoveryManager =
936 deps.makeMdnsDiscoveryManager(new ExecutorProvider(), mMdnsSocketClient);
937 handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
938 } else {
939 mMdnsSocketProvider = null;
940 mMdnsSocketClient = null;
941 mMdnsDiscoveryManager = null;
942 }
943 }
944
945 /**
946 * Dependencies of NsdService, for injection in tests.
947 */
948 @VisibleForTesting
949 public static class Dependencies {
950 /**
951 * Check whether or not MdnsDiscoveryManager feature is enabled.
952 *
953 * @param context The global context information about an app environment.
954 * @return true if MdnsDiscoveryManager feature is enabled.
955 */
956 public boolean isMdnsDiscoveryManagerEnabled(Context context) {
957 return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY,
958 MDNS_DISCOVERY_MANAGER_VERSION, false /* defaultEnabled */);
959 }
960
961 /**
962 * @see MdnsDiscoveryManager
963 */
964 public MdnsDiscoveryManager makeMdnsDiscoveryManager(
965 ExecutorProvider executorProvider, MdnsSocketClientBase socketClient) {
966 return new MdnsDiscoveryManager(executorProvider, socketClient);
967 }
968
969 /**
970 * @see MdnsSocketProvider
971 */
972 public MdnsSocketProvider makeMdnsSocketProvider(Context context, Looper looper) {
973 return new MdnsSocketProvider(context, looper);
974 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700975 }
976
paulhu1b35e822022-04-08 14:48:41 +0800977 public static NsdService create(Context context) {
Hugo Benichi803a2f02017-04-24 11:35:06 +0900978 HandlerThread thread = new HandlerThread(TAG);
979 thread.start();
980 Handler handler = new Handler(thread.getLooper());
paulhu2b9ed952022-02-10 21:58:32 +0800981 NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700982 return service;
983 }
984
paulhu2b9ed952022-02-10 21:58:32 +0800985 private static class MDnsEventCallback extends IMDnsEventListener.Stub {
986 private final StateMachine mStateMachine;
987
988 MDnsEventCallback(StateMachine sm) {
989 mStateMachine = sm;
990 }
991
992 @Override
993 public void onServiceRegistrationStatus(final RegistrationInfo status) {
994 mStateMachine.sendMessage(
995 MDNS_SERVICE_EVENT, status.result, status.id, status);
996 }
997
998 @Override
999 public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
1000 mStateMachine.sendMessage(
1001 MDNS_SERVICE_EVENT, status.result, status.id, status);
1002 }
1003
1004 @Override
1005 public void onServiceResolutionStatus(final ResolutionInfo status) {
1006 mStateMachine.sendMessage(
1007 MDNS_SERVICE_EVENT, status.result, status.id, status);
1008 }
1009
1010 @Override
1011 public void onGettingServiceAddressStatus(final GetAddressInfo status) {
1012 mStateMachine.sendMessage(
1013 MDNS_SERVICE_EVENT, status.result, status.id, status);
1014 }
1015
1016 @Override
1017 public int getInterfaceVersion() throws RemoteException {
1018 return this.VERSION;
1019 }
1020
1021 @Override
1022 public String getInterfaceHash() throws RemoteException {
1023 return this.HASH;
1024 }
1025 }
1026
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001027 @Override
1028 public INsdServiceConnector connect(INsdManagerCallback cb) {
Hugo Benichi803a2f02017-04-24 11:35:06 +09001029 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001030 final INsdServiceConnector connector = new NsdServiceConnector();
1031 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1032 NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb)));
1033 return connector;
Irfan Sheriff75006652012-04-17 23:15:29 -07001034 }
1035
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001036 private static class ListenerArgs {
1037 public final NsdServiceConnector connector;
1038 public final NsdServiceInfo serviceInfo;
1039 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
1040 this.connector = connector;
1041 this.serviceInfo = serviceInfo;
1042 }
1043 }
1044
1045 private class NsdServiceConnector extends INsdServiceConnector.Stub
1046 implements IBinder.DeathRecipient {
1047 @Override
1048 public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
1049 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1050 NsdManager.REGISTER_SERVICE, 0, listenerKey,
1051 new ListenerArgs(this, serviceInfo)));
1052 }
1053
1054 @Override
1055 public void unregisterService(int listenerKey) {
1056 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1057 NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
1058 new ListenerArgs(this, null)));
1059 }
1060
1061 @Override
1062 public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
1063 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1064 NsdManager.DISCOVER_SERVICES, 0, listenerKey,
1065 new ListenerArgs(this, serviceInfo)));
1066 }
1067
1068 @Override
1069 public void stopDiscovery(int listenerKey) {
1070 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1071 NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
1072 }
1073
1074 @Override
1075 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
1076 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1077 NsdManager.RESOLVE_SERVICE, 0, listenerKey,
1078 new ListenerArgs(this, serviceInfo)));
1079 }
1080
1081 @Override
1082 public void startDaemon() {
1083 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
1084 NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
1085 }
1086
1087 @Override
1088 public void binderDied() {
1089 mNsdStateMachine.sendMessage(
1090 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
1091 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001092 }
1093
Hugo Benichi912db992017-04-24 16:41:03 +09001094 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff52fc83a2012-04-19 10:26:34 -07001095 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff75006652012-04-17 23:15:29 -07001096 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi912db992017-04-24 16:41:03 +09001097 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
1098 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn692107e2012-08-29 18:32:08 -07001099 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff75006652012-04-17 23:15:29 -07001100 }
1101
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001102 private int getUniqueId() {
1103 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
1104 return mUniqueId;
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001105 }
1106
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001107 private boolean registerService(int regId, NsdServiceInfo service) {
Hugo Benichi6d706442017-04-24 16:19:58 +09001108 if (DBG) {
paulhub2225702021-11-17 09:35:33 +08001109 Log.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001110 }
Hugo Benichi6d706442017-04-24 16:19:58 +09001111 String name = service.getServiceName();
1112 String type = service.getServiceType();
1113 int port = service.getPort();
1114 byte[] textRecord = service.getTxtRecord();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001115 final int registerInterface = getNetworkInterfaceIndex(service);
1116 if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) {
Paul Hu360a8e92022-04-26 11:14:14 +08001117 Log.e(TAG, "Interface to register service on not found");
1118 return false;
1119 }
1120 return mMDnsManager.registerService(regId, name, type, port, textRecord, registerInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001121 }
1122
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001123 private boolean unregisterService(int regId) {
paulhu2b9ed952022-02-10 21:58:32 +08001124 return mMDnsManager.stopOperation(regId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001125 }
1126
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001127 private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
paulhu2b9ed952022-02-10 21:58:32 +08001128 final String type = serviceInfo.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001129 final int discoverInterface = getNetworkInterfaceIndex(serviceInfo);
1130 if (serviceInfo.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001131 Log.e(TAG, "Interface to discover service on not found");
1132 return false;
1133 }
paulhu2b9ed952022-02-10 21:58:32 +08001134 return mMDnsManager.discover(discoveryId, type, discoverInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001135 }
1136
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001137 private boolean stopServiceDiscovery(int discoveryId) {
paulhu2b9ed952022-02-10 21:58:32 +08001138 return mMDnsManager.stopOperation(discoveryId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001139 }
1140
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001141 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001142 final String name = service.getServiceName();
1143 final String type = service.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001144 final int resolveInterface = getNetworkInterfaceIndex(service);
1145 if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001146 Log.e(TAG, "Interface to resolve service on not found");
1147 return false;
1148 }
paulhu2b9ed952022-02-10 21:58:32 +08001149 return mMDnsManager.resolve(resolveId, name, type, "local.", resolveInterface);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001150 }
1151
1152 /**
1153 * Guess the interface to use to resolve or discover a service on a specific network.
1154 *
1155 * This is an imperfect guess, as for example the network may be gone or not yet fully
1156 * registered. This is fine as failing is correct if the network is gone, and a client
1157 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
1158 * this is to support the legacy mdnsresponder implementation, which historically resolved
1159 * services on an unspecified network.
1160 */
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +09001161 private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) {
1162 final Network network = serviceInfo.getNetwork();
1163 if (network == null) {
1164 // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was
1165 // provided by NsdService from discovery results, and the service was found on an
1166 // interface that has no app-usable Network).
1167 if (serviceInfo.getInterfaceIndex() != 0) {
1168 return serviceInfo.getInterfaceIndex();
1169 }
1170 return IFACE_IDX_ANY;
1171 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +09001172
1173 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
1174 if (cm == null) {
1175 Log.wtf(TAG, "No ConnectivityManager for resolveService");
1176 return IFACE_IDX_ANY;
1177 }
1178 final LinkProperties lp = cm.getLinkProperties(network);
1179 if (lp == null) return IFACE_IDX_ANY;
1180
1181 // Only resolve on non-stacked interfaces
1182 final NetworkInterface iface;
1183 try {
1184 iface = NetworkInterface.getByName(lp.getInterfaceName());
1185 } catch (SocketException e) {
1186 Log.e(TAG, "Error querying interface", e);
1187 return IFACE_IDX_ANY;
1188 }
1189
1190 if (iface == null) {
1191 Log.e(TAG, "Interface not found: " + lp.getInterfaceName());
1192 return IFACE_IDX_ANY;
1193 }
1194
1195 return iface.getIndex();
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001196 }
1197
1198 private boolean stopResolveService(int resolveId) {
paulhu2b9ed952022-02-10 21:58:32 +08001199 return mMDnsManager.stopOperation(resolveId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001200 }
1201
paulhu2b9ed952022-02-10 21:58:32 +08001202 private boolean getAddrInfo(int resolveId, String hostname, int interfaceIdx) {
1203 return mMDnsManager.getServiceAddress(resolveId, hostname, interfaceIdx);
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001204 }
1205
1206 private boolean stopGetAddrInfo(int resolveId) {
paulhu2b9ed952022-02-10 21:58:32 +08001207 return mMDnsManager.stopOperation(resolveId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001208 }
1209
1210 @Override
Irfan Sheriff75006652012-04-17 23:15:29 -07001211 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
paulhu3ffffe72021-09-16 10:15:22 +08001212 if (!PermissionUtils.checkDumpPermission(mContext, TAG, pw)) return;
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001213
Irfan Sheriff75006652012-04-17 23:15:29 -07001214 for (ClientInfo client : mClients.values()) {
1215 pw.println("Client Info");
1216 pw.println(client);
1217 }
1218
1219 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001220 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001221
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001222 /* Information tracked per client */
1223 private class ClientInfo {
1224
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001225 private static final int MAX_LIMIT = 10;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001226 private final INsdManagerCallback mCb;
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001227 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001228 private NsdServiceInfo mResolvedService;
1229
1230 /* A map from client id to unique id sent to mDns */
Hugo Benichid2552ae2017-04-11 14:42:47 +09001231 private final SparseIntArray mClientIds = new SparseIntArray();
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001232
Dave Plattfeff2af2014-03-07 14:48:22 -08001233 /* A map from client id to the type of the request we had received */
Hugo Benichid2552ae2017-04-11 14:42:47 +09001234 private final SparseIntArray mClientRequests = new SparseIntArray();
Dave Plattfeff2af2014-03-07 14:48:22 -08001235
Paul Hu23fa2022023-01-13 22:57:24 +08001236 /* A map from client id to the MdnsListener */
1237 private final SparseArray<MdnsListener> mListeners = new SparseArray<>();
1238
Luke Huangf7277ed2021-07-12 21:15:10 +08001239 // The target SDK of this client < Build.VERSION_CODES.S
1240 private boolean mIsLegacy = false;
1241
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001242 private ClientInfo(INsdManagerCallback cb) {
1243 mCb = cb;
paulhub2225702021-11-17 09:35:33 +08001244 if (DBG) Log.d(TAG, "New client");
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001245 }
Irfan Sheriff75006652012-04-17 23:15:29 -07001246
1247 @Override
1248 public String toString() {
Jeff Sharkey63465382020-10-17 21:20:13 -06001249 StringBuilder sb = new StringBuilder();
Irfan Sheriff75006652012-04-17 23:15:29 -07001250 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Luke Huangf7277ed2021-07-12 21:15:10 +08001251 sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -07001252 for(int i = 0; i< mClientIds.size(); i++) {
Dave Plattfeff2af2014-03-07 14:48:22 -08001253 int clientID = mClientIds.keyAt(i);
1254 sb.append("clientId ").append(clientID).
1255 append(" mDnsId ").append(mClientIds.valueAt(i)).
1256 append(" type ").append(mClientRequests.get(clientID)).append("\n");
Irfan Sheriff75006652012-04-17 23:15:29 -07001257 }
1258 return sb.toString();
1259 }
Dave Plattfeff2af2014-03-07 14:48:22 -08001260
Luke Huangf7277ed2021-07-12 21:15:10 +08001261 private boolean isLegacy() {
1262 return mIsLegacy;
1263 }
1264
1265 private void setLegacy() {
1266 mIsLegacy = true;
1267 }
1268
Dave Plattfeff2af2014-03-07 14:48:22 -08001269 // Remove any pending requests from the global map when we get rid of a client,
1270 // and send cancellations to the daemon.
1271 private void expungeAllRequests() {
1272 int globalId, clientId, i;
Hugo Benichid2552ae2017-04-11 14:42:47 +09001273 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Dave Plattfeff2af2014-03-07 14:48:22 -08001274 for (i = 0; i < mClientIds.size(); i++) {
1275 clientId = mClientIds.keyAt(i);
1276 globalId = mClientIds.valueAt(i);
1277 mIdToClientInfoMap.remove(globalId);
paulhub2225702021-11-17 09:35:33 +08001278 if (DBG) {
1279 Log.d(TAG, "Terminating client-ID " + clientId
1280 + " global-ID " + globalId + " type " + mClientRequests.get(clientId));
1281 }
Dave Plattfeff2af2014-03-07 14:48:22 -08001282 switch (mClientRequests.get(clientId)) {
1283 case NsdManager.DISCOVER_SERVICES:
1284 stopServiceDiscovery(globalId);
1285 break;
1286 case NsdManager.RESOLVE_SERVICE:
1287 stopResolveService(globalId);
1288 break;
1289 case NsdManager.REGISTER_SERVICE:
1290 unregisterService(globalId);
1291 break;
1292 default:
1293 break;
1294 }
1295 }
1296 mClientIds.clear();
1297 mClientRequests.clear();
1298 }
1299
Paul Hu23fa2022023-01-13 22:57:24 +08001300 void unregisterAllListeners() {
1301 for (int i = 0; i < mListeners.size(); i++) {
1302 final MdnsListener listener = mListeners.valueAt(i);
1303 mMdnsDiscoveryManager.unregisterListener(
1304 listener.getListenedServiceType(), listener);
1305 }
1306 mListeners.clear();
1307 }
1308
Christopher Lane74411222014-04-25 18:39:07 -07001309 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
1310 // return the corresponding listener id. mDnsClient id is also called a global id.
1311 private int getClientId(final int globalId) {
Hugo Benichid2552ae2017-04-11 14:42:47 +09001312 int idx = mClientIds.indexOfValue(globalId);
1313 if (idx < 0) {
1314 return idx;
Christopher Lane74411222014-04-25 18:39:07 -07001315 }
Hugo Benichid2552ae2017-04-11 14:42:47 +09001316 return mClientIds.keyAt(idx);
Christopher Lane74411222014-04-25 18:39:07 -07001317 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +09001318
1319 void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
1320 try {
1321 mCb.onDiscoverServicesStarted(listenerKey, info);
1322 } catch (RemoteException e) {
1323 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
1324 }
1325 }
1326
1327 void onDiscoverServicesFailed(int listenerKey, int error) {
1328 try {
1329 mCb.onDiscoverServicesFailed(listenerKey, error);
1330 } catch (RemoteException e) {
1331 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
1332 }
1333 }
1334
1335 void onServiceFound(int listenerKey, NsdServiceInfo info) {
1336 try {
1337 mCb.onServiceFound(listenerKey, info);
1338 } catch (RemoteException e) {
1339 Log.e(TAG, "Error calling onServiceFound(", e);
1340 }
1341 }
1342
1343 void onServiceLost(int listenerKey, NsdServiceInfo info) {
1344 try {
1345 mCb.onServiceLost(listenerKey, info);
1346 } catch (RemoteException e) {
1347 Log.e(TAG, "Error calling onServiceLost(", e);
1348 }
1349 }
1350
1351 void onStopDiscoveryFailed(int listenerKey, int error) {
1352 try {
1353 mCb.onStopDiscoveryFailed(listenerKey, error);
1354 } catch (RemoteException e) {
1355 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
1356 }
1357 }
1358
1359 void onStopDiscoverySucceeded(int listenerKey) {
1360 try {
1361 mCb.onStopDiscoverySucceeded(listenerKey);
1362 } catch (RemoteException e) {
1363 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
1364 }
1365 }
1366
1367 void onRegisterServiceFailed(int listenerKey, int error) {
1368 try {
1369 mCb.onRegisterServiceFailed(listenerKey, error);
1370 } catch (RemoteException e) {
1371 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
1372 }
1373 }
1374
1375 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1376 try {
1377 mCb.onRegisterServiceSucceeded(listenerKey, info);
1378 } catch (RemoteException e) {
1379 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
1380 }
1381 }
1382
1383 void onUnregisterServiceFailed(int listenerKey, int error) {
1384 try {
1385 mCb.onUnregisterServiceFailed(listenerKey, error);
1386 } catch (RemoteException e) {
1387 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
1388 }
1389 }
1390
1391 void onUnregisterServiceSucceeded(int listenerKey) {
1392 try {
1393 mCb.onUnregisterServiceSucceeded(listenerKey);
1394 } catch (RemoteException e) {
1395 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
1396 }
1397 }
1398
1399 void onResolveServiceFailed(int listenerKey, int error) {
1400 try {
1401 mCb.onResolveServiceFailed(listenerKey, error);
1402 } catch (RemoteException e) {
1403 Log.e(TAG, "Error calling onResolveServiceFailed", e);
1404 }
1405 }
1406
1407 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1408 try {
1409 mCb.onResolveServiceSucceeded(listenerKey, info);
1410 } catch (RemoteException e) {
1411 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
1412 }
1413 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001414 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001415}