blob: 7115720fd6ec108b72571285eda85f1d62e57e4c [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();
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);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700298
299 if (requestLimitReached(clientInfo)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900300 clientInfo.onDiscoverServicesFailed(
301 clientId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriff75006652012-04-17 23:15:29 -0700302 break;
303 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700304
Luke Huang05298582021-06-13 16:52:05 +0000305 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700306 id = getUniqueId();
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900307 if (discoverServices(id, args.serviceInfo)) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700308 if (DBG) {
paulhub2225702021-11-17 09:35:33 +0800309 Log.d(TAG, "Discover " + msg.arg2 + " " + id
310 + args.serviceInfo.getServiceType());
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700311 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900312 storeRequestMap(clientId, id, clientInfo, msg.what);
313 clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo);
Irfan Sheriff75006652012-04-17 23:15:29 -0700314 } else {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700315 stopServiceDiscovery(id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900316 clientInfo.onDiscoverServicesFailed(clientId,
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700317 NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700318 }
319 break;
320 case NsdManager.STOP_DISCOVERY:
paulhub2225702021-11-17 09:35:33 +0800321 if (DBG) Log.d(TAG, "Stop service discovery");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900322 args = (ListenerArgs) msg.obj;
323 clientInfo = mClients.get(args.connector);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700324
325 try {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900326 id = clientInfo.mClientIds.get(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700327 } catch (NullPointerException e) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900328 clientInfo.onStopDiscoveryFailed(
329 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700330 break;
331 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900332 removeRequestMap(clientId, id, clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700333 if (stopServiceDiscovery(id)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900334 clientInfo.onStopDiscoverySucceeded(clientId);
Irfan Sheriff75006652012-04-17 23:15:29 -0700335 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900336 clientInfo.onStopDiscoveryFailed(
337 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700338 }
339 break;
340 case NsdManager.REGISTER_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800341 if (DBG) Log.d(TAG, "Register service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900342 args = (ListenerArgs) msg.obj;
343 clientInfo = mClients.get(args.connector);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700344 if (requestLimitReached(clientInfo)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900345 clientInfo.onRegisterServiceFailed(
346 clientId, NsdManager.FAILURE_MAX_LIMIT);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700347 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700348 }
349
Luke Huang05298582021-06-13 16:52:05 +0000350 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700351 id = getUniqueId();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900352 if (registerService(id, args.serviceInfo)) {
paulhub2225702021-11-17 09:35:33 +0800353 if (DBG) Log.d(TAG, "Register " + clientId + " " + id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900354 storeRequestMap(clientId, id, clientInfo, msg.what);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700355 // Return success after mDns reports success
Irfan Sheriff75006652012-04-17 23:15:29 -0700356 } else {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700357 unregisterService(id);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900358 clientInfo.onRegisterServiceFailed(
359 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700360 }
361 break;
362 case NsdManager.UNREGISTER_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800363 if (DBG) Log.d(TAG, "unregister service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900364 args = (ListenerArgs) msg.obj;
365 clientInfo = mClients.get(args.connector);
366 if (clientInfo == null) {
paulhub2225702021-11-17 09:35:33 +0800367 Log.e(TAG, "Unknown connector in unregistration");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700368 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700369 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900370 id = clientInfo.mClientIds.get(clientId);
371 removeRequestMap(clientId, id, clientInfo);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700372 if (unregisterService(id)) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900373 clientInfo.onUnregisterServiceSucceeded(clientId);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700374 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900375 clientInfo.onUnregisterServiceFailed(
376 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700377 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700378 break;
379 case NsdManager.RESOLVE_SERVICE:
paulhub2225702021-11-17 09:35:33 +0800380 if (DBG) Log.d(TAG, "Resolve service");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900381 args = (ListenerArgs) msg.obj;
382 clientInfo = mClients.get(args.connector);
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700383
384 if (clientInfo.mResolvedService != null) {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900385 clientInfo.onResolveServiceFailed(
386 clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
Irfan Sheriff75006652012-04-17 23:15:29 -0700387 break;
388 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700389
Luke Huang05298582021-06-13 16:52:05 +0000390 maybeStartDaemon();
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700391 id = getUniqueId();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900392 if (resolveService(id, args.serviceInfo)) {
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700393 clientInfo.mResolvedService = new NsdServiceInfo();
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900394 storeRequestMap(clientId, id, clientInfo, msg.what);
Irfan Sheriff75006652012-04-17 23:15:29 -0700395 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900396 clientInfo.onResolveServiceFailed(
397 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Irfan Sheriff75006652012-04-17 23:15:29 -0700398 }
399 break;
paulhu2b9ed952022-02-10 21:58:32 +0800400 case MDNS_SERVICE_EVENT:
401 if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
Hugo Benichif0c84092017-04-05 14:43:29 +0900402 return NOT_HANDLED;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700403 }
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700404 break;
Irfan Sheriff75006652012-04-17 23:15:29 -0700405 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900406 return NOT_HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700407 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900408 return HANDLED;
Irfan Sheriff75006652012-04-17 23:15:29 -0700409 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700410
paulhu2b9ed952022-02-10 21:58:32 +0800411 private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700412 NsdServiceInfo servInfo;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700413 ClientInfo clientInfo = mIdToClientInfoMap.get(id);
414 if (clientInfo == null) {
paulhu2b9ed952022-02-10 21:58:32 +0800415 Log.e(TAG, String.format("id %d for %d has no client mapping", id, code));
Hugo Benichif0c84092017-04-05 14:43:29 +0900416 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700417 }
418
419 /* This goes in response as msg.arg2 */
Christopher Lane74411222014-04-25 18:39:07 -0700420 int clientId = clientInfo.getClientId(id);
421 if (clientId < 0) {
Vinit Deshapnde930a8512013-06-25 19:45:03 -0700422 // This can happen because of race conditions. For example,
423 // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
424 // and we may get in this situation.
paulhu2b9ed952022-02-10 21:58:32 +0800425 Log.d(TAG, String.format("%d for listener id %d that is no longer active",
426 code, id));
Hugo Benichif0c84092017-04-05 14:43:29 +0900427 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700428 }
Hugo Benichi32be63d2017-04-05 14:06:11 +0900429 if (DBG) {
paulhu2b9ed952022-02-10 21:58:32 +0800430 Log.d(TAG, String.format("MDns service event code:%d id=%d", code, id));
Hugo Benichi32be63d2017-04-05 14:06:11 +0900431 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700432 switch (code) {
paulhu2b9ed952022-02-10 21:58:32 +0800433 case IMDnsEventListener.SERVICE_FOUND: {
434 final DiscoveryInfo info = (DiscoveryInfo) obj;
435 final String name = info.serviceName;
436 final String type = info.registrationType;
437 servInfo = new NsdServiceInfo(name, type);
438 final int foundNetId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900439 if (foundNetId == 0L) {
440 // Ignore services that do not have a Network: they are not usable
441 // by apps, as they would need privileged permissions to use
442 // interfaces that do not have an associated Network.
443 break;
444 }
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900445 setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900446 clientInfo.onServiceFound(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700447 break;
paulhu2b9ed952022-02-10 21:58:32 +0800448 }
449 case IMDnsEventListener.SERVICE_LOST: {
450 final DiscoveryInfo info = (DiscoveryInfo) obj;
451 final String name = info.serviceName;
452 final String type = info.registrationType;
453 final int lostNetId = info.netId;
454 servInfo = new NsdServiceInfo(name, type);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900455 // The network could be set to null (netId 0) if it was torn down when the
456 // service is lost
457 // TODO: avoid returning null in that case, possibly by remembering
458 // found services on the same interface index and their network at the time
459 setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900460 clientInfo.onServiceLost(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700461 break;
paulhu2b9ed952022-02-10 21:58:32 +0800462 }
463 case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900464 clientInfo.onDiscoverServicesFailed(
465 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700466 break;
paulhu2b9ed952022-02-10 21:58:32 +0800467 case IMDnsEventListener.SERVICE_REGISTERED: {
468 final RegistrationInfo info = (RegistrationInfo) obj;
469 final String name = info.serviceName;
470 servInfo = new NsdServiceInfo(name, null /* serviceType */);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900471 clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700472 break;
paulhu2b9ed952022-02-10 21:58:32 +0800473 }
474 case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900475 clientInfo.onRegisterServiceFailed(
476 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700477 break;
paulhu2b9ed952022-02-10 21:58:32 +0800478 case IMDnsEventListener.SERVICE_RESOLVED: {
479 final ResolutionInfo info = (ResolutionInfo) obj;
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700480 int index = 0;
paulhu2b9ed952022-02-10 21:58:32 +0800481 final String fullName = info.serviceFullName;
482 while (index < fullName.length() && fullName.charAt(index) != '.') {
483 if (fullName.charAt(index) == '\\') {
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700484 ++index;
485 }
486 ++index;
487 }
paulhu2b9ed952022-02-10 21:58:32 +0800488 if (index >= fullName.length()) {
489 Log.e(TAG, "Invalid service found " + fullName);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700490 break;
491 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900492
paulhube186602022-04-12 07:18:23 +0000493 String name = unescape(fullName.substring(0, index));
paulhu2b9ed952022-02-10 21:58:32 +0800494 String rest = fullName.substring(index);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700495 String type = rest.replace(".local.", "");
496
497 clientInfo.mResolvedService.setServiceName(name);
498 clientInfo.mResolvedService.setServiceType(type);
paulhu2b9ed952022-02-10 21:58:32 +0800499 clientInfo.mResolvedService.setPort(info.port);
500 clientInfo.mResolvedService.setTxtRecords(info.txtRecord);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900501 // Network will be added after SERVICE_GET_ADDR_SUCCESS
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700502
503 stopResolveService(id);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800504 removeRequestMap(clientId, id, clientInfo);
505
paulhu2b9ed952022-02-10 21:58:32 +0800506 final int id2 = getUniqueId();
507 if (getAddrInfo(id2, info.hostname, info.interfaceIdx)) {
Dave Plattfeff2af2014-03-07 14:48:22 -0800508 storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
Vinit Deshapnde4429e872013-11-12 15:36:37 -0800509 } else {
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900510 clientInfo.onResolveServiceFailed(
511 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700512 clientInfo.mResolvedService = null;
513 }
514 break;
paulhu2b9ed952022-02-10 21:58:32 +0800515 }
516 case IMDnsEventListener.SERVICE_RESOLUTION_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700517 /* NNN resolveId errorCode */
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700518 stopResolveService(id);
519 removeRequestMap(clientId, id, clientInfo);
520 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900521 clientInfo.onResolveServiceFailed(
522 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700523 break;
paulhu2b9ed952022-02-10 21:58:32 +0800524 case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700525 /* NNN resolveId errorCode */
526 stopGetAddrInfo(id);
527 removeRequestMap(clientId, id, clientInfo);
528 clientInfo.mResolvedService = null;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900529 clientInfo.onResolveServiceFailed(
530 clientId, NsdManager.FAILURE_INTERNAL_ERROR);
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700531 break;
paulhu2b9ed952022-02-10 21:58:32 +0800532 case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900533 /* NNN resolveId hostname ttl addr interfaceIdx netId */
paulhu2b9ed952022-02-10 21:58:32 +0800534 final GetAddressInfo info = (GetAddressInfo) obj;
535 final String address = info.address;
536 final int netId = info.netId;
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900537 InetAddress serviceHost = null;
538 try {
paulhu2b9ed952022-02-10 21:58:32 +0800539 serviceHost = InetAddress.getByName(address);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900540 } catch (UnknownHostException e) {
541 Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
542 }
543
544 // If the resolved service is on an interface without a network, consider it
545 // as a failure: it would not be usable by apps as they would need
546 // privileged permissions.
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900547 if (netId != NETID_UNSET && serviceHost != null) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900548 clientInfo.mResolvedService.setHost(serviceHost);
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900549 setServiceNetworkForCallback(clientInfo.mResolvedService,
550 netId, info.interfaceIdx);
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900551 clientInfo.onResolveServiceSucceeded(
552 clientId, clientInfo.mResolvedService);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900553 } else {
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 }
557 stopGetAddrInfo(id);
558 removeRequestMap(clientId, id, clientInfo);
559 clientInfo.mResolvedService = null;
560 break;
paulhu2b9ed952022-02-10 21:58:32 +0800561 }
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700562 default:
Hugo Benichif0c84092017-04-05 14:43:29 +0900563 return false;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700564 }
Hugo Benichif0c84092017-04-05 14:43:29 +0900565 return true;
Vairavan Srinivasan6ce48182012-08-05 13:14:12 -0700566 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700567 }
568 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700569
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900570 private static void setServiceNetworkForCallback(NsdServiceInfo info, int netId, int ifaceIdx) {
571 switch (netId) {
572 case NETID_UNSET:
573 info.setNetwork(null);
574 break;
575 case INetd.LOCAL_NET_ID:
576 // Special case for LOCAL_NET_ID: Networks on netId 99 are not generally
577 // visible / usable for apps, so do not return it. Store the interface
578 // index instead, so at least if the client tries to resolve the service
579 // with that NsdServiceInfo, it will be done on the same interface.
580 // If they recreate the NsdServiceInfo themselves, resolution would be
581 // done on all interfaces as before T, which should also work.
582 info.setNetwork(null);
583 info.setInterfaceIndex(ifaceIdx);
584 break;
585 default:
586 info.setNetwork(new Network(netId));
587 }
588 }
589
paulhube186602022-04-12 07:18:23 +0000590 // The full service name is escaped from standard DNS rules on mdnsresponder, making it suitable
591 // for passing to standard system DNS APIs such as res_query() . Thus, make the service name
592 // unescape for getting right service address. See "Notes on DNS Name Escaping" on
593 // external/mdnsresponder/mDNSShared/dns_sd.h for more details.
594 private String unescape(String s) {
595 StringBuilder sb = new StringBuilder(s.length());
596 for (int i = 0; i < s.length(); ++i) {
597 char c = s.charAt(i);
598 if (c == '\\') {
599 if (++i >= s.length()) {
600 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
601 break;
602 }
603 c = s.charAt(i);
604 if (c != '.' && c != '\\') {
605 if (i + 2 >= s.length()) {
606 Log.e(TAG, "Unexpected end of escape sequence in: " + s);
607 break;
608 }
609 c = (char) ((c - '0') * 100 + (s.charAt(i + 1) - '0') * 10
610 + (s.charAt(i + 2) - '0'));
611 i += 2;
612 }
613 }
614 sb.append(c);
615 }
616 return sb.toString();
617 }
618
Hugo Benichi803a2f02017-04-24 11:35:06 +0900619 @VisibleForTesting
paulhu2b9ed952022-02-10 21:58:32 +0800620 NsdService(Context ctx, Handler handler, long cleanupDelayMs) {
Luke Huang05298582021-06-13 16:52:05 +0000621 mCleanupDelayMs = cleanupDelayMs;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900622 mContext = ctx;
Hugo Benichi803a2f02017-04-24 11:35:06 +0900623 mNsdStateMachine = new NsdStateMachine(TAG, handler);
Irfan Sheriff75006652012-04-17 23:15:29 -0700624 mNsdStateMachine.start();
paulhu2b9ed952022-02-10 21:58:32 +0800625 mMDnsManager = ctx.getSystemService(MDnsManager.class);
626 mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700627 }
628
paulhu1b35e822022-04-08 14:48:41 +0800629 public static NsdService create(Context context) {
Hugo Benichi803a2f02017-04-24 11:35:06 +0900630 HandlerThread thread = new HandlerThread(TAG);
631 thread.start();
632 Handler handler = new Handler(thread.getLooper());
paulhu2b9ed952022-02-10 21:58:32 +0800633 NsdService service = new NsdService(context, handler, CLEANUP_DELAY_MS);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700634 return service;
635 }
636
paulhu2b9ed952022-02-10 21:58:32 +0800637 private static class MDnsEventCallback extends IMDnsEventListener.Stub {
638 private final StateMachine mStateMachine;
639
640 MDnsEventCallback(StateMachine sm) {
641 mStateMachine = sm;
642 }
643
644 @Override
645 public void onServiceRegistrationStatus(final RegistrationInfo status) {
646 mStateMachine.sendMessage(
647 MDNS_SERVICE_EVENT, status.result, status.id, status);
648 }
649
650 @Override
651 public void onServiceDiscoveryStatus(final DiscoveryInfo status) {
652 mStateMachine.sendMessage(
653 MDNS_SERVICE_EVENT, status.result, status.id, status);
654 }
655
656 @Override
657 public void onServiceResolutionStatus(final ResolutionInfo status) {
658 mStateMachine.sendMessage(
659 MDNS_SERVICE_EVENT, status.result, status.id, status);
660 }
661
662 @Override
663 public void onGettingServiceAddressStatus(final GetAddressInfo status) {
664 mStateMachine.sendMessage(
665 MDNS_SERVICE_EVENT, status.result, status.id, status);
666 }
667
668 @Override
669 public int getInterfaceVersion() throws RemoteException {
670 return this.VERSION;
671 }
672
673 @Override
674 public String getInterfaceHash() throws RemoteException {
675 return this.HASH;
676 }
677 }
678
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900679 @Override
680 public INsdServiceConnector connect(INsdManagerCallback cb) {
Hugo Benichi803a2f02017-04-24 11:35:06 +0900681 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900682 final INsdServiceConnector connector = new NsdServiceConnector();
683 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
684 NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb)));
685 return connector;
Irfan Sheriff75006652012-04-17 23:15:29 -0700686 }
687
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900688 private static class ListenerArgs {
689 public final NsdServiceConnector connector;
690 public final NsdServiceInfo serviceInfo;
691 ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
692 this.connector = connector;
693 this.serviceInfo = serviceInfo;
694 }
695 }
696
697 private class NsdServiceConnector extends INsdServiceConnector.Stub
698 implements IBinder.DeathRecipient {
699 @Override
700 public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
701 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
702 NsdManager.REGISTER_SERVICE, 0, listenerKey,
703 new ListenerArgs(this, serviceInfo)));
704 }
705
706 @Override
707 public void unregisterService(int listenerKey) {
708 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
709 NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
710 new ListenerArgs(this, null)));
711 }
712
713 @Override
714 public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
715 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
716 NsdManager.DISCOVER_SERVICES, 0, listenerKey,
717 new ListenerArgs(this, serviceInfo)));
718 }
719
720 @Override
721 public void stopDiscovery(int listenerKey) {
722 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
723 NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
724 }
725
726 @Override
727 public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
728 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
729 NsdManager.RESOLVE_SERVICE, 0, listenerKey,
730 new ListenerArgs(this, serviceInfo)));
731 }
732
733 @Override
734 public void startDaemon() {
735 mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
736 NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
737 }
738
739 @Override
740 public void binderDied() {
741 mNsdStateMachine.sendMessage(
742 mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
743 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700744 }
745
Hugo Benichi912db992017-04-24 16:41:03 +0900746 private void sendNsdStateChangeBroadcast(boolean isEnabled) {
Irfan Sheriff52fc83a2012-04-19 10:26:34 -0700747 final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED);
Irfan Sheriff75006652012-04-17 23:15:29 -0700748 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Hugo Benichi912db992017-04-24 16:41:03 +0900749 int nsdState = isEnabled ? NsdManager.NSD_STATE_ENABLED : NsdManager.NSD_STATE_DISABLED;
750 intent.putExtra(NsdManager.EXTRA_NSD_STATE, nsdState);
Dianne Hackborn692107e2012-08-29 18:32:08 -0700751 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
Irfan Sheriff75006652012-04-17 23:15:29 -0700752 }
753
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700754 private int getUniqueId() {
755 if (++mUniqueId == INVALID_ID) return ++mUniqueId;
756 return mUniqueId;
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700757 }
758
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700759 private boolean registerService(int regId, NsdServiceInfo service) {
Hugo Benichi6d706442017-04-24 16:19:58 +0900760 if (DBG) {
paulhub2225702021-11-17 09:35:33 +0800761 Log.d(TAG, "registerService: " + regId + " " + service);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700762 }
Hugo Benichi6d706442017-04-24 16:19:58 +0900763 String name = service.getServiceName();
764 String type = service.getServiceType();
765 int port = service.getPort();
766 byte[] textRecord = service.getTxtRecord();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900767 final int registerInterface = getNetworkInterfaceIndex(service);
768 if (service.getNetwork() != null && registerInterface == IFACE_IDX_ANY) {
Paul Hu360a8e92022-04-26 11:14:14 +0800769 Log.e(TAG, "Interface to register service on not found");
770 return false;
771 }
772 return mMDnsManager.registerService(regId, name, type, port, textRecord, registerInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700773 }
774
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700775 private boolean unregisterService(int regId) {
paulhu2b9ed952022-02-10 21:58:32 +0800776 return mMDnsManager.stopOperation(regId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700777 }
778
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900779 private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
paulhu2b9ed952022-02-10 21:58:32 +0800780 final String type = serviceInfo.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900781 final int discoverInterface = getNetworkInterfaceIndex(serviceInfo);
782 if (serviceInfo.getNetwork() != null && discoverInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900783 Log.e(TAG, "Interface to discover service on not found");
784 return false;
785 }
paulhu2b9ed952022-02-10 21:58:32 +0800786 return mMDnsManager.discover(discoveryId, type, discoverInterface);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700787 }
788
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700789 private boolean stopServiceDiscovery(int discoveryId) {
paulhu2b9ed952022-02-10 21:58:32 +0800790 return mMDnsManager.stopOperation(discoveryId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700791 }
792
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700793 private boolean resolveService(int resolveId, NsdServiceInfo service) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900794 final String name = service.getServiceName();
795 final String type = service.getServiceType();
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900796 final int resolveInterface = getNetworkInterfaceIndex(service);
797 if (service.getNetwork() != null && resolveInterface == IFACE_IDX_ANY) {
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900798 Log.e(TAG, "Interface to resolve service on not found");
799 return false;
800 }
paulhu2b9ed952022-02-10 21:58:32 +0800801 return mMDnsManager.resolve(resolveId, name, type, "local.", resolveInterface);
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900802 }
803
804 /**
805 * Guess the interface to use to resolve or discover a service on a specific network.
806 *
807 * This is an imperfect guess, as for example the network may be gone or not yet fully
808 * registered. This is fine as failing is correct if the network is gone, and a client
809 * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
810 * this is to support the legacy mdnsresponder implementation, which historically resolved
811 * services on an unspecified network.
812 */
Remi NGUYEN VAN1a8ee102022-05-30 12:42:24 +0900813 private int getNetworkInterfaceIndex(NsdServiceInfo serviceInfo) {
814 final Network network = serviceInfo.getNetwork();
815 if (network == null) {
816 // Fallback to getInterfaceIndex if present (typically if the NsdServiceInfo was
817 // provided by NsdService from discovery results, and the service was found on an
818 // interface that has no app-usable Network).
819 if (serviceInfo.getInterfaceIndex() != 0) {
820 return serviceInfo.getInterfaceIndex();
821 }
822 return IFACE_IDX_ANY;
823 }
Remi NGUYEN VAN23651302021-12-16 15:31:16 +0900824
825 final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
826 if (cm == null) {
827 Log.wtf(TAG, "No ConnectivityManager for resolveService");
828 return IFACE_IDX_ANY;
829 }
830 final LinkProperties lp = cm.getLinkProperties(network);
831 if (lp == null) return IFACE_IDX_ANY;
832
833 // Only resolve on non-stacked interfaces
834 final NetworkInterface iface;
835 try {
836 iface = NetworkInterface.getByName(lp.getInterfaceName());
837 } catch (SocketException e) {
838 Log.e(TAG, "Error querying interface", e);
839 return IFACE_IDX_ANY;
840 }
841
842 if (iface == null) {
843 Log.e(TAG, "Interface not found: " + lp.getInterfaceName());
844 return IFACE_IDX_ANY;
845 }
846
847 return iface.getIndex();
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700848 }
849
850 private boolean stopResolveService(int resolveId) {
paulhu2b9ed952022-02-10 21:58:32 +0800851 return mMDnsManager.stopOperation(resolveId);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700852 }
853
paulhu2b9ed952022-02-10 21:58:32 +0800854 private boolean getAddrInfo(int resolveId, String hostname, int interfaceIdx) {
855 return mMDnsManager.getServiceAddress(resolveId, hostname, interfaceIdx);
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700856 }
857
858 private boolean stopGetAddrInfo(int resolveId) {
paulhu2b9ed952022-02-10 21:58:32 +0800859 return mMDnsManager.stopOperation(resolveId);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700860 }
861
862 @Override
Irfan Sheriff75006652012-04-17 23:15:29 -0700863 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
paulhub2225702021-11-17 09:35:33 +0800864 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
865 != PackageManager.PERMISSION_GRANTED) {
866 pw.println("Permission Denial: can't dump " + TAG
867 + " due to missing android.permission.DUMP permission");
868 return;
869 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700870
Irfan Sheriff75006652012-04-17 23:15:29 -0700871 for (ClientInfo client : mClients.values()) {
872 pw.println("Client Info");
873 pw.println(client);
874 }
875
876 mNsdStateMachine.dump(fd, pw, args);
Irfan Sheriff77ec5582012-03-22 17:01:39 -0700877 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700878
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700879 /* Information tracked per client */
880 private class ClientInfo {
881
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700882 private static final int MAX_LIMIT = 10;
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900883 private final INsdManagerCallback mCb;
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700884 /* Remembers a resolved service until getaddrinfo completes */
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700885 private NsdServiceInfo mResolvedService;
886
887 /* A map from client id to unique id sent to mDns */
Hugo Benichid2552ae2017-04-11 14:42:47 +0900888 private final SparseIntArray mClientIds = new SparseIntArray();
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700889
Dave Plattfeff2af2014-03-07 14:48:22 -0800890 /* A map from client id to the type of the request we had received */
Hugo Benichid2552ae2017-04-11 14:42:47 +0900891 private final SparseIntArray mClientRequests = new SparseIntArray();
Dave Plattfeff2af2014-03-07 14:48:22 -0800892
Luke Huangf7277ed2021-07-12 21:15:10 +0800893 // The target SDK of this client < Build.VERSION_CODES.S
894 private boolean mIsLegacy = false;
895
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900896 private ClientInfo(INsdManagerCallback cb) {
897 mCb = cb;
paulhub2225702021-11-17 09:35:33 +0800898 if (DBG) Log.d(TAG, "New client");
Irfan Sheriffe8de2462012-04-11 14:52:19 -0700899 }
Irfan Sheriff75006652012-04-17 23:15:29 -0700900
901 @Override
902 public String toString() {
Jeff Sharkey63465382020-10-17 21:20:13 -0600903 StringBuilder sb = new StringBuilder();
Irfan Sheriff75006652012-04-17 23:15:29 -0700904 sb.append("mResolvedService ").append(mResolvedService).append("\n");
Luke Huangf7277ed2021-07-12 21:15:10 +0800905 sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
Irfan Sheriffe4c42f42012-05-03 16:44:27 -0700906 for(int i = 0; i< mClientIds.size(); i++) {
Dave Plattfeff2af2014-03-07 14:48:22 -0800907 int clientID = mClientIds.keyAt(i);
908 sb.append("clientId ").append(clientID).
909 append(" mDnsId ").append(mClientIds.valueAt(i)).
910 append(" type ").append(mClientRequests.get(clientID)).append("\n");
Irfan Sheriff75006652012-04-17 23:15:29 -0700911 }
912 return sb.toString();
913 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800914
Luke Huangf7277ed2021-07-12 21:15:10 +0800915 private boolean isLegacy() {
916 return mIsLegacy;
917 }
918
919 private void setLegacy() {
920 mIsLegacy = true;
921 }
922
Dave Plattfeff2af2014-03-07 14:48:22 -0800923 // Remove any pending requests from the global map when we get rid of a client,
924 // and send cancellations to the daemon.
925 private void expungeAllRequests() {
926 int globalId, clientId, i;
Hugo Benichid2552ae2017-04-11 14:42:47 +0900927 // TODO: to keep handler responsive, do not clean all requests for that client at once.
Dave Plattfeff2af2014-03-07 14:48:22 -0800928 for (i = 0; i < mClientIds.size(); i++) {
929 clientId = mClientIds.keyAt(i);
930 globalId = mClientIds.valueAt(i);
931 mIdToClientInfoMap.remove(globalId);
paulhub2225702021-11-17 09:35:33 +0800932 if (DBG) {
933 Log.d(TAG, "Terminating client-ID " + clientId
934 + " global-ID " + globalId + " type " + mClientRequests.get(clientId));
935 }
Dave Plattfeff2af2014-03-07 14:48:22 -0800936 switch (mClientRequests.get(clientId)) {
937 case NsdManager.DISCOVER_SERVICES:
938 stopServiceDiscovery(globalId);
939 break;
940 case NsdManager.RESOLVE_SERVICE:
941 stopResolveService(globalId);
942 break;
943 case NsdManager.REGISTER_SERVICE:
944 unregisterService(globalId);
945 break;
946 default:
947 break;
948 }
949 }
950 mClientIds.clear();
951 mClientRequests.clear();
952 }
953
Christopher Lane74411222014-04-25 18:39:07 -0700954 // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
955 // return the corresponding listener id. mDnsClient id is also called a global id.
956 private int getClientId(final int globalId) {
Hugo Benichid2552ae2017-04-11 14:42:47 +0900957 int idx = mClientIds.indexOfValue(globalId);
958 if (idx < 0) {
959 return idx;
Christopher Lane74411222014-04-25 18:39:07 -0700960 }
Hugo Benichid2552ae2017-04-11 14:42:47 +0900961 return mClientIds.keyAt(idx);
Christopher Lane74411222014-04-25 18:39:07 -0700962 }
Remi NGUYEN VAN62eb76e2021-09-09 17:39:05 +0900963
964 void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
965 try {
966 mCb.onDiscoverServicesStarted(listenerKey, info);
967 } catch (RemoteException e) {
968 Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
969 }
970 }
971
972 void onDiscoverServicesFailed(int listenerKey, int error) {
973 try {
974 mCb.onDiscoverServicesFailed(listenerKey, error);
975 } catch (RemoteException e) {
976 Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
977 }
978 }
979
980 void onServiceFound(int listenerKey, NsdServiceInfo info) {
981 try {
982 mCb.onServiceFound(listenerKey, info);
983 } catch (RemoteException e) {
984 Log.e(TAG, "Error calling onServiceFound(", e);
985 }
986 }
987
988 void onServiceLost(int listenerKey, NsdServiceInfo info) {
989 try {
990 mCb.onServiceLost(listenerKey, info);
991 } catch (RemoteException e) {
992 Log.e(TAG, "Error calling onServiceLost(", e);
993 }
994 }
995
996 void onStopDiscoveryFailed(int listenerKey, int error) {
997 try {
998 mCb.onStopDiscoveryFailed(listenerKey, error);
999 } catch (RemoteException e) {
1000 Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
1001 }
1002 }
1003
1004 void onStopDiscoverySucceeded(int listenerKey) {
1005 try {
1006 mCb.onStopDiscoverySucceeded(listenerKey);
1007 } catch (RemoteException e) {
1008 Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
1009 }
1010 }
1011
1012 void onRegisterServiceFailed(int listenerKey, int error) {
1013 try {
1014 mCb.onRegisterServiceFailed(listenerKey, error);
1015 } catch (RemoteException e) {
1016 Log.e(TAG, "Error calling onRegisterServiceFailed", e);
1017 }
1018 }
1019
1020 void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1021 try {
1022 mCb.onRegisterServiceSucceeded(listenerKey, info);
1023 } catch (RemoteException e) {
1024 Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
1025 }
1026 }
1027
1028 void onUnregisterServiceFailed(int listenerKey, int error) {
1029 try {
1030 mCb.onUnregisterServiceFailed(listenerKey, error);
1031 } catch (RemoteException e) {
1032 Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
1033 }
1034 }
1035
1036 void onUnregisterServiceSucceeded(int listenerKey) {
1037 try {
1038 mCb.onUnregisterServiceSucceeded(listenerKey);
1039 } catch (RemoteException e) {
1040 Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
1041 }
1042 }
1043
1044 void onResolveServiceFailed(int listenerKey, int error) {
1045 try {
1046 mCb.onResolveServiceFailed(listenerKey, error);
1047 } catch (RemoteException e) {
1048 Log.e(TAG, "Error calling onResolveServiceFailed", e);
1049 }
1050 }
1051
1052 void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
1053 try {
1054 mCb.onResolveServiceSucceeded(listenerKey, info);
1055 } catch (RemoteException e) {
1056 Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
1057 }
1058 }
Irfan Sheriffe8de2462012-04-11 14:52:19 -07001059 }
Irfan Sheriff77ec5582012-03-22 17:01:39 -07001060}