blob: 95e6114255f3c55fe938e2c29bfd30d8430ca6d0 [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;
20import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
21
paulhua262cc12019-08-12 16:25:11 +080022import android.content.Context;
Irfan Sheriff75006652012-04-17 23:15:29 -070023import android.content.Intent;
paulhub2225702021-11-17 09:35:33 +080024import android.content.pm.PackageManager;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090025import android.net.ConnectivityManager;
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +090026import android.net.INetd;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090027import android.net.LinkProperties;
28import android.net.Network;
paulhu2b9ed952022-02-10 21:58:32 +080029import android.net.mdns.aidl.DiscoveryInfo;
30import android.net.mdns.aidl.GetAddressInfo;
31import android.net.mdns.aidl.IMDnsEventListener;
32import android.net.mdns.aidl.RegistrationInfo;
33import android.net.mdns.aidl.ResolutionInfo;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070034import android.net.nsd.INsdManager;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090035import android.net.nsd.INsdManagerCallback;
36import android.net.nsd.INsdServiceConnector;
paulhu2b9ed952022-02-10 21:58:32 +080037import android.net.nsd.MDnsManager;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070038import android.net.nsd.NsdManager;
paulhua262cc12019-08-12 16:25:11 +080039import android.net.nsd.NsdServiceInfo;
Hugo Benichi803a2f02017-04-24 11:35:06 +090040import android.os.Handler;
paulhua262cc12019-08-12 16:25:11 +080041import android.os.HandlerThread;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090042import android.os.IBinder;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070043import android.os.Message;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090044import android.os.RemoteException;
Dianne Hackborn692107e2012-08-29 18:32:08 -070045import android.os.UserHandle;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090046import android.util.Log;
47import android.util.Pair;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070048import android.util.SparseArray;
Hugo Benichid2552ae2017-04-11 14:42:47 +090049import android.util.SparseIntArray;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070050
paulhua262cc12019-08-12 16:25:11 +080051import com.android.internal.annotations.VisibleForTesting;
paulhua262cc12019-08-12 16:25:11 +080052import com.android.internal.util.State;
53import com.android.internal.util.StateMachine;
54
Irfan Sheriff77ec5582012-03-22 17:01:39 -070055import java.io.FileDescriptor;
56import java.io.PrintWriter;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070057import java.net.InetAddress;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090058import java.net.NetworkInterface;
59import java.net.SocketException;
60import java.net.UnknownHostException;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070061import java.util.HashMap;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070062
Irfan Sheriff77ec5582012-03-22 17:01:39 -070063/**
64 * Network Service Discovery Service handles remote service discovery operation requests by
65 * implementing the INsdManager interface.
66 *
67 * @hide
68 */
69public class NsdService extends INsdManager.Stub {
70 private static final String TAG = "NsdService";
71 private static final String MDNS_TAG = "mDnsConnector";
72
paulhu2b9ed952022-02-10 21:58:32 +080073 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Luke Huang92860f92021-06-23 06:29:30 +000074 private static final long CLEANUP_DELAY_MS = 10000;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090075 private static final int IFACE_IDX_ANY = 0;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070076
Hugo Benichi32be63d2017-04-05 14:06:11 +090077 private final Context mContext;
Hugo Benichi32be63d2017-04-05 14:06:11 +090078 private final NsdStateMachine mNsdStateMachine;
paulhu2b9ed952022-02-10 21:58:32 +080079 private final MDnsManager mMDnsManager;
80 private final MDnsEventCallback mMDnsEventCallback;
81 // WARNING : Accessing this value in any thread is not safe, it must only be changed in the
82 // state machine thread. If change this outside state machine, it will need to introduce
83 // synchronization.
84 private boolean mIsDaemonStarted = false;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070085
86 /**
87 * Clients receiving asynchronous messages
88 */
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090089 private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
Irfan Sheriff77ec5582012-03-22 17:01:39 -070090
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070091 /* A map from unique id to client info */
Hugo Benichi32be63d2017-04-05 14:06:11 +090092 private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070093
Luke Huang05298582021-06-13 16:52:05 +000094 private final long mCleanupDelayMs;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070095
Hugo Benichi32be63d2017-04-05 14:06:11 +090096 private static final int INVALID_ID = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -070097 private int mUniqueId = 1;
Luke Huangf7277ed2021-07-12 21:15:10 +080098 // The count of the connected legacy clients.
99 private int mLegacyClientCount = 0;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700100
Irfan Sheriff75006652012-04-17 23:15:29 -0700101 private class NsdStateMachine extends StateMachine {
102
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700103 private final DefaultState mDefaultState = new DefaultState();
104 private final DisabledState mDisabledState = new DisabledState();
105 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff75006652012-04-17 23:15:29 -0700106
107 @Override
Wink Saville358f5d42012-05-29 12:40:46 -0700108 protected String getWhatToString(int what) {
Hugo Benichi32be63d2017-04-05 14:06:11 +0900109 return NsdManager.nameOf(what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700110 }
111
Luke Huang92860f92021-06-23 06:29:30 +0000112 private void maybeStartDaemon() {
paulhu2b9ed952022-02-10 21:58:32 +0800113 if (mIsDaemonStarted) {
114 if (DBG) Log.d(TAG, "Daemon is already started.");
115 return;
116 }
117 mMDnsManager.registerEventListener(mMDnsEventCallback);
118 mMDnsManager.startDaemon();
119 mIsDaemonStarted = true;
Luke Huang05298582021-06-13 16:52:05 +0000120 maybeScheduleStop();
121 }
122
paulhu2b9ed952022-02-10 21:58:32 +0800123 private void maybeStopDaemon() {
124 if (!mIsDaemonStarted) {
125 if (DBG) Log.d(TAG, "Daemon has not been started.");
126 return;
127 }
128 mMDnsManager.unregisterEventListener(mMDnsEventCallback);
129 mMDnsManager.stopDaemon();
130 mIsDaemonStarted = false;
131 }
132
Luke Huang92860f92021-06-23 06:29:30 +0000133 private boolean isAnyRequestActive() {
134 return mIdToClientInfoMap.size() != 0;
135 }
136
137 private void scheduleStop() {
138 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
139 }
140 private void maybeScheduleStop() {
Luke Huangf7277ed2021-07-12 21:15:10 +0800141 // The native daemon should stay alive and can't be cleanup
142 // if any legacy client connected.
143 if (!isAnyRequestActive() && mLegacyClientCount == 0) {
Luke Huang92860f92021-06-23 06:29:30 +0000144 scheduleStop();
Luke Huang05298582021-06-13 16:52:05 +0000145 }
146 }
147
Luke Huang92860f92021-06-23 06:29:30 +0000148 private void cancelStop() {
Luke Huang05298582021-06-13 16:52:05 +0000149 this.removeMessages(NsdManager.DAEMON_CLEANUP);
150 }
151
Hugo Benichi803a2f02017-04-24 11:35:06 +0900152 NsdStateMachine(String name, Handler handler) {
153 super(name, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700154 addState(mDefaultState);
155 addState(mDisabledState, mDefaultState);
156 addState(mEnabledState, mDefaultState);
paulhu5568f452021-11-30 13:31:29 +0800157 State initialState = mEnabledState;
Hugo Benichi912db992017-04-24 16:41:03 +0900158 setInitialState(initialState);
Wink Saville358f5d42012-05-29 12:40:46 -0700159 setLogRecSize(25);
Irfan Sheriff75006652012-04-17 23:15:29 -0700160 }
161
162 class DefaultState extends State {
163 @Override
164 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900165 final ClientInfo cInfo;
166 final int clientId = msg.arg2;
Irfan Sheriff75006652012-04-17 23:15:29 -0700167 switch (msg.what) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900168 case NsdManager.REGISTER_CLIENT:
169 final Pair<NsdServiceConnector, INsdManagerCallback> arg =
170 (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj;
171 final INsdManagerCallback cb = arg.second;
172 try {
173 cb.asBinder().linkToDeath(arg.first, 0);
174 cInfo = new ClientInfo(cb);
175 mClients.put(arg.first, cInfo);
176 } catch (RemoteException e) {
177 Log.w(TAG, "Client " + clientId + " has already died");
Irfan Sheriff75006652012-04-17 23:15:29 -0700178 }
179 break;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900180 case NsdManager.UNREGISTER_CLIENT:
181 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
182 cInfo = mClients.remove(connector);
Dave Plattfeff2af2014-03-07 14:48:22 -0800183 if (cInfo != null) {
184 cInfo.expungeAllRequests();
Luke Huangf7277ed2021-07-12 21:15:10 +0800185 if (cInfo.isLegacy()) {
186 mLegacyClientCount -= 1;
187 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800188 }
Luke Huangf7277ed2021-07-12 21:15:10 +0800189 maybeScheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700190 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700191 case NsdManager.DISCOVER_SERVICES:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900192 cInfo = getClientInfoForReply(msg);
193 if (cInfo != null) {
194 cInfo.onDiscoverServicesFailed(
195 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
196 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700197 break;
198 case NsdManager.STOP_DISCOVERY:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900199 cInfo = getClientInfoForReply(msg);
200 if (cInfo != null) {
201 cInfo.onStopDiscoveryFailed(
202 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
203 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700204 break;
205 case NsdManager.REGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900206 cInfo = getClientInfoForReply(msg);
207 if (cInfo != null) {
208 cInfo.onRegisterServiceFailed(
209 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
210 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700211 break;
212 case NsdManager.UNREGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900213 cInfo = getClientInfoForReply(msg);
214 if (cInfo != null) {
215 cInfo.onUnregisterServiceFailed(
216 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
217 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700218 break;
219 case NsdManager.RESOLVE_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900220 cInfo = getClientInfoForReply(msg);
221 if (cInfo != null) {
222 cInfo.onResolveServiceFailed(
223 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
224 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700225 break;
Luke Huang05298582021-06-13 16:52:05 +0000226 case NsdManager.DAEMON_CLEANUP:
paulhu2b9ed952022-02-10 21:58:32 +0800227 maybeStopDaemon();
Luke Huang05298582021-06-13 16:52:05 +0000228 break;
Luke Huangf7277ed2021-07-12 21:15:10 +0800229 // This event should be only sent by the legacy (target SDK < S) clients.
230 // Mark the sending client as legacy.
231 case NsdManager.DAEMON_STARTUP:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900232 cInfo = getClientInfoForReply(msg);
Luke Huangf7277ed2021-07-12 21:15:10 +0800233 if (cInfo != null) {
234 cancelStop();
235 cInfo.setLegacy();
236 mLegacyClientCount += 1;
237 maybeStartDaemon();
238 }
239 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700240 default:
paulhub2225702021-11-17 09:35:33 +0800241 Log.e(TAG, "Unhandled " + msg);
Irfan Sheriff75006652012-04-17 23:15:29 -0700242 return NOT_HANDLED;
243 }
244 return HANDLED;
245 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900246
247 private ClientInfo getClientInfoForReply(Message msg) {
248 final ListenerArgs args = (ListenerArgs) msg.obj;
249 return mClients.get(args.connector);
250 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700251 }
252
253 class DisabledState extends State {
254 @Override
255 public void enter() {
256 sendNsdStateChangeBroadcast(false);
257 }
258
259 @Override
260 public boolean processMessage(Message msg) {
261 switch (msg.what) {
262 case NsdManager.ENABLE:
263 transitionTo(mEnabledState);
264 break;
265 default:
266 return NOT_HANDLED;
267 }
268 return HANDLED;
269 }
270 }
271
272 class EnabledState extends State {
273 @Override
274 public void enter() {
275 sendNsdStateChangeBroadcast(true);
Irfan Sheriff75006652012-04-17 23:15:29 -0700276 }
277
278 @Override
279 public void exit() {
Luke Huang05298582021-06-13 16:52:05 +0000280 // TODO: it is incorrect to stop the daemon without expunging all requests
281 // and sending error callbacks to clients.
Luke Huang92860f92021-06-23 06:29:30 +0000282 scheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700283 }
284
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700285 private boolean requestLimitReached(ClientInfo clientInfo) {
286 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
paulhub2225702021-11-17 09:35:33 +0800287 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700288 return true;
289 }
290 return false;
291 }
292
Dave Plattfeff2af2014-03-07 14:48:22 -0800293 private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700294 clientInfo.mClientIds.put(clientId, globalId);
Dave Plattfeff2af2014-03-07 14:48:22 -0800295 clientInfo.mClientRequests.put(clientId, what);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700296 mIdToClientInfoMap.put(globalId, clientInfo);
Luke Huang05298582021-06-13 16:52:05 +0000297 // Remove the cleanup event because here comes a new request.
298 cancelStop();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700299 }
300
301 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
Hugo Benichid2552ae2017-04-11 14:42:47 +0900302 clientInfo.mClientIds.delete(clientId);
303 clientInfo.mClientRequests.delete(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700304 mIdToClientInfoMap.remove(globalId);
Luke Huang05298582021-06-13 16:52:05 +0000305 maybeScheduleStop();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700306 }
307
Irfan Sheriff75006652012-04-17 23:15:29 -0700308 @Override
309 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900310 final ClientInfo clientInfo;
311 final int id;
312 final int clientId = msg.arg2;
313 final ListenerArgs args;
Irfan Sheriff75006652012-04-17 23:15:29 -0700314 switch (msg.what) {
Irfan Sheriff75006652012-04-17 23:15:29 -0700315 case NsdManager.DISABLE:
316 //TODO: cleanup clients
317 transitionTo(mDisabledState);
318 break;
319 case NsdManager.DISCOVER_SERVICES:
paulhub2225702021-11-17 09:35:33 +0800320 if (DBG) Log.d(TAG, "Discover services");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900321 args = (ListenerArgs) msg.obj;
322 clientInfo = mClients.get(args.connector);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700323
324 if (requestLimitReached(clientInfo)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900325 clientInfo.onDiscoverServicesFailed(
326 clientId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriff75006652012-04-17 23:15:29 -0700327 break;
328 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700329
Luke Huang05298582021-06-13 16:52:05 +0000330 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700331 id = getUniqueId();
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900332 if (discoverServices(id, args.serviceInfo)) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700333 if (DBG) {
paulhub2225702021-11-17 09:35:33 +0800334 Log.d(TAG, "Discover " + msg.arg2 + " " + id
335 + args.serviceInfo.getServiceType());
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700336 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900337 storeRequestMap(clientId, id, clientInfo, msg.what);
338 clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo);
Irfan Sheriff75006652012-04-17 23:15:29 -0700339 } else {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700340 stopServiceDiscovery(id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900341 clientInfo.onDiscoverServicesFailed(clientId,
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700342 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700343 }
344 break;
345 case NsdManager.STOP_DISCOVERY:
paulhub2225702021-11-17 09:35:33 +0800346 if (DBG) Log.d(TAG, "Stop service discovery");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900347 args = (ListenerArgs) msg.obj;
348 clientInfo = mClients.get(args.connector);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700349
350 try {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900351 id = clientInfo.mClientIds.get(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700352 } catch (NullPointerException e) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900353 clientInfo.onStopDiscoveryFailed(
354 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700355 break;
356 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900357 removeRequestMap(clientId, id, clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700358 if (stopServiceDiscovery(id)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900359 clientInfo.onStopDiscoverySucceeded(clientId);
Irfan Sheriff75006652012-04-17 23:15:29 -0700360 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900361 clientInfo.onStopDiscoveryFailed(
362 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700363 }
364 break;
365 case NsdManager.REGISTER_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800366 if (DBG) Log.d(TAG, "Register service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900367 args = (ListenerArgs) msg.obj;
368 clientInfo = mClients.get(args.connector);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700369 if (requestLimitReached(clientInfo)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900370 clientInfo.onRegisterServiceFailed(
371 clientId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700372 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700373 }
374
Luke Huang05298582021-06-13 16:52:05 +0000375 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700376 id = getUniqueId();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900377 if (registerService(id, args.serviceInfo)) {
paulhub2225702021-11-17 09:35:33 +0800378 if (DBG) Log.d(TAG, "Register " + clientId + " " + id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900379 storeRequestMap(clientId, id, clientInfo, msg.what);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700380 // Return success after mDns reports success
Irfan Sheriff75006652012-04-17 23:15:29 -0700381 } else {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700382 unregisterService(id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900383 clientInfo.onRegisterServiceFailed(
384 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700385 }
386 break;
387 case NsdManager.UNREGISTER_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800388 if (DBG) Log.d(TAG, "unregister service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900389 args = (ListenerArgs) msg.obj;
390 clientInfo = mClients.get(args.connector);
391 if (clientInfo == null) {
paulhub2225702021-11-17 09:35:33 +0800392 Log.e(TAG, "Unknown connector in unregistration");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700393 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700394 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900395 id = clientInfo.mClientIds.get(clientId);
396 removeRequestMap(clientId, id, clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700397 if (unregisterService(id)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900398 clientInfo.onUnregisterServiceSucceeded(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700399 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900400 clientInfo.onUnregisterServiceFailed(
401 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700402 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700403 break;
404 case NsdManager.RESOLVE_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800405 if (DBG) Log.d(TAG, "Resolve service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900406 args = (ListenerArgs) msg.obj;
407 clientInfo = mClients.get(args.connector);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700408
409 if (clientInfo.mResolvedService != null) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900410 clientInfo.onResolveServiceFailed(
411 clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
Irfan Sheriff75006652012-04-17 23:15:29 -0700412 break;
413 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700414
Luke Huang05298582021-06-13 16:52:05 +0000415 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700416 id = getUniqueId();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900417 if (resolveService(id, args.serviceInfo)) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700418 clientInfo.mResolvedService = new NsdServiceInfo();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900419 storeRequestMap(clientId, id, clientInfo, msg.what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700420 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900421 clientInfo.onResolveServiceFailed(
422 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700423 }
424 break;
paulhu2b9ed952022-02-10 21:58:32 +0800425 case MDNS_SERVICE_EVENT:
426 if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
Hugo Benichif0c84092017-04-05 14:43:29 +0900427 return NOT_HANDLED;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700428 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700429 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700430 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900431 return NOT_HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700432 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900433 return HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700434 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700435
paulhu2b9ed952022-02-10 21:58:32 +0800436 private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700437 NsdServiceInfo servInfo;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700438 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
439 if (clientInfo == null) {
paulhu2b9ed952022-02-10 21:58:32 +0800440 Log.e(TAG, String.format("id %d for %d has no client mapping", id, code));
Hugo Benichif0c84092017-04-05 14:43:29 +0900441 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700442 }
443
444 /* This goes in response as msg.arg2 */
Christopher Lane74411222014-04-25 18:39:07 -0700445 int clientId = clientInfo.getClientId(id);
446 if (clientId < 0) {
Vinit Deshapnde930a8512013-06-25 19:45:03 -0700447 // This can happen because of race conditions. For example,
448 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
449 // and we may get in this situation.
paulhu2b9ed952022-02-10 21:58:32 +0800450 Log.d(TAG, String.format("%d for listener id %d that is no longer active",
451 code, id));
Hugo Benichif0c84092017-04-05 14:43:29 +0900452 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700453 }
Hugo Benichi32be63d2017-04-05 14:06:11 +0900454 if (DBG) {
paulhu2b9ed952022-02-10 21:58:32 +0800455 Log.d(TAG, String.format("MDns service event code:%d id=%d", code, id));
Hugo Benichi32be63d2017-04-05 14:06:11 +0900456 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700457 switch (code) {
paulhu2b9ed952022-02-10 21:58:32 +0800458 case IMDnsEventListener.SERVICE_FOUND: {
459 final DiscoveryInfo info = (DiscoveryInfo) obj;
460 final String name = info.serviceName;
461 final String type = info.registrationType;
462 servInfo = new NsdServiceInfo(name, type);
463 final int foundNetId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900464 if (foundNetId == 0L) {
465 // Ignore services that do not have a Network: they are not usable
466 // by apps, as they would need privileged permissions to use
467 // interfaces that do not have an associated Network.
468 break;
469 }
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900470 setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900471 clientInfo.onServiceFound(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700472 break;
paulhu2b9ed952022-02-10 21:58:32 +0800473 }
474 case IMDnsEventListener.SERVICE_LOST: {
475 final DiscoveryInfo info = (DiscoveryInfo) obj;
476 final String name = info.serviceName;
477 final String type = info.registrationType;
478 final int lostNetId = info.netId;
479 servInfo = new NsdServiceInfo(name, type);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900480 // The network could be set to null (netId 0) if it was torn down when the
481 // service is lost
482 // TODO: avoid returning null in that case, possibly by remembering
483 // found services on the same interface index and their network at the time
484 setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900485 clientInfo.onServiceLost(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700486 break;
paulhu2b9ed952022-02-10 21:58:32 +0800487 }
488 case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900489 clientInfo.onDiscoverServicesFailed(
490 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700491 break;
paulhu2b9ed952022-02-10 21:58:32 +0800492 case IMDnsEventListener.SERVICE_REGISTERED: {
493 final RegistrationInfo info = (RegistrationInfo) obj;
494 final String name = info.serviceName;
495 servInfo = new NsdServiceInfo(name, null /* serviceType */);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900496 clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700497 break;
paulhu2b9ed952022-02-10 21:58:32 +0800498 }
499 case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900500 clientInfo.onRegisterServiceFailed(
501 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700502 break;
paulhu2b9ed952022-02-10 21:58:32 +0800503 case IMDnsEventListener.SERVICE_RESOLVED: {
504 final ResolutionInfo info = (ResolutionInfo) obj;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700505 int index = 0;
paulhu2b9ed952022-02-10 21:58:32 +0800506 final String fullName = info.serviceFullName;
507 while (index < fullName.length() && fullName.charAt(index) != '.') {
508 if (fullName.charAt(index) == '\\') {
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700509 ++index;
510 }
511 ++index;
512 }
paulhu2b9ed952022-02-10 21:58:32 +0800513 if (index >= fullName.length()) {
514 Log.e(TAG, "Invalid service found " + fullName);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700515 break;
516 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900517
paulhube186602022-04-12 07:18:23 +0000518 String name = unescape(fullName.substring(0, index));
paulhu2b9ed952022-02-10 21:58:32 +0800519 String rest = fullName.substring(index);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700520 String type = rest.replace(".local.", "");
521
522 clientInfo.mResolvedService.setServiceName(name);
523 clientInfo.mResolvedService.setServiceType(type);
paulhu2b9ed952022-02-10 21:58:32 +0800524 clientInfo.mResolvedService.setPort(info.port);
525 clientInfo.mResolvedService.setTxtRecords(info.txtRecord);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900526 // Network will be added after SERVICE_GET_ADDR_SUCCESS
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700527
528 stopResolveService(id);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800529 removeRequestMap(clientId, id, clientInfo);
530
paulhu2b9ed952022-02-10 21:58:32 +0800531 final int id2 = getUniqueId();
532 if (getAddrInfo(id2, info.hostname, info.interfaceIdx)) {
Dave Plattfeff2af2014-03-07 14:48:22 -0800533 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800534 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900535 clientInfo.onResolveServiceFailed(
536 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700537 clientInfo.mResolvedService = null;
538 }
539 break;
paulhu2b9ed952022-02-10 21:58:32 +0800540 }
541 case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700542 /* NNN resolveId errorCode */
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700543 stopResolveService(id);
544 removeRequestMap(clientId, id, clientInfo);
545 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900546 clientInfo.onResolveServiceFailed(
547 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700548 break;
paulhu2b9ed952022-02-10 21:58:32 +0800549 case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700550 /* NNN resolveId errorCode */
551 stopGetAddrInfo(id);
552 removeRequestMap(clientId, id, clientInfo);
553 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900554 clientInfo.onResolveServiceFailed(
555 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700556 break;
paulhu2b9ed952022-02-10 21:58:32 +0800557 case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900558 /* NNN resolveId hostname ttl addr interfaceIdx netId */
paulhu2b9ed952022-02-10 21:58:32 +0800559 final GetAddressInfo info = (GetAddressInfo) obj;
560 final String address = info.address;
561 final int netId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900562 InetAddress serviceHost = null;
563 try {
paulhu2b9ed952022-02-10 21:58:32 +0800564 serviceHost = InetAddress.getByName(address);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900565 } catch (UnknownHostException e) {
566 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
567 }
568
569 // If the resolved service is on an interface without a network, consider it
570 // as a failure: it would not be usable by apps as they would need
571 // privileged permissions.
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900572 if (netId != NETID_UNSET && serviceHost != null) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900573 clientInfo.mResolvedService.setHost(serviceHost);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900574 setServiceNetworkForCallback(clientInfo.mResolvedService,
575 netId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900576 clientInfo.onResolveServiceSucceeded(
577 clientId, clientInfo.mResolvedService);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900578 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900579 clientInfo.onResolveServiceFailed(
580 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700581 }
582 stopGetAddrInfo(id);
583 removeRequestMap(clientId, id, clientInfo);
584 clientInfo.mResolvedService = null;
585 break;
paulhu2b9ed952022-02-10 21:58:32 +0800586 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700587 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900588 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700589 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900590 return true;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700591 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700592 }
593 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700594
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900595 private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
596 switch (netId) {
597 case NETID_UNSET:
598 info.setNetwork(null);
599 break;
600 case INetd.LOCAL_NET_ID:
601 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally
602 // visible / usable for apps, so do not return it. Store the interface
603 // index instead, so at least if the client tries to resolve the service
604 // with that NsdServiceInfo, it will be done on the same interface.
605 // If they recreate the NsdServiceInfo themselves, resolution would be
606 // done on all interfaces as before T, which should also work.
607 info.setNetwork(null);
608 info.setInterfaceIndex(ifaceIdx);
609 break;
610 default:
611 info.setNetwork(new Network(netId));
612 }
613 }
614
paulhube186602022-04-12 07:18:23 +0000615 // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable
616 // for passing to standard system DNS APIs such as res_query() . Thus, make the service name
617 // unescape for getting right service address. See "Notes on DNS Name Escaping" on
618 // external/mdnsresponder/mDNSShared/dns_sd.h for more details.
619 private String unescape(String s) {
620 StringBuilder sb = new StringBuilder(s.length());
621 for (int i = 0; i < s.length(); ++i) {
622 char c = s.charAt(i);
623 if (c == '\\') {
624 if (++i >= s.length()) {
625 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
626 break;
627 }
628 c = s.charAt(i);
629 if (c != '.' && c != '\\') {
630 if (i + 2 >= s.length()) {
631 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
632 break;
633 }
634 c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10
635 + (s.charAt(i + 2) - '0'));
636 i += 2;
637 }
638 }
639 sb.append(c);
640 }
641 return sb.toString();
642 }
643
Hugo Benichi803a2f02017-04-24 11:35:06 +0900644 @VisibleForTesting
paulhu2b9ed952022-02-10 21:58:32 +0800645 NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
Luke Huang05298582021-06-13 16:52:05 +0000646 mCleanupDelayMs = cleanupDelayMs;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900647 mContext = ctx;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900648 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700649 mNsdStateMachine.start();
paulhu2b9ed952022-02-10 21:58:32 +0800650 mMDnsManager = ctx.getSystemService(MDnsManager.class);
651 mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700652 }
653
paulhu1b35e822022-04-08 14:48:41 +0800654 public static NsdService create(Context context) {
Hugo Benichi803a2f02017-04-24 11:35:06 +0900655 HandlerThread thread = new HandlerThread(TAG);
656 thread.start();
657 Handler handler = new Handler(thread.getLooper());
paulhu2b9ed952022-02-10 21:58:32 +0800658 NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700659 return service;
660 }
661
paulhu2b9ed952022-02-10 21:58:32 +0800662 private static class MDnsEventCallback extends IMDnsEventListener.Stub {
663 private final StateMachine mStateMachine;
664
665 MDnsEventCallback(StateMachine sm) {
666 mStateMachine = sm;
667 }
668
669 @Override
670 public void onServiceRegistrationStatus(final RegistrationInfo status) {
671 mStateMachine.sendMessage(
672 MDNS_SERVICE_EVENT, status.result, status.id, status);
673 }
674
675 @Override
676 public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
677 mStateMachine.sendMessage(
678 MDNS_SERVICE_EVENT, status.result, status.id, status);
679 }
680
681 @Override
682 public void onServiceResolutionStatus(final ResolutionInfo status) {
683 mStateMachine.sendMessage(
684 MDNS_SERVICE_EVENT, status.result, status.id, status);
685 }
686
687 @Override
688 public void onGettingServiceAddressStatus(final GetAddressInfo status) {
689 mStateMachine.sendMessage(
690 MDNS_SERVICE_EVENT, status.result, status.id, status);
691 }
692
693 @Override
694 public int getInterfaceVersion() throws RemoteException {
695 return this.VERSION;
696 }
697
698 @Override
699 public String getInterfaceHash() throws RemoteException {
700 return this.HASH;
701 }
702 }
703
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900704 @Override
705 public INsdServiceConnector connect(INsdManagerCallback cb) {
Hugo Benichi803a2f02017-04-24 11:35:06 +0900706 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900707 final INsdServiceConnector connector = new NsdServiceConnector();
708 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
709 NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb)));
710 return connector;
Irfan Sheriff75006652012-04-17 23:15:29 -0700711 }
712
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900713 private static class ListenerArgs {
714 public final NsdServiceConnector connector;
715 public final NsdServiceInfo serviceInfo;
716 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
717 this.connector = connector;
718 this.serviceInfo = serviceInfo;
719 }
720 }
721
722 private class NsdServiceConnector extends INsdServiceConnector.Stub
723 implements IBinder.DeathRecipient {
724 @Override
725 public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
726 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
727 NsdManager.REGISTER_SERVICE, 0, listenerKey,
728 new ListenerArgs(this, serviceInfo)));
729 }
730
731 @Override
732 public void unregisterService(int listenerKey) {
733 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
734 NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
735 new ListenerArgs(this, null)));
736 }
737
738 @Override
739 public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
740 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
741 NsdManager.DISCOVER_SERVICES, 0, listenerKey,
742 new ListenerArgs(this, serviceInfo)));
743 }
744
745 @Override
746 public void stopDiscovery(int listenerKey) {
747 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
748 NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
749 }
750
751 @Override
752 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
753 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
754 NsdManager.RESOLVE_SERVICE, 0, listenerKey,
755 new ListenerArgs(this, serviceInfo)));
756 }
757
758 @Override
759 public void startDaemon() {
760 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
761 NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
762 }
763
764 @Override
765 public void binderDied() {
766 mNsdStateMachine.sendMessage(
767 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
768 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700769 }
770
Hugo Benichi912db992017-04-24 16:41:03 +0900771 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff52fc83a2012-04-19 10:26:34 -0700772 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff75006652012-04-17 23:15:29 -0700773 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi912db992017-04-24 16:41:03 +0900774 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
775 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn692107e2012-08-29 18:32:08 -0700776 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff75006652012-04-17 23:15:29 -0700777 }
778
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700779 private int getUniqueId() {
780 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
781 return mUniqueId;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700782 }
783
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700784 private boolean registerService(int regId, NsdServiceInfo service) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900785 if (DBG) {
paulhub2225702021-11-17 09:35:33 +0800786 Log.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700787 }
Hugo Benichi6d706442017-04-24 16:19:58 +0900788 String name = service.getServiceName();
789 String type = service.getServiceType();
790 int port = service.getPort();
791 byte[] textRecord = service.getTxtRecord();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900792 final int registerInterface = getNetworkInterfaceIndex(service);
793 if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) {
Paul Hu360a8e92022-04-26 11:14:14 +0800794 Log.e(TAG, "Interface to register service on not found");
795 return false;
796 }
797 return mMDnsManager.registerService(regId, name, type, port, textRecord, registerInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700798 }
799
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700800 private boolean unregisterService(int regId) {
paulhu2b9ed952022-02-10 21:58:32 +0800801 return mMDnsManager.stopOperation(regId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700802 }
803
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900804 private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
paulhu2b9ed952022-02-10 21:58:32 +0800805 final String type = serviceInfo.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900806 final int discoverInterface = getNetworkInterfaceIndex(serviceInfo);
807 if (serviceInfo.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900808 Log.e(TAG, "Interface to discover service on not found");
809 return false;
810 }
paulhu2b9ed952022-02-10 21:58:32 +0800811 return mMDnsManager.discover(discoveryId, type, discoverInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700812 }
813
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700814 private boolean stopServiceDiscovery(int discoveryId) {
paulhu2b9ed952022-02-10 21:58:32 +0800815 return mMDnsManager.stopOperation(discoveryId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700816 }
817
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700818 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900819 final String name = service.getServiceName();
820 final String type = service.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900821 final int resolveInterface = getNetworkInterfaceIndex(service);
822 if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900823 Log.e(TAG, "Interface to resolve service on not found");
824 return false;
825 }
paulhu2b9ed952022-02-10 21:58:32 +0800826 return mMDnsManager.resolve(resolveId, name, type, "local.", resolveInterface);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900827 }
828
829 /**
830 * Guess the interface to use to resolve or discover a service on a specific network.
831 *
832 * This is an imperfect guess, as for example the network may be gone or not yet fully
833 * registered. This is fine as failing is correct if the network is gone, and a client
834 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
835 * this is to support the legacy mdnsresponder implementation, which historically resolved
836 * services on an unspecified network.
837 */
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900838 private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) {
839 final Network network = serviceInfo.getNetwork();
840 if (network == null) {
841 // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was
842 // provided by NsdService from discovery results, and the service was found on an
843 // interface that has no app-usable Network).
844 if (serviceInfo.getInterfaceIndex() != 0) {
845 return serviceInfo.getInterfaceIndex();
846 }
847 return IFACE_IDX_ANY;
848 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900849
850 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
851 if (cm == null) {
852 Log.wtf(TAG, "No ConnectivityManager for resolveService");
853 return IFACE_IDX_ANY;
854 }
855 final LinkProperties lp = cm.getLinkProperties(network);
856 if (lp == null) return IFACE_IDX_ANY;
857
858 // Only resolve on non-stacked interfaces
859 final NetworkInterface iface;
860 try {
861 iface = NetworkInterface.getByName(lp.getInterfaceName());
862 } catch (SocketException e) {
863 Log.e(TAG, "Error querying interface", e);
864 return IFACE_IDX_ANY;
865 }
866
867 if (iface == null) {
868 Log.e(TAG, "Interface not found: " + lp.getInterfaceName());
869 return IFACE_IDX_ANY;
870 }
871
872 return iface.getIndex();
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700873 }
874
875 private boolean stopResolveService(int resolveId) {
paulhu2b9ed952022-02-10 21:58:32 +0800876 return mMDnsManager.stopOperation(resolveId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700877 }
878
paulhu2b9ed952022-02-10 21:58:32 +0800879 private boolean getAddrInfo(int resolveId, String hostname, int interfaceIdx) {
880 return mMDnsManager.getServiceAddress(resolveId, hostname, interfaceIdx);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700881 }
882
883 private boolean stopGetAddrInfo(int resolveId) {
paulhu2b9ed952022-02-10 21:58:32 +0800884 return mMDnsManager.stopOperation(resolveId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700885 }
886
887 @Override
Irfan Sheriff75006652012-04-17 23:15:29 -0700888 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
paulhub2225702021-11-17 09:35:33 +0800889 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
890 != PackageManager.PERMISSION_GRANTED) {
891 pw.println("Permission Denial: can't dump " + TAG
892 + " due to missing android.permission.DUMP permission");
893 return;
894 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700895
Irfan Sheriff75006652012-04-17 23:15:29 -0700896 for (ClientInfo client : mClients.values()) {
897 pw.println("Client Info");
898 pw.println(client);
899 }
900
901 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700902 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700903
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700904 /* Information tracked per client */
905 private class ClientInfo {
906
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700907 private static final int MAX_LIMIT = 10;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900908 private final INsdManagerCallback mCb;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700909 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700910 private NsdServiceInfo mResolvedService;
911
912 /* A map from client id to unique id sent to mDns */
Hugo Benichid2552ae2017-04-11 14:42:47 +0900913 private final SparseIntArray mClientIds = new SparseIntArray();
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700914
Dave Plattfeff2af2014-03-07 14:48:22 -0800915 /* A map from client id to the type of the request we had received */
Hugo Benichid2552ae2017-04-11 14:42:47 +0900916 private final SparseIntArray mClientRequests = new SparseIntArray();
Dave Plattfeff2af2014-03-07 14:48:22 -0800917
Luke Huangf7277ed2021-07-12 21:15:10 +0800918 // The target SDK of this client < Build.VERSION_CODES.S
919 private boolean mIsLegacy = false;
920
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900921 private ClientInfo(INsdManagerCallback cb) {
922 mCb = cb;
paulhub2225702021-11-17 09:35:33 +0800923 if (DBG) Log.d(TAG, "New client");
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700924 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700925
926 @Override
927 public String toString() {
Jeff Sharkey63465382020-10-17 21:20:13 -0600928 StringBuilder sb = new StringBuilder();
Irfan Sheriff75006652012-04-17 23:15:29 -0700929 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Luke Huangf7277ed2021-07-12 21:15:10 +0800930 sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700931 for(int i = 0; i< mClientIds.size(); i++) {
Dave Plattfeff2af2014-03-07 14:48:22 -0800932 int clientID = mClientIds.keyAt(i);
933 sb.append("clientId ").append(clientID).
934 append(" mDnsId ").append(mClientIds.valueAt(i)).
935 append(" type ").append(mClientRequests.get(clientID)).append("\n");
Irfan Sheriff75006652012-04-17 23:15:29 -0700936 }
937 return sb.toString();
938 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800939
Luke Huangf7277ed2021-07-12 21:15:10 +0800940 private boolean isLegacy() {
941 return mIsLegacy;
942 }
943
944 private void setLegacy() {
945 mIsLegacy = true;
946 }
947
Dave Plattfeff2af2014-03-07 14:48:22 -0800948 // Remove any pending requests from the global map when we get rid of a client,
949 // and send cancellations to the daemon.
950 private void expungeAllRequests() {
951 int globalId, clientId, i;
Hugo Benichid2552ae2017-04-11 14:42:47 +0900952 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Dave Plattfeff2af2014-03-07 14:48:22 -0800953 for (i = 0; i < mClientIds.size(); i++) {
954 clientId = mClientIds.keyAt(i);
955 globalId = mClientIds.valueAt(i);
956 mIdToClientInfoMap.remove(globalId);
paulhub2225702021-11-17 09:35:33 +0800957 if (DBG) {
958 Log.d(TAG, "Terminating client-ID " + clientId
959 + " global-ID " + globalId + " type " + mClientRequests.get(clientId));
960 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800961 switch (mClientRequests.get(clientId)) {
962 case NsdManager.DISCOVER_SERVICES:
963 stopServiceDiscovery(globalId);
964 break;
965 case NsdManager.RESOLVE_SERVICE:
966 stopResolveService(globalId);
967 break;
968 case NsdManager.REGISTER_SERVICE:
969 unregisterService(globalId);
970 break;
971 default:
972 break;
973 }
974 }
975 mClientIds.clear();
976 mClientRequests.clear();
977 }
978
Christopher Lane74411222014-04-25 18:39:07 -0700979 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
980 // return the corresponding listener id. mDnsClient id is also called a global id.
981 private int getClientId(final int globalId) {
Hugo Benichid2552ae2017-04-11 14:42:47 +0900982 int idx = mClientIds.indexOfValue(globalId);
983 if (idx < 0) {
984 return idx;
Christopher Lane74411222014-04-25 18:39:07 -0700985 }
Hugo Benichid2552ae2017-04-11 14:42:47 +0900986 return mClientIds.keyAt(idx);
Christopher Lane74411222014-04-25 18:39:07 -0700987 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900988
989 void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
990 try {
991 mCb.onDiscoverServicesStarted(listenerKey, info);
992 } catch (RemoteException e) {
993 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
994 }
995 }
996
997 void onDiscoverServicesFailed(int listenerKey, int error) {
998 try {
999 mCb.onDiscoverServicesFailed(listenerKey, error);
1000 } catch (RemoteException e) {
1001 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
1002 }
1003 }
1004
1005 void onServiceFound(int listenerKey, NsdServiceInfo info) {
1006 try {
1007 mCb.onServiceFound(listenerKey, info);
1008 } catch (RemoteException e) {
1009 Log.e(TAG, "Error calling onServiceFound(", e);
1010 }
1011 }
1012
1013 void onServiceLost(int listenerKey, NsdServiceInfo info) {
1014 try {
1015 mCb.onServiceLost(listenerKey, info);
1016 } catch (RemoteException e) {
1017 Log.e(TAG, "Error calling onServiceLost(", e);
1018 }
1019 }
1020
1021 void onStopDiscoveryFailed(int listenerKey, int error) {
1022 try {
1023 mCb.onStopDiscoveryFailed(listenerKey, error);
1024 } catch (RemoteException e) {
1025 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
1026 }
1027 }
1028
1029 void onStopDiscoverySucceeded(int listenerKey) {
1030 try {
1031 mCb.onStopDiscoverySucceeded(listenerKey);
1032 } catch (RemoteException e) {
1033 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
1034 }
1035 }
1036
1037 void onRegisterServiceFailed(int listenerKey, int error) {
1038 try {
1039 mCb.onRegisterServiceFailed(listenerKey, error);
1040 } catch (RemoteException e) {
1041 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
1042 }
1043 }
1044
1045 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1046 try {
1047 mCb.onRegisterServiceSucceeded(listenerKey, info);
1048 } catch (RemoteException e) {
1049 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
1050 }
1051 }
1052
1053 void onUnregisterServiceFailed(int listenerKey, int error) {
1054 try {
1055 mCb.onUnregisterServiceFailed(listenerKey, error);
1056 } catch (RemoteException e) {
1057 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
1058 }
1059 }
1060
1061 void onUnregisterServiceSucceeded(int listenerKey) {
1062 try {
1063 mCb.onUnregisterServiceSucceeded(listenerKey);
1064 } catch (RemoteException e) {
1065 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
1066 }
1067 }
1068
1069 void onResolveServiceFailed(int listenerKey, int error) {
1070 try {
1071 mCb.onResolveServiceFailed(listenerKey, error);
1072 } catch (RemoteException e) {
1073 Log.e(TAG, "Error calling onResolveServiceFailed", e);
1074 }
1075 }
1076
1077 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1078 try {
1079 mCb.onResolveServiceSucceeded(listenerKey, info);
1080 } catch (RemoteException e) {
1081 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
1082 }
1083 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001084 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001085}