blob: 1226eea7bf6fb59c12cbffc25f1c1ce4ef827c2f [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;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090024import android.net.ConnectivityManager;
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +090025import android.net.INetd;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +090026import android.net.LinkProperties;
27import android.net.Network;
paulhu2b9ed952022-02-10 21:58:32 +080028import android.net.mdns.aidl.DiscoveryInfo;
29import android.net.mdns.aidl.GetAddressInfo;
30import android.net.mdns.aidl.IMDnsEventListener;
31import android.net.mdns.aidl.RegistrationInfo;
32import android.net.mdns.aidl.ResolutionInfo;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070033import android.net.nsd.INsdManager;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090034import android.net.nsd.INsdManagerCallback;
35import android.net.nsd.INsdServiceConnector;
paulhu2b9ed952022-02-10 21:58:32 +080036import android.net.nsd.MDnsManager;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070037import android.net.nsd.NsdManager;
paulhua262cc12019-08-12 16:25:11 +080038import android.net.nsd.NsdServiceInfo;
Hugo Benichi803a2f02017-04-24 11:35:06 +090039import android.os.Handler;
paulhua262cc12019-08-12 16:25:11 +080040import android.os.HandlerThread;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090041import android.os.IBinder;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070042import android.os.Message;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090043import android.os.RemoteException;
Dianne Hackborn692107e2012-08-29 18:32:08 -070044import android.os.UserHandle;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +090045import android.util.Log;
46import android.util.Pair;
Irfan Sheriffe4c42f42012-05-03 16:44:27 -070047import android.util.SparseArray;
Hugo Benichid2552ae2017-04-11 14:42:47 +090048import android.util.SparseIntArray;
Irfan Sheriff77ec5582012-03-22 17:01:39 -070049
paulhua262cc12019-08-12 16:25:11 +080050import com.android.internal.annotations.VisibleForTesting;
paulhua262cc12019-08-12 16:25:11 +080051import com.android.internal.util.State;
52import com.android.internal.util.StateMachine;
paulhu3ffffe72021-09-16 10:15:22 +080053import com.android.net.module.util.PermissionUtils;
paulhua262cc12019-08-12 16:25:11 +080054
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();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700104 private final EnabledState mEnabledState = new EnabledState();
Irfan Sheriff75006652012-04-17 23:15:29 -0700105
106 @Override
Wink Saville358f5d42012-05-29 12:40:46 -0700107 protected String getWhatToString(int what) {
Hugo Benichi32be63d2017-04-05 14:06:11 +0900108 return NsdManager.nameOf(what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700109 }
110
Luke Huang92860f92021-06-23 06:29:30 +0000111 private void maybeStartDaemon() {
paulhu2b9ed952022-02-10 21:58:32 +0800112 if (mIsDaemonStarted) {
113 if (DBG) Log.d(TAG, "Daemon is already started.");
114 return;
115 }
116 mMDnsManager.registerEventListener(mMDnsEventCallback);
117 mMDnsManager.startDaemon();
118 mIsDaemonStarted = true;
Luke Huang05298582021-06-13 16:52:05 +0000119 maybeScheduleStop();
120 }
121
paulhu2b9ed952022-02-10 21:58:32 +0800122 private void maybeStopDaemon() {
123 if (!mIsDaemonStarted) {
124 if (DBG) Log.d(TAG, "Daemon has not been started.");
125 return;
126 }
127 mMDnsManager.unregisterEventListener(mMDnsEventCallback);
128 mMDnsManager.stopDaemon();
129 mIsDaemonStarted = false;
130 }
131
Luke Huang92860f92021-06-23 06:29:30 +0000132 private boolean isAnyRequestActive() {
133 return mIdToClientInfoMap.size() != 0;
134 }
135
136 private void scheduleStop() {
137 sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
138 }
139 private void maybeScheduleStop() {
Luke Huangf7277ed2021-07-12 21:15:10 +0800140 // The native daemon should stay alive and can't be cleanup
141 // if any legacy client connected.
142 if (!isAnyRequestActive() && mLegacyClientCount == 0) {
Luke Huang92860f92021-06-23 06:29:30 +0000143 scheduleStop();
Luke Huang05298582021-06-13 16:52:05 +0000144 }
145 }
146
Luke Huang92860f92021-06-23 06:29:30 +0000147 private void cancelStop() {
Luke Huang05298582021-06-13 16:52:05 +0000148 this.removeMessages(NsdManager.DAEMON_CLEANUP);
149 }
150
Hugo Benichi803a2f02017-04-24 11:35:06 +0900151 NsdStateMachine(String name, Handler handler) {
152 super(name, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700153 addState(mDefaultState);
Irfan Sheriff75006652012-04-17 23:15:29 -0700154 addState(mEnabledState, mDefaultState);
paulhu5568f452021-11-30 13:31:29 +0800155 State initialState = mEnabledState;
Hugo Benichi912db992017-04-24 16:41:03 +0900156 setInitialState(initialState);
Wink Saville358f5d42012-05-29 12:40:46 -0700157 setLogRecSize(25);
Irfan Sheriff75006652012-04-17 23:15:29 -0700158 }
159
160 class DefaultState extends State {
161 @Override
162 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900163 final ClientInfo cInfo;
164 final int clientId = msg.arg2;
Irfan Sheriff75006652012-04-17 23:15:29 -0700165 switch (msg.what) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900166 case NsdManager.REGISTER_CLIENT:
167 final Pair<NsdServiceConnector, INsdManagerCallback> arg =
168 (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj;
169 final INsdManagerCallback cb = arg.second;
170 try {
171 cb.asBinder().linkToDeath(arg.first, 0);
172 cInfo = new ClientInfo(cb);
173 mClients.put(arg.first, cInfo);
174 } catch (RemoteException e) {
175 Log.w(TAG, "Client " + clientId + " has already died");
Irfan Sheriff75006652012-04-17 23:15:29 -0700176 }
177 break;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900178 case NsdManager.UNREGISTER_CLIENT:
179 final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
180 cInfo = mClients.remove(connector);
Dave Plattfeff2af2014-03-07 14:48:22 -0800181 if (cInfo != null) {
182 cInfo.expungeAllRequests();
Luke Huangf7277ed2021-07-12 21:15:10 +0800183 if (cInfo.isLegacy()) {
184 mLegacyClientCount -= 1;
185 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800186 }
Luke Huangf7277ed2021-07-12 21:15:10 +0800187 maybeScheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700188 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700189 case NsdManager.DISCOVER_SERVICES:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900190 cInfo = getClientInfoForReply(msg);
191 if (cInfo != null) {
192 cInfo.onDiscoverServicesFailed(
193 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
194 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700195 break;
196 case NsdManager.STOP_DISCOVERY:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900197 cInfo = getClientInfoForReply(msg);
198 if (cInfo != null) {
199 cInfo.onStopDiscoveryFailed(
200 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
201 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700202 break;
203 case NsdManager.REGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900204 cInfo = getClientInfoForReply(msg);
205 if (cInfo != null) {
206 cInfo.onRegisterServiceFailed(
207 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
208 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700209 break;
210 case NsdManager.UNREGISTER_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900211 cInfo = getClientInfoForReply(msg);
212 if (cInfo != null) {
213 cInfo.onUnregisterServiceFailed(
214 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
215 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700216 break;
217 case NsdManager.RESOLVE_SERVICE:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900218 cInfo = getClientInfoForReply(msg);
219 if (cInfo != null) {
220 cInfo.onResolveServiceFailed(
221 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
222 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700223 break;
Luke Huang05298582021-06-13 16:52:05 +0000224 case NsdManager.DAEMON_CLEANUP:
paulhu2b9ed952022-02-10 21:58:32 +0800225 maybeStopDaemon();
Luke Huang05298582021-06-13 16:52:05 +0000226 break;
Luke Huangf7277ed2021-07-12 21:15:10 +0800227 // This event should be only sent by the legacy (target SDK < S) clients.
228 // Mark the sending client as legacy.
229 case NsdManager.DAEMON_STARTUP:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900230 cInfo = getClientInfoForReply(msg);
Luke Huangf7277ed2021-07-12 21:15:10 +0800231 if (cInfo != null) {
232 cancelStop();
233 cInfo.setLegacy();
234 mLegacyClientCount += 1;
235 maybeStartDaemon();
236 }
237 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700238 default:
paulhub2225702021-11-17 09:35:33 +0800239 Log.e(TAG, "Unhandled " + msg);
Irfan Sheriff75006652012-04-17 23:15:29 -0700240 return NOT_HANDLED;
241 }
242 return HANDLED;
243 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900244
245 private ClientInfo getClientInfoForReply(Message msg) {
246 final ListenerArgs args = (ListenerArgs) msg.obj;
247 return mClients.get(args.connector);
248 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700249 }
250
Irfan Sheriff75006652012-04-17 23:15:29 -0700251 class EnabledState extends State {
252 @Override
253 public void enter() {
254 sendNsdStateChangeBroadcast(true);
Irfan Sheriff75006652012-04-17 23:15:29 -0700255 }
256
257 @Override
258 public void exit() {
Luke Huang05298582021-06-13 16:52:05 +0000259 // TODO: it is incorrect to stop the daemon without expunging all requests
260 // and sending error callbacks to clients.
Luke Huang92860f92021-06-23 06:29:30 +0000261 scheduleStop();
Irfan Sheriff75006652012-04-17 23:15:29 -0700262 }
263
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700264 private boolean requestLimitReached(ClientInfo clientInfo) {
265 if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
paulhub2225702021-11-17 09:35:33 +0800266 if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700267 return true;
268 }
269 return false;
270 }
271
Dave Plattfeff2af2014-03-07 14:48:22 -0800272 private void storeRequestMap(int clientId, int globalId, ClientInfo clientInfo, int what) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700273 clientInfo.mClientIds.put(clientId, globalId);
Dave Plattfeff2af2014-03-07 14:48:22 -0800274 clientInfo.mClientRequests.put(clientId, what);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700275 mIdToClientInfoMap.put(globalId, clientInfo);
Luke Huang05298582021-06-13 16:52:05 +0000276 // Remove the cleanup event because here comes a new request.
277 cancelStop();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700278 }
279
280 private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
Hugo Benichid2552ae2017-04-11 14:42:47 +0900281 clientInfo.mClientIds.delete(clientId);
282 clientInfo.mClientRequests.delete(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700283 mIdToClientInfoMap.remove(globalId);
Luke Huang05298582021-06-13 16:52:05 +0000284 maybeScheduleStop();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700285 }
286
Irfan Sheriff75006652012-04-17 23:15:29 -0700287 @Override
288 public boolean processMessage(Message msg) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900289 final ClientInfo clientInfo;
290 final int id;
291 final int clientId = msg.arg2;
292 final ListenerArgs args;
Irfan Sheriff75006652012-04-17 23:15:29 -0700293 switch (msg.what) {
Irfan Sheriff75006652012-04-17 23:15:29 -0700294 case NsdManager.DISCOVER_SERVICES:
paulhub2225702021-11-17 09:35:33 +0800295 if (DBG) Log.d(TAG, "Discover services");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900296 args = (ListenerArgs) msg.obj;
297 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000298 // If the binder death notification for a INsdManagerCallback was received
299 // before any calls are received by NsdService, the clientInfo would be
300 // cleared and cause NPE. Add a null check here to prevent this corner case.
301 if (clientInfo == null) {
302 Log.e(TAG, "Unknown connector in discovery");
303 break;
304 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700305
306 if (requestLimitReached(clientInfo)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900307 clientInfo.onDiscoverServicesFailed(
308 clientId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriff75006652012-04-17 23:15:29 -0700309 break;
310 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700311
Luke Huang05298582021-06-13 16:52:05 +0000312 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700313 id = getUniqueId();
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900314 if (discoverServices(id, args.serviceInfo)) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700315 if (DBG) {
paulhub2225702021-11-17 09:35:33 +0800316 Log.d(TAG, "Discover " + msg.arg2 + " " + id
317 + args.serviceInfo.getServiceType());
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700318 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900319 storeRequestMap(clientId, id, clientInfo, msg.what);
320 clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo);
Irfan Sheriff75006652012-04-17 23:15:29 -0700321 } else {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700322 stopServiceDiscovery(id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900323 clientInfo.onDiscoverServicesFailed(clientId,
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700324 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700325 }
326 break;
327 case NsdManager.STOP_DISCOVERY:
paulhub2225702021-11-17 09:35:33 +0800328 if (DBG) Log.d(TAG, "Stop service discovery");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900329 args = (ListenerArgs) msg.obj;
330 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000331 // If the binder death notification for a INsdManagerCallback was received
332 // before any calls are received by NsdService, the clientInfo would be
333 // cleared and cause NPE. Add a null check here to prevent this corner case.
334 if (clientInfo == null) {
335 Log.e(TAG, "Unknown connector in stop discovery");
336 break;
337 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700338
339 try {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900340 id = clientInfo.mClientIds.get(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700341 } catch (NullPointerException e) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900342 clientInfo.onStopDiscoveryFailed(
343 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700344 break;
345 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900346 removeRequestMap(clientId, id, clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700347 if (stopServiceDiscovery(id)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900348 clientInfo.onStopDiscoverySucceeded(clientId);
Irfan Sheriff75006652012-04-17 23:15:29 -0700349 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900350 clientInfo.onStopDiscoveryFailed(
351 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700352 }
353 break;
354 case NsdManager.REGISTER_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800355 if (DBG) Log.d(TAG, "Register service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900356 args = (ListenerArgs) msg.obj;
357 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000358 // If the binder death notification for a INsdManagerCallback was received
359 // before any calls are received by NsdService, the clientInfo would be
360 // cleared and cause NPE. Add a null check here to prevent this corner case.
361 if (clientInfo == null) {
362 Log.e(TAG, "Unknown connector in registration");
363 break;
364 }
365
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700366 if (requestLimitReached(clientInfo)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900367 clientInfo.onRegisterServiceFailed(
368 clientId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700369 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700370 }
371
Luke Huang05298582021-06-13 16:52:05 +0000372 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700373 id = getUniqueId();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900374 if (registerService(id, args.serviceInfo)) {
paulhub2225702021-11-17 09:35:33 +0800375 if (DBG) Log.d(TAG, "Register " + clientId + " " + id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900376 storeRequestMap(clientId, id, clientInfo, msg.what);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700377 // Return success after mDns reports success
Irfan Sheriff75006652012-04-17 23:15:29 -0700378 } else {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700379 unregisterService(id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900380 clientInfo.onRegisterServiceFailed(
381 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700382 }
383 break;
384 case NsdManager.UNREGISTER_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800385 if (DBG) Log.d(TAG, "unregister service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900386 args = (ListenerArgs) msg.obj;
387 clientInfo = mClients.get(args.connector);
Paul Hu116b4c02022-08-16 07:21:55 +0000388 // If the binder death notification for a INsdManagerCallback was received
389 // before any calls are received by NsdService, the clientInfo would be
390 // cleared and cause NPE. Add a null check here to prevent this corner case.
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900391 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);
Paul Hu116b4c02022-08-16 07:21:55 +0000408 // If the binder death notification for a INsdManagerCallback was received
409 // before any calls are received by NsdService, the clientInfo would be
410 // cleared and cause NPE. Add a null check here to prevent this corner case.
411 if (clientInfo == null) {
412 Log.e(TAG, "Unknown connector in resolution");
413 break;
414 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700415
416 if (clientInfo.mResolvedService != null) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900417 clientInfo.onResolveServiceFailed(
418 clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
Irfan Sheriff75006652012-04-17 23:15:29 -0700419 break;
420 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700421
Luke Huang05298582021-06-13 16:52:05 +0000422 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700423 id = getUniqueId();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900424 if (resolveService(id, args.serviceInfo)) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700425 clientInfo.mResolvedService = new NsdServiceInfo();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900426 storeRequestMap(clientId, id, clientInfo, msg.what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700427 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900428 clientInfo.onResolveServiceFailed(
429 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700430 }
431 break;
paulhu2b9ed952022-02-10 21:58:32 +0800432 case MDNS_SERVICE_EVENT:
433 if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
Hugo Benichif0c84092017-04-05 14:43:29 +0900434 return NOT_HANDLED;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700435 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700436 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700437 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900438 return NOT_HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700439 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900440 return HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700441 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700442
paulhu2b9ed952022-02-10 21:58:32 +0800443 private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700444 NsdServiceInfo servInfo;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700445 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
446 if (clientInfo == null) {
paulhu2b9ed952022-02-10 21:58:32 +0800447 Log.e(TAG, String.format("id %d for %d has no client mapping", id, code));
Hugo Benichif0c84092017-04-05 14:43:29 +0900448 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700449 }
450
451 /* This goes in response as msg.arg2 */
Christopher Lane74411222014-04-25 18:39:07 -0700452 int clientId = clientInfo.getClientId(id);
453 if (clientId < 0) {
Vinit Deshapnde930a8512013-06-25 19:45:03 -0700454 // This can happen because of race conditions. For example,
455 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
456 // and we may get in this situation.
paulhu2b9ed952022-02-10 21:58:32 +0800457 Log.d(TAG, String.format("%d for listener id %d that is no longer active",
458 code, id));
Hugo Benichif0c84092017-04-05 14:43:29 +0900459 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700460 }
Hugo Benichi32be63d2017-04-05 14:06:11 +0900461 if (DBG) {
paulhu2b9ed952022-02-10 21:58:32 +0800462 Log.d(TAG, String.format("MDns service event code:%d id=%d", code, id));
Hugo Benichi32be63d2017-04-05 14:06:11 +0900463 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700464 switch (code) {
paulhu2b9ed952022-02-10 21:58:32 +0800465 case IMDnsEventListener.SERVICE_FOUND: {
466 final DiscoveryInfo info = (DiscoveryInfo) obj;
467 final String name = info.serviceName;
468 final String type = info.registrationType;
469 servInfo = new NsdServiceInfo(name, type);
470 final int foundNetId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900471 if (foundNetId == 0L) {
472 // Ignore services that do not have a Network: they are not usable
473 // by apps, as they would need privileged permissions to use
474 // interfaces that do not have an associated Network.
475 break;
476 }
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900477 setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900478 clientInfo.onServiceFound(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700479 break;
paulhu2b9ed952022-02-10 21:58:32 +0800480 }
481 case IMDnsEventListener.SERVICE_LOST: {
482 final DiscoveryInfo info = (DiscoveryInfo) obj;
483 final String name = info.serviceName;
484 final String type = info.registrationType;
485 final int lostNetId = info.netId;
486 servInfo = new NsdServiceInfo(name, type);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900487 // The network could be set to null (netId 0) if it was torn down when the
488 // service is lost
489 // TODO: avoid returning null in that case, possibly by remembering
490 // found services on the same interface index and their network at the time
491 setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900492 clientInfo.onServiceLost(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700493 break;
paulhu2b9ed952022-02-10 21:58:32 +0800494 }
495 case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900496 clientInfo.onDiscoverServicesFailed(
497 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700498 break;
paulhu2b9ed952022-02-10 21:58:32 +0800499 case IMDnsEventListener.SERVICE_REGISTERED: {
500 final RegistrationInfo info = (RegistrationInfo) obj;
501 final String name = info.serviceName;
502 servInfo = new NsdServiceInfo(name, null /* serviceType */);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900503 clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700504 break;
paulhu2b9ed952022-02-10 21:58:32 +0800505 }
506 case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900507 clientInfo.onRegisterServiceFailed(
508 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700509 break;
paulhu2b9ed952022-02-10 21:58:32 +0800510 case IMDnsEventListener.SERVICE_RESOLVED: {
511 final ResolutionInfo info = (ResolutionInfo) obj;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700512 int index = 0;
paulhu2b9ed952022-02-10 21:58:32 +0800513 final String fullName = info.serviceFullName;
514 while (index < fullName.length() && fullName.charAt(index) != '.') {
515 if (fullName.charAt(index) == '\\') {
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700516 ++index;
517 }
518 ++index;
519 }
paulhu2b9ed952022-02-10 21:58:32 +0800520 if (index >= fullName.length()) {
521 Log.e(TAG, "Invalid service found " + fullName);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700522 break;
523 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900524
paulhube186602022-04-12 07:18:23 +0000525 String name = unescape(fullName.substring(0, index));
paulhu2b9ed952022-02-10 21:58:32 +0800526 String rest = fullName.substring(index);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700527 String type = rest.replace(".local.", "");
528
529 clientInfo.mResolvedService.setServiceName(name);
530 clientInfo.mResolvedService.setServiceType(type);
paulhu2b9ed952022-02-10 21:58:32 +0800531 clientInfo.mResolvedService.setPort(info.port);
532 clientInfo.mResolvedService.setTxtRecords(info.txtRecord);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900533 // Network will be added after SERVICE_GET_ADDR_SUCCESS
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700534
535 stopResolveService(id);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800536 removeRequestMap(clientId, id, clientInfo);
537
paulhu2b9ed952022-02-10 21:58:32 +0800538 final int id2 = getUniqueId();
539 if (getAddrInfo(id2, info.hostname, info.interfaceIdx)) {
Dave Plattfeff2af2014-03-07 14:48:22 -0800540 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800541 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900542 clientInfo.onResolveServiceFailed(
543 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700544 clientInfo.mResolvedService = null;
545 }
546 break;
paulhu2b9ed952022-02-10 21:58:32 +0800547 }
548 case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700549 /* NNN resolveId errorCode */
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700550 stopResolveService(id);
551 removeRequestMap(clientId, id, clientInfo);
552 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900553 clientInfo.onResolveServiceFailed(
554 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700555 break;
paulhu2b9ed952022-02-10 21:58:32 +0800556 case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700557 /* NNN resolveId errorCode */
558 stopGetAddrInfo(id);
559 removeRequestMap(clientId, id, clientInfo);
560 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900561 clientInfo.onResolveServiceFailed(
562 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700563 break;
paulhu2b9ed952022-02-10 21:58:32 +0800564 case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900565 /* NNN resolveId hostname ttl addr interfaceIdx netId */
paulhu2b9ed952022-02-10 21:58:32 +0800566 final GetAddressInfo info = (GetAddressInfo) obj;
567 final String address = info.address;
568 final int netId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900569 InetAddress serviceHost = null;
570 try {
paulhu2b9ed952022-02-10 21:58:32 +0800571 serviceHost = InetAddress.getByName(address);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900572 } catch (UnknownHostException e) {
573 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
574 }
575
576 // If the resolved service is on an interface without a network, consider it
577 // as a failure: it would not be usable by apps as they would need
578 // privileged permissions.
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900579 if (netId != NETID_UNSET && serviceHost != null) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900580 clientInfo.mResolvedService.setHost(serviceHost);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900581 setServiceNetworkForCallback(clientInfo.mResolvedService,
582 netId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900583 clientInfo.onResolveServiceSucceeded(
584 clientId, clientInfo.mResolvedService);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900585 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900586 clientInfo.onResolveServiceFailed(
587 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700588 }
589 stopGetAddrInfo(id);
590 removeRequestMap(clientId, id, clientInfo);
591 clientInfo.mResolvedService = null;
592 break;
paulhu2b9ed952022-02-10 21:58:32 +0800593 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700594 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900595 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700596 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900597 return true;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700598 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700599 }
600 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700601
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900602 private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
603 switch (netId) {
604 case NETID_UNSET:
605 info.setNetwork(null);
606 break;
607 case INetd.LOCAL_NET_ID:
608 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally
609 // visible / usable for apps, so do not return it. Store the interface
610 // index instead, so at least if the client tries to resolve the service
611 // with that NsdServiceInfo, it will be done on the same interface.
612 // If they recreate the NsdServiceInfo themselves, resolution would be
613 // done on all interfaces as before T, which should also work.
614 info.setNetwork(null);
615 info.setInterfaceIndex(ifaceIdx);
616 break;
617 default:
618 info.setNetwork(new Network(netId));
619 }
620 }
621
paulhube186602022-04-12 07:18:23 +0000622 // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable
623 // for passing to standard system DNS APIs such as res_query() . Thus, make the service name
624 // unescape for getting right service address. See "Notes on DNS Name Escaping" on
625 // external/mdnsresponder/mDNSShared/dns_sd.h for more details.
626 private String unescape(String s) {
627 StringBuilder sb = new StringBuilder(s.length());
628 for (int i = 0; i < s.length(); ++i) {
629 char c = s.charAt(i);
630 if (c == '\\') {
631 if (++i >= s.length()) {
632 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
633 break;
634 }
635 c = s.charAt(i);
636 if (c != '.' && c != '\\') {
637 if (i + 2 >= s.length()) {
638 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
639 break;
640 }
641 c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10
642 + (s.charAt(i + 2) - '0'));
643 i += 2;
644 }
645 }
646 sb.append(c);
647 }
648 return sb.toString();
649 }
650
Hugo Benichi803a2f02017-04-24 11:35:06 +0900651 @VisibleForTesting
paulhu2b9ed952022-02-10 21:58:32 +0800652 NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
Luke Huang05298582021-06-13 16:52:05 +0000653 mCleanupDelayMs = cleanupDelayMs;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900654 mContext = ctx;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900655 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700656 mNsdStateMachine.start();
paulhu2b9ed952022-02-10 21:58:32 +0800657 mMDnsManager = ctx.getSystemService(MDnsManager.class);
658 mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700659 }
660
paulhu1b35e822022-04-08 14:48:41 +0800661 public static NsdService create(Context context) {
Hugo Benichi803a2f02017-04-24 11:35:06 +0900662 HandlerThread thread = new HandlerThread(TAG);
663 thread.start();
664 Handler handler = new Handler(thread.getLooper());
paulhu2b9ed952022-02-10 21:58:32 +0800665 NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700666 return service;
667 }
668
paulhu2b9ed952022-02-10 21:58:32 +0800669 private static class MDnsEventCallback extends IMDnsEventListener.Stub {
670 private final StateMachine mStateMachine;
671
672 MDnsEventCallback(StateMachine sm) {
673 mStateMachine = sm;
674 }
675
676 @Override
677 public void onServiceRegistrationStatus(final RegistrationInfo status) {
678 mStateMachine.sendMessage(
679 MDNS_SERVICE_EVENT, status.result, status.id, status);
680 }
681
682 @Override
683 public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
684 mStateMachine.sendMessage(
685 MDNS_SERVICE_EVENT, status.result, status.id, status);
686 }
687
688 @Override
689 public void onServiceResolutionStatus(final ResolutionInfo status) {
690 mStateMachine.sendMessage(
691 MDNS_SERVICE_EVENT, status.result, status.id, status);
692 }
693
694 @Override
695 public void onGettingServiceAddressStatus(final GetAddressInfo status) {
696 mStateMachine.sendMessage(
697 MDNS_SERVICE_EVENT, status.result, status.id, status);
698 }
699
700 @Override
701 public int getInterfaceVersion() throws RemoteException {
702 return this.VERSION;
703 }
704
705 @Override
706 public String getInterfaceHash() throws RemoteException {
707 return this.HASH;
708 }
709 }
710
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900711 @Override
712 public INsdServiceConnector connect(INsdManagerCallback cb) {
Hugo Benichi803a2f02017-04-24 11:35:06 +0900713 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900714 final INsdServiceConnector connector = new NsdServiceConnector();
715 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
716 NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb)));
717 return connector;
Irfan Sheriff75006652012-04-17 23:15:29 -0700718 }
719
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900720 private static class ListenerArgs {
721 public final NsdServiceConnector connector;
722 public final NsdServiceInfo serviceInfo;
723 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
724 this.connector = connector;
725 this.serviceInfo = serviceInfo;
726 }
727 }
728
729 private class NsdServiceConnector extends INsdServiceConnector.Stub
730 implements IBinder.DeathRecipient {
731 @Override
732 public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
733 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
734 NsdManager.REGISTER_SERVICE, 0, listenerKey,
735 new ListenerArgs(this, serviceInfo)));
736 }
737
738 @Override
739 public void unregisterService(int listenerKey) {
740 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
741 NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
742 new ListenerArgs(this, null)));
743 }
744
745 @Override
746 public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
747 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
748 NsdManager.DISCOVER_SERVICES, 0, listenerKey,
749 new ListenerArgs(this, serviceInfo)));
750 }
751
752 @Override
753 public void stopDiscovery(int listenerKey) {
754 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
755 NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
756 }
757
758 @Override
759 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
760 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
761 NsdManager.RESOLVE_SERVICE, 0, listenerKey,
762 new ListenerArgs(this, serviceInfo)));
763 }
764
765 @Override
766 public void startDaemon() {
767 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
768 NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
769 }
770
771 @Override
772 public void binderDied() {
773 mNsdStateMachine.sendMessage(
774 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
775 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700776 }
777
Hugo Benichi912db992017-04-24 16:41:03 +0900778 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff52fc83a2012-04-19 10:26:34 -0700779 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff75006652012-04-17 23:15:29 -0700780 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi912db992017-04-24 16:41:03 +0900781 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
782 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn692107e2012-08-29 18:32:08 -0700783 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff75006652012-04-17 23:15:29 -0700784 }
785
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700786 private int getUniqueId() {
787 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
788 return mUniqueId;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700789 }
790
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700791 private boolean registerService(int regId, NsdServiceInfo service) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900792 if (DBG) {
paulhub2225702021-11-17 09:35:33 +0800793 Log.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700794 }
Hugo Benichi6d706442017-04-24 16:19:58 +0900795 String name = service.getServiceName();
796 String type = service.getServiceType();
797 int port = service.getPort();
798 byte[] textRecord = service.getTxtRecord();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900799 final int registerInterface = getNetworkInterfaceIndex(service);
800 if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) {
Paul Hu360a8e92022-04-26 11:14:14 +0800801 Log.e(TAG, "Interface to register service on not found");
802 return false;
803 }
804 return mMDnsManager.registerService(regId, name, type, port, textRecord, registerInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700805 }
806
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700807 private boolean unregisterService(int regId) {
paulhu2b9ed952022-02-10 21:58:32 +0800808 return mMDnsManager.stopOperation(regId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700809 }
810
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900811 private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
paulhu2b9ed952022-02-10 21:58:32 +0800812 final String type = serviceInfo.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900813 final int discoverInterface = getNetworkInterfaceIndex(serviceInfo);
814 if (serviceInfo.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900815 Log.e(TAG, "Interface to discover service on not found");
816 return false;
817 }
paulhu2b9ed952022-02-10 21:58:32 +0800818 return mMDnsManager.discover(discoveryId, type, discoverInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700819 }
820
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700821 private boolean stopServiceDiscovery(int discoveryId) {
paulhu2b9ed952022-02-10 21:58:32 +0800822 return mMDnsManager.stopOperation(discoveryId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700823 }
824
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700825 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900826 final String name = service.getServiceName();
827 final String type = service.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900828 final int resolveInterface = getNetworkInterfaceIndex(service);
829 if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900830 Log.e(TAG, "Interface to resolve service on not found");
831 return false;
832 }
paulhu2b9ed952022-02-10 21:58:32 +0800833 return mMDnsManager.resolve(resolveId, name, type, "local.", resolveInterface);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900834 }
835
836 /**
837 * Guess the interface to use to resolve or discover a service on a specific network.
838 *
839 * This is an imperfect guess, as for example the network may be gone or not yet fully
840 * registered. This is fine as failing is correct if the network is gone, and a client
841 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
842 * this is to support the legacy mdnsresponder implementation, which historically resolved
843 * services on an unspecified network.
844 */
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900845 private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) {
846 final Network network = serviceInfo.getNetwork();
847 if (network == null) {
848 // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was
849 // provided by NsdService from discovery results, and the service was found on an
850 // interface that has no app-usable Network).
851 if (serviceInfo.getInterfaceIndex() != 0) {
852 return serviceInfo.getInterfaceIndex();
853 }
854 return IFACE_IDX_ANY;
855 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900856
857 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
858 if (cm == null) {
859 Log.wtf(TAG, "No ConnectivityManager for resolveService");
860 return IFACE_IDX_ANY;
861 }
862 final LinkProperties lp = cm.getLinkProperties(network);
863 if (lp == null) return IFACE_IDX_ANY;
864
865 // Only resolve on non-stacked interfaces
866 final NetworkInterface iface;
867 try {
868 iface = NetworkInterface.getByName(lp.getInterfaceName());
869 } catch (SocketException e) {
870 Log.e(TAG, "Error querying interface", e);
871 return IFACE_IDX_ANY;
872 }
873
874 if (iface == null) {
875 Log.e(TAG, "Interface not found: " + lp.getInterfaceName());
876 return IFACE_IDX_ANY;
877 }
878
879 return iface.getIndex();
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700880 }
881
882 private boolean stopResolveService(int resolveId) {
paulhu2b9ed952022-02-10 21:58:32 +0800883 return mMDnsManager.stopOperation(resolveId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700884 }
885
paulhu2b9ed952022-02-10 21:58:32 +0800886 private boolean getAddrInfo(int resolveId, String hostname, int interfaceIdx) {
887 return mMDnsManager.getServiceAddress(resolveId, hostname, interfaceIdx);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700888 }
889
890 private boolean stopGetAddrInfo(int resolveId) {
paulhu2b9ed952022-02-10 21:58:32 +0800891 return mMDnsManager.stopOperation(resolveId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700892 }
893
894 @Override
Irfan Sheriff75006652012-04-17 23:15:29 -0700895 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
paulhu3ffffe72021-09-16 10:15:22 +0800896 if (!PermissionUtils.checkDumpPermission(mContext, TAG, pw)) return;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700897
Irfan Sheriff75006652012-04-17 23:15:29 -0700898 for (ClientInfo client : mClients.values()) {
899 pw.println("Client Info");
900 pw.println(client);
901 }
902
903 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700904 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700905
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700906 /* Information tracked per client */
907 private class ClientInfo {
908
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700909 private static final int MAX_LIMIT = 10;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900910 private final INsdManagerCallback mCb;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700911 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700912 private NsdServiceInfo mResolvedService;
913
914 /* A map from client id to unique id sent to mDns */
Hugo Benichid2552ae2017-04-11 14:42:47 +0900915 private final SparseIntArray mClientIds = new SparseIntArray();
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700916
Dave Plattfeff2af2014-03-07 14:48:22 -0800917 /* A map from client id to the type of the request we had received */
Hugo Benichid2552ae2017-04-11 14:42:47 +0900918 private final SparseIntArray mClientRequests = new SparseIntArray();
Dave Plattfeff2af2014-03-07 14:48:22 -0800919
Luke Huangf7277ed2021-07-12 21:15:10 +0800920 // The target SDK of this client < Build.VERSION_CODES.S
921 private boolean mIsLegacy = false;
922
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900923 private ClientInfo(INsdManagerCallback cb) {
924 mCb = cb;
paulhub2225702021-11-17 09:35:33 +0800925 if (DBG) Log.d(TAG, "New client");
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700926 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700927
928 @Override
929 public String toString() {
Jeff Sharkey63465382020-10-17 21:20:13 -0600930 StringBuilder sb = new StringBuilder();
Irfan Sheriff75006652012-04-17 23:15:29 -0700931 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Luke Huangf7277ed2021-07-12 21:15:10 +0800932 sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700933 for(int i = 0; i< mClientIds.size(); i++) {
Dave Plattfeff2af2014-03-07 14:48:22 -0800934 int clientID = mClientIds.keyAt(i);
935 sb.append("clientId ").append(clientID).
936 append(" mDnsId ").append(mClientIds.valueAt(i)).
937 append(" type ").append(mClientRequests.get(clientID)).append("\n");
Irfan Sheriff75006652012-04-17 23:15:29 -0700938 }
939 return sb.toString();
940 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800941
Luke Huangf7277ed2021-07-12 21:15:10 +0800942 private boolean isLegacy() {
943 return mIsLegacy;
944 }
945
946 private void setLegacy() {
947 mIsLegacy = true;
948 }
949
Dave Plattfeff2af2014-03-07 14:48:22 -0800950 // Remove any pending requests from the global map when we get rid of a client,
951 // and send cancellations to the daemon.
952 private void expungeAllRequests() {
953 int globalId, clientId, i;
Hugo Benichid2552ae2017-04-11 14:42:47 +0900954 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Dave Plattfeff2af2014-03-07 14:48:22 -0800955 for (i = 0; i < mClientIds.size(); i++) {
956 clientId = mClientIds.keyAt(i);
957 globalId = mClientIds.valueAt(i);
958 mIdToClientInfoMap.remove(globalId);
paulhub2225702021-11-17 09:35:33 +0800959 if (DBG) {
960 Log.d(TAG, "Terminating client-ID " + clientId
961 + " global-ID " + globalId + " type " + mClientRequests.get(clientId));
962 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800963 switch (mClientRequests.get(clientId)) {
964 case NsdManager.DISCOVER_SERVICES:
965 stopServiceDiscovery(globalId);
966 break;
967 case NsdManager.RESOLVE_SERVICE:
968 stopResolveService(globalId);
969 break;
970 case NsdManager.REGISTER_SERVICE:
971 unregisterService(globalId);
972 break;
973 default:
974 break;
975 }
976 }
977 mClientIds.clear();
978 mClientRequests.clear();
979 }
980
Christopher Lane74411222014-04-25 18:39:07 -0700981 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
982 // return the corresponding listener id. mDnsClient id is also called a global id.
983 private int getClientId(final int globalId) {
Hugo Benichid2552ae2017-04-11 14:42:47 +0900984 int idx = mClientIds.indexOfValue(globalId);
985 if (idx < 0) {
986 return idx;
Christopher Lane74411222014-04-25 18:39:07 -0700987 }
Hugo Benichid2552ae2017-04-11 14:42:47 +0900988 return mClientIds.keyAt(idx);
Christopher Lane74411222014-04-25 18:39:07 -0700989 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900990
991 void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
992 try {
993 mCb.onDiscoverServicesStarted(listenerKey, info);
994 } catch (RemoteException e) {
995 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
996 }
997 }
998
999 void onDiscoverServicesFailed(int listenerKey, int error) {
1000 try {
1001 mCb.onDiscoverServicesFailed(listenerKey, error);
1002 } catch (RemoteException e) {
1003 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
1004 }
1005 }
1006
1007 void onServiceFound(int listenerKey, NsdServiceInfo info) {
1008 try {
1009 mCb.onServiceFound(listenerKey, info);
1010 } catch (RemoteException e) {
1011 Log.e(TAG, "Error calling onServiceFound(", e);
1012 }
1013 }
1014
1015 void onServiceLost(int listenerKey, NsdServiceInfo info) {
1016 try {
1017 mCb.onServiceLost(listenerKey, info);
1018 } catch (RemoteException e) {
1019 Log.e(TAG, "Error calling onServiceLost(", e);
1020 }
1021 }
1022
1023 void onStopDiscoveryFailed(int listenerKey, int error) {
1024 try {
1025 mCb.onStopDiscoveryFailed(listenerKey, error);
1026 } catch (RemoteException e) {
1027 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
1028 }
1029 }
1030
1031 void onStopDiscoverySucceeded(int listenerKey) {
1032 try {
1033 mCb.onStopDiscoverySucceeded(listenerKey);
1034 } catch (RemoteException e) {
1035 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
1036 }
1037 }
1038
1039 void onRegisterServiceFailed(int listenerKey, int error) {
1040 try {
1041 mCb.onRegisterServiceFailed(listenerKey, error);
1042 } catch (RemoteException e) {
1043 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
1044 }
1045 }
1046
1047 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1048 try {
1049 mCb.onRegisterServiceSucceeded(listenerKey, info);
1050 } catch (RemoteException e) {
1051 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
1052 }
1053 }
1054
1055 void onUnregisterServiceFailed(int listenerKey, int error) {
1056 try {
1057 mCb.onUnregisterServiceFailed(listenerKey, error);
1058 } catch (RemoteException e) {
1059 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
1060 }
1061 }
1062
1063 void onUnregisterServiceSucceeded(int listenerKey) {
1064 try {
1065 mCb.onUnregisterServiceSucceeded(listenerKey);
1066 } catch (RemoteException e) {
1067 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
1068 }
1069 }
1070
1071 void onResolveServiceFailed(int listenerKey, int error) {
1072 try {
1073 mCb.onResolveServiceFailed(listenerKey, error);
1074 } catch (RemoteException e) {
1075 Log.e(TAG, "Error calling onResolveServiceFailed", e);
1076 }
1077 }
1078
1079 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1080 try {
1081 mCb.onResolveServiceSucceeded(listenerKey, info);
1082 } catch (RemoteException e) {
1083 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
1084 }
1085 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001086 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001087}